CESI: Architecture N tiers, exercice
This commit is contained in:
parent
e041aa9b9f
commit
c8caed6efc
|
@ -130,19 +130,9 @@ La conception d'une application distribuée nécessite d'établir une répartiti
|
|||
|
||||
---
|
||||
|
||||
## Exercice (1)
|
||||
|
||||
Implémenter en NodeJS une application distribuée permettant d'effectuer les opérations arithmétiques simples (addition, soustraction, multiplication et division). Cette application devra répondre aux contraintes suivantes:
|
||||
|
||||
- Elle devra être suivre une architecture 2 tiers de classe 4.
|
||||
- Le client devra utiliser le transport TCP/IP pour communiquer avec le serveur.
|
||||
- Elle n'aura pas à supporter de multiples clients.
|
||||
- Toutes les implémentations de la classe devront être compatibles, i.e. vous devez vous mettre d'accord sur un protocole de sérialisation/désérialisation des messages commun.
|
||||
|
||||
---
|
||||
|
||||
## Exercice (2)
|
||||
## Exercice
|
||||
|
||||
### Implémentation d'une calculatrice à état distribuée en NodeJS
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Exercice: implémentation d'une calculatrice à état distribuée
|
||||
|
||||
## Consignes
|
||||
|
||||
Implémenter en NodeJS une application distribuée permettant d'effectuer les opérations arithmétiques simples. Cette application devra répondre aux contraintes suivantes:
|
||||
|
||||
- Elle devra être suivre une architecture 2 tiers de classe 4.
|
||||
- Le client devra utiliser le transport TCP/IP pour communiquer avec le serveur.
|
||||
- Elle n'aura pas à supporter de multiples clients.
|
||||
- Toutes les implémentations de la classe devront être compatibles, i.e. vous devez vous mettre d'accord sur un protocole de sérialisation/désérialisation des messages commun.
|
||||
|
||||
Le client/serveur devront gérer les instructions suivantes:
|
||||
|
||||
- `add` Requête d'addition
|
||||
- `sub` Requête de soustraction
|
||||
- `div` Requête de division
|
||||
- `mul` Requête de multiplication
|
||||
- `status` Requête de récupération de la valeur de l'accumulateur sur le serveur
|
||||
- `reset` Requête de réinitialisation de la valeur de l'accumulateur sur le serveur.
|
||||
|
||||
Vous pouvez vous baser sur les fichiers `client.js` et `server.js` présent dans ce répertoire pour amorcer votre projet.
|
||||
|
||||
## Exemple de séquence d'échange
|
||||
|
||||
![center](./seq.png)
|
||||
|
||||
## Ressources
|
||||
|
||||
- [Télécharger/installer NodeJS](https://nodejs.org/en/download/)
|
||||
- [Le module `net` de NodeJS](https://nodejs.org/api/net.html)
|
|
@ -0,0 +1,78 @@
|
|||
var net = require('net');
|
||||
|
||||
// Notre client se connecte sur le serveur local
|
||||
var SERVER_HOST = '127.0.0.1';
|
||||
var SERVER_PORT = 3333;
|
||||
|
||||
// Création du "socket" de connexion TCP/IP
|
||||
var client = new net.Socket();
|
||||
|
||||
// Notre objet "client" est une instance d'EventEmitter
|
||||
// (voir https://nodejs.org/api/events.html)
|
||||
// On peut donc lui attacher des fonctions,
|
||||
// appelées "callback" ou "handler", afin de réagir
|
||||
// à certains évènements liés au cycle de vie de l'objet
|
||||
|
||||
// On attache un callback à notre client pour
|
||||
// l'évènement "connect". En passant une référence
|
||||
// à la fonction "onConnect" on indique que l'on
|
||||
// souhaite que celle ci soit automatiquement
|
||||
// invoquée lorsque le client sera connecté.
|
||||
client.on('connect', onConnect);
|
||||
|
||||
// Les données transmises par le serveur au client sont
|
||||
// automatiquement transformées sous forme d'évènement
|
||||
// par NodeJS. On peut récupérer ces données en ajoutant
|
||||
// un callback sur notre objet "client" pour l'évènement
|
||||
// "data"
|
||||
client.on('data', onData);
|
||||
|
||||
// L'évènement "close" est émis lorsque la connexion
|
||||
// avec le serveur est close.
|
||||
client.on('close', onClose);
|
||||
|
||||
// L'évènement "error" est émis lorsque une erreur
|
||||
// est soulevée. (exemple: perte de connexion)
|
||||
client.on('error', onError);
|
||||
|
||||
|
||||
// On initialise la connexion au serveur
|
||||
console.log('Tentative de connexion au serveur: %s:%s', SERVER_HOST, SERVER_PORT);
|
||||
client.connect(SERVER_PORT, SERVER_HOST);
|
||||
|
||||
function onConnect() {
|
||||
|
||||
// Nous sommes connecté au serveur !
|
||||
console.log('Connecté sur ' + SERVER_HOST + ':' + SERVER_PORT);
|
||||
|
||||
// TODO envoyer les commandes au serveur ici
|
||||
|
||||
// On peut envoyer des données au serveur via la méthode
|
||||
// write() du client
|
||||
// Exemple
|
||||
client.write('add 1');
|
||||
|
||||
// Si on le souhaite, on peut clore la connexion
|
||||
// au serveur via la méthode destroy()
|
||||
client.destroy();
|
||||
|
||||
}
|
||||
|
||||
function onData(data) {
|
||||
|
||||
// Les données sont reçues sous forme de bytes. On les transforme
|
||||
// en chaine de caractères UTF-8 pour faciliter leur manipulation
|
||||
var str = data.toString('utf8');
|
||||
|
||||
console.log('Données reçues du serveur: %s', str);
|
||||
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
console.log('La connexion au serveur a été close.')
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
console.log('Une erreur a été soulevée: %s', err);
|
||||
process.exit(1);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
msc {
|
||||
|
||||
wordwraparcs=true, hscale=2;
|
||||
|
||||
Client,Server;
|
||||
|
||||
... [ label = "Lancement du serveur et du client" ];
|
||||
|
||||
Server box Server [ label="acc = 0" ];
|
||||
|
||||
Client->Server [ label="status" ];
|
||||
Server->Client [ label="0" ];
|
||||
|
||||
Client->Server [ label="add 10" ];
|
||||
Server->Server [ label="acc = acc + 10" ];
|
||||
Server->Client [ label="10" ];
|
||||
|
||||
Client->Server [ label="sub 2" ];
|
||||
Server->Server [ label="acc = acc - 2" ];
|
||||
Server->Client [ label="8" ];
|
||||
|
||||
Client->Server [ label="div 2" ];
|
||||
Server->Server [ label="acc = acc / 2" ];
|
||||
Server->Client [ label="4" ];
|
||||
|
||||
Client->Server [ label="mul 5" ];
|
||||
Server->Server [ label="acc = acc * 5" ];
|
||||
Server->Client [ label="20" ];
|
||||
|
||||
Client->Server [ label="reset" ];
|
||||
Server->Server [ label="acc = 0" ];
|
||||
Server->Client [ label="0" ];
|
||||
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
|
@ -0,0 +1,48 @@
|
|||
var net = require('net');
|
||||
|
||||
var HOST = '127.0.0.1'; // On écoute sur toutes les interfaces
|
||||
var PORT = 3333;
|
||||
|
||||
var server = net.createServer();
|
||||
|
||||
server.on('connection', onClientConnection);
|
||||
server.on('listening', onListening);
|
||||
server.on('error', onError);
|
||||
|
||||
server.listen(PORT, HOST);
|
||||
|
||||
function onClientConnection(client) {
|
||||
|
||||
console.log('Nouveau client depuis %s:%s', client.remoteAddress, client.remotePort);
|
||||
|
||||
// On souhaite commencer à récupérer les données
|
||||
// provenant de ce nouveau client
|
||||
client.on('data', onClientData);
|
||||
client.on('close', onClientClose);
|
||||
|
||||
function onClientData(data) {
|
||||
|
||||
// Les données sont reçues sous forme de bytes. On les transforme
|
||||
// en chaine de caractères UTF-8 pour faciliter leur manipulation
|
||||
var str = data.toString('utf8');
|
||||
|
||||
console.log('Données reçues de %s:%s: "%s"', client.remoteAddress, client.remotePort, str);
|
||||
|
||||
// TODO désérialiser les données reçues et traiter les opérations.
|
||||
|
||||
}
|
||||
|
||||
function onClientClose() {
|
||||
console.log('Le client %s:%s s\'est déconnecté.', client.remoteAddress, client.remotePort);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onListening() {
|
||||
console.log("Serveur en écoute sur %s:%s", HOST, PORT)
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
console.log('Une erreur a été soulevée: %s', err);
|
||||
process.exit(1);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
var net = require('net');
|
||||
|
||||
// Notre client se connecte sur le serveur local
|
||||
var SERVER_HOST = '127.0.0.1';
|
||||
var SERVER_PORT = 3333;
|
||||
|
||||
// Création du "socket" de connexion TCP/IP
|
||||
var client = new net.Socket();
|
||||
|
||||
// Notre objet "client" est une instance d'EventEmitter
|
||||
// (voir https://nodejs.org/api/events.html)
|
||||
// On peut donc lui attacher des fonctions,
|
||||
// appelées "callback" ou "handler", afin de réagir
|
||||
// à certains évènements liés au cycle de vie de l'objet
|
||||
|
||||
// On attache un callback à notre client pour
|
||||
// l'évènement "connect". En passant une référence
|
||||
// à la fonction "onConnect" on indique que l'on
|
||||
// souhaite que celle ci soit automatiquement
|
||||
// invoquée lorsque le client sera connecté.
|
||||
client.on('connect', onConnect);
|
||||
|
||||
// Les données transmises par le serveur au client sont
|
||||
// automatiquement transformées sous forme d'évènement
|
||||
// par NodeJS. On peut récupérer ces données en ajoutant
|
||||
// un callback sur notre objet "client" pour l'évènement
|
||||
// "data"
|
||||
client.on('data', onData);
|
||||
|
||||
// L'évènement "close" est émis lorsque la connexion
|
||||
// avec le serveur est close.
|
||||
client.on('close', onClose);
|
||||
|
||||
// L'évènement "error" est émis lorsque une erreur
|
||||
// est soulevée. (exemple: perte de connexion)
|
||||
client.on('error', onError);
|
||||
|
||||
|
||||
// On initialise la connexion au serveur
|
||||
console.log('Tentative de connexion au serveur: %s:%s', SERVER_HOST, SERVER_PORT);
|
||||
client.connect(SERVER_PORT, SERVER_HOST);
|
||||
|
||||
function onConnect() {
|
||||
|
||||
// Nous sommes connecté au serveur !
|
||||
console.log('Connecté sur ' + SERVER_HOST + ':' + SERVER_PORT);
|
||||
|
||||
// TODO envoyer les commandes au serveur ici
|
||||
|
||||
// On peut envoyer des données au serveur via la méthode
|
||||
// write() du client
|
||||
// Exemple
|
||||
client.write('add 100\n');
|
||||
client.write('sub 10\n');
|
||||
client.write('div 9\n');
|
||||
client.write('mul 5\n');
|
||||
|
||||
client.write('status\n');
|
||||
|
||||
client.write('reset\n');
|
||||
client.write('status\n');
|
||||
|
||||
// Si on le souhaite, on peut clore la connexion
|
||||
// au serveur via la méthode destroy()
|
||||
// client.destroy();
|
||||
|
||||
}
|
||||
|
||||
function onData(data) {
|
||||
console.log('Données récupérée du serveur: %s', data);
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
console.log('La connexion au serveur a été close.')
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
console.log('Une erreur a été soulevée: %s', err);
|
||||
process.exit(1);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
server.js {
|
||||
daemon: node server.js
|
||||
}
|
||||
client.js server.js {
|
||||
prep: node client.js
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
var net = require('net');
|
||||
|
||||
var HOST = '127.0.0.1'; // On écoute sur toutes les interfaces
|
||||
var PORT = 3333;
|
||||
|
||||
var server = net.createServer();
|
||||
|
||||
server.on('connection', onClientConnection);
|
||||
server.on('listening', onListening);
|
||||
server.on('error', onError);
|
||||
|
||||
server.listen(PORT, HOST);
|
||||
|
||||
function onClientConnection(client) {
|
||||
|
||||
var accumulator = 0;
|
||||
|
||||
console.log('Nouveau client depuis %s:%s', client.remoteAddress, client.remotePort);
|
||||
|
||||
// On souhaite commencer à récupérer les données
|
||||
// provenant de ce nouveau client
|
||||
client.on('data', onClientData);
|
||||
client.on('close', onClientClose);
|
||||
|
||||
function onClientData(data) {
|
||||
|
||||
console.log('Données reçues de %s:%s: "%s"', client.remoteAddress, client.remotePort, data);
|
||||
|
||||
var messages = data.toString('utf8').split('\n');
|
||||
|
||||
for(var i = 0; i < messages.length; i++) {
|
||||
|
||||
var line = messages[i];
|
||||
var tokens = line.split(' ');
|
||||
|
||||
if (tokens.length !== 1 && tokens.length !== 2) {
|
||||
client.write('invalid message');
|
||||
continue
|
||||
}
|
||||
|
||||
var command = tokens[0];
|
||||
|
||||
var arg;
|
||||
if (tokens.length === 2) {
|
||||
try {
|
||||
arg = parseFloat(tokens[1]);
|
||||
} catch(err) {
|
||||
client.write('invalid message\n');
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Tokens", tokens);
|
||||
|
||||
switch(command) {
|
||||
case "add":
|
||||
accumulator = accumulator + arg;
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
case "sub":
|
||||
accumulator = accumulator - arg;
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
case "div":
|
||||
accumulator = accumulator / arg;
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
case "mul":
|
||||
accumulator = accumulator * arg;
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
case "status":
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
case "reset":
|
||||
accumulator = 0;
|
||||
client.write(accumulator+'\n');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function onClientClose() {
|
||||
console.log('Le client %s:%s s\'est déconnecté.', client.remoteAddress, client.remotePort);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onListening() {
|
||||
console.log("Serveur en écoute sur %s:%s", HOST, PORT)
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
console.log('Une erreur a été soulevée: %s', err);
|
||||
process.exit(1);
|
||||
}
|
Loading…
Reference in New Issue