CESI: Architecture N tiers, exercice

This commit is contained in:
wpetit 2018-01-13 16:53:31 +01:00
parent e041aa9b9f
commit c8caed6efc
9 changed files with 380 additions and 12 deletions

View File

@ -130,19 +130,9 @@ La conception d'une application distribuée nécessite d'établir une répartiti
--- ---
## Exercice (1) ## Exercice
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)
### Implémentation d'une calculatrice à état distribuée en NodeJS
--- ---

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,6 @@
server.js {
daemon: node server.js
}
client.js server.js {
prep: node client.js
}

View File

@ -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);
}