basée sur des sockets en mode connecté
T.D. "Systèmes distribués"
L'objectif de ce T.D est d'étudier des protocoles de synchronisation
dans les systèmes distribués.
Il s'agit de programmer un service de Télédiscussion : des personnes (situées sur des machines différentes) dialoguent entre elles par l'intermédiaire de leur clavier.
Cet exemple est basé sur une relation client-serveur avec des clients communiquant par l'intermédiaire du serveur, celui-ci maintenant une liste des connexions courantes.
Le fonctionnement de ce serveur est simple. Il envoie à tous les clients (enregistrés dans la liste de connexions) le message qu'il a reçu d'un client.
Tous les messages sont préfixés par le nom du client afin d'identifier les propos des différents interlocuteurs. Un client peut intervenir dans la discussion à tout instant, rester seulement "à l'écoute" ou terminer la discussion .
Cette application constitue une communication de groupes par télédiscussion.
Etude du modèle et des protocoles :
Le modèle et les protocoles à envisager (voir cours de Le Than "systèmes distribués") sont :
Il s'agit d'implémenter en Java cette
application client-serveur de type télédiscussion.
Avec cet exemple complet, outre la mise en pratique des mécanismes
de synchronisation et de communication de groupes des systèmes distribués,
vous aborderez un grand nombre d'aspects intéressants de la programmation
réseau avec Java :
Notions à connaître
:
Vous pourrez allez voir cette page de rappels rapides sur l'utilisation
des sockets en Java.
Vous pourrez aussi vous aider des TDs Numero 2
et 3 sur mon cours "programmation réseau
en Java" (.ps.gz ou .pdf).
Pour l'utilisation des threads Java, vous pouvez jeter un coup d'oeil
sur mon cours "multithreading en Java" (.ps.gz
ou .pdf).
Eléments d'implémentation :
Identification
et rôle des processus nécessaire à la télédiscussion
Un certain nombre de processus (threads) ont été identifiés
pour mettre en oeuvre simplement cette application de télédiscussion
(voir schéma ci-après).
Tous les processus (sauf l'application client) sont des instances de
la classe java.lang.Thread.
Identification
des données à manipuler
public void run() {
try
{
while(true) {
... // Attente d'une demande
... // Création du processus Connexion
... // Ajout de ce processus dans la liste de connexions
// (en exclusion mutuelle)
}
} catch(IOException e) {...}
}
}
Ce processus (dans sa méthode run()) attend un message du client sur son socket. Lorsqu'elle en reçoit un, elle demande l'accès exclusif (synchronized) à la liste de connexions, puis écrit sur le socket de chaque connexion de cette liste le message qu'elle a reçu.
public Connexion(Socket
client_sock, Serveur serveur, Nettoyeur nettoyeur) {
... // Récupération des canaux de lecture et d'écriture
du socket
this.start() ;
}
public void run() {
try {
while(true){
... // Attente d'une lecture d'un message sur le canal de lecture du socket
... // Envoyer le message à tous les clients de la liste (accès
exclusif)
... // Si message "FIN" alors terminer ce processus
}
}
catch(IOException e){...}
finally {
... // Fermer le socket
... // Réveiller le processus Nettoyeur (accès exclusif)
}
}
}
La veille du processus Nettoyeur peut-être interrompue par une notification provenant d'un processus Connexion.
public Nettoyeur(Serveur
serveur){
...
this.start();
}
public void run() {
while(true) {
... // Attente de 5 secondes
... // Pour tous les processus Connexion de la liste des connexions
... // Tester si le processus est actif
... // Si oui, le retirer de la liste
}
}
}
Son constructeur reçoit comme paramètres la référence du socket (pour récupérer son canal de lecture) et celle du champs de texte de l'application client pour y afficher le message en provenance de la connexion.
public Ecouteur(Socket s,
TextArea out) {
... // Récupération du canal de lecture du socket
this.start();
}
public void run() {
try {
while(true){
... // Lecture du message en provenance de la connexion
... // Si la connexion est terminée : break
... // Sinon, on affiche le message dans le champs de texte
}
}
catch(IOException e) {...}
finally {...}
}
}

L'interface graphique (voir ci-dessus) de cette application est une fenêtre composée de 2 champs de texte : un en bas pour la saisie et un en haut pour l'affichage des messages. Cette fenêtre contient également 2 boutons Send et Fin. La classe AppliClient est une sous-classe de la classe java.awt.Frame. Le constructeur de l'application client demande la création d'un socket sur la machine sur laquelle fonctionne le serveur. Le processus Ecouteur est crée pour permettre la lecture sur le socket de manière asynchrone avec la saisie des messages de l'utilisateur.
L'appui sur le bouton Send provoque l'écriture sur le socket du texte que l'utilisateur a tapé (préfixé par le nom du client).
L'appui sur le bouton Fin signale la demande de fin de connexion de la part d'un client (message "FIN") puis arrête l'application client.
Ceci constitue naturellement un exemple de petite interface graphique destinée aux clients. Mais vous êtes libre de faire differemment ...