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