:: Enseignements :: Master :: M1 :: 2014-2015 :: Programmation d'applications réseaux ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
Serveurs TCP, implémentations de la concurrence
|
Exercice 1 - Serveur Somme de Long itératif
On souhaite maintenant écrire notre propre classe qui
implante un serveur TCP rendant le service de somme de
long, tel que vu au TP précédent:
-
le client envoie un INT (en big endian) donnant le nombre
d'opérandes puis chacun des opérandes qui sont des LONG en big endian;
- le serveur répond par un LONG qui correspond à la
somme des opérandes.
- Le serveur ne ferme pas la connection qui peut donc être utilisée
pour faire plusieurs sommes pendant la même connexion cliente.
La première version de ce serveur,
IterativeLongSumServer, un peu simpliste,
se contente d'accepter un client, de traiter ses requêtes (éventuellement plusieurs sommes),
et lorsque la connexion est fermée, il accepte un client suivant, et ainsi de suite.
Pour écrire ce serveur, vous vous baserez sur la trame ci-dessous.
Il se lance par exemple avec java fr.upem.net.tcp.IterativeLongSumServer 7777
Vous pouvez le testez avec votre client réalisé lors d'un TP précédent:
java fr.upem.net.tcp.ClientLongSum localhost 7777
Que se passe-t-il si deux clients tentent d'accéder au service simultanément?
Le client arrivé en second est il connecté? accepté? servi?
Est-ce que le comportement est le même qu'en UDP?
Pour vous aider à mieux comprendre ce qui se passe, vous pouvez utiliser le
ClientLongSumVerbose.jar,
qui se connecte au serveur, et lui envoie successivement 5 salves d'opérandes à sommer,
en attendant entre chaque salve un délai paramétrable sur la ligne de commande.
Par exemple, dans deux fenêtres de shell distictes, lancez presque simultanément
java -jar ClientLongSumVerbose.jar localhost 7777 2000
et
java -jar ClientLongSumVerbose.jar localhost 7777 100
Exercice 2 - Serveur Somme de Long concurrents
On veut maintenant permettre à plusieurs clients d'être
servis simultanément par le serveur. Pour cela,
nous allons devoir créer plusieurs threads.
-
Une première méthode, assez facile à implémenter, consiste à créer
une nouvelle thread à chaque fois qu'un client est accepté, et à
lui confier le service de ce client.
Sur le principe du serveur IterativeLongSumServer précédent,
créez une nouvelle classe OnDemandConcurrentLongSumServer, qui va simplement
créer et démarrer une nouvelle thread à chaque nouveau client accepté.
Pour contrôler ce qui se passe au niveau de la JVM, vous allez "monitorer"
votre application en utilisant jconsole pour observer le nombre de threads
utilisés par votre serveur, et démarrer des clients
de plus en plus nombreux qui se connectent à votre serveur.
Que pensez vous de cette solution? Que se passe-t-il lorsque le nombre de clients
augmente?
-
Pour borner le nombre de threads créées à une valeur passée sur la
ligne de commande (maxClient), écrire une autre classe
BoundedOnDemandConcurrentLongSumServer qui vérifie, avant de démarrer
une nouvelle thread pour traiter un nouveau client, que
le nombre de threads ne dépasse pas cette valeur maximum. Par ailleurs, lorsqu'un
client est terminé de servir et que sa thread s'arrête, il faut penser à
décrémenter le nombre de threads actives...
Implémentez cette solution et vérifiez son comportement avec jconsole.
Que pensez vous de cette solution? Rappelez vous de ce qui est coûteux dans
l'usage des threads...
-
Sans nécessairement coder les solutions correspondantes, réfléchir
aux solutions alternatives qui permettraient de palier les problèmes
identifiés pour les solutions précédentes.
-
Réalisez une implémentation à nombre de thread fixes (et pré-démarrées),
FixedPrestartedConcurrentLongSumServer qui
utilise des threads pré-démarrées et qui attendent en concurrence de pouvoir réaliser
une acceptation de nouveau client. Pour éviter que cette attente soit active,
il suffit de les exclure mutuellement grâce à une synchronisation sur un lock commun
au moment de faire le accept().
Vérifiez son comportement avec jconsole en testant avec de
multiples clients.
Que deviennent les clients qui tentent de se connecter lorsque
maxClient sont actuellement en train d'être servis? Y aurait-il
une différence si on utilisait un ExecutorService?
-
Proposez une méthode shutdown() dans la classe du serveur
qui permette "proprement" d'arrêter d'accepter de nouveaux clients, et une méthode
shutdownNow() qui tente plus radialement d'arrêter un serveur démarré, qu'il soit
ou non en cours de traitement de clients; les clients devront voir
leur connexion fermée.
-
En commenaçant par réfléchir à votre premier serveur, IterativeLongSumServer,
proposez une solution permettant au serveur de se débarrasser des clients inactifs
depuis trop longtemps (durée paramétrable). Mettez en place cette solution, et
transposez là au cas du serveur FixedPrestartedConcurrentLongSumServer.
© Université de Marne-la-Vallée