TP3 - Nicolas Baudru
Transcription
TP3 - Nicolas Baudru
Java - TP3 Nicolas Baudru, Carine Guivier-Curien, Laurent Vallet Année 2008-2009 Le but de ce TD est d'écrire une application client/serveur de type msn : 1. Des clients se connectent à un serveur 2. Un client envoie un message au serveur 3. le serveur distribue le message à tous les clients. Nous nous focaliserons ici uniquement sur les aspects réseaux. La conception de l'IHM (Interface Homme-Machine) ne fait pas partie de ce TP. Par conséquent, elle vous est donnée clé en main . Affiche les messages transmis par le Client et retourne au client le message à envoyer IHM.class Demande à l'IHM d'afficher les messages reçus et transmet les messages de l'IHM Client.class au serveur Serveur.class Recoit un message d'un client puis le transmet à tous les clients Client.class Client.class 1 Ce TP se décompose en trois étapes. Dans un premier temps, vous allez aprendre à utiliser l'IHM qui vous est donnée. Puis vous écrirez la partie client de l'application. Le client se connecte au serveur, transmet et reçoit des messages et interagit avec l'IHM. A cette étape, vous utiliserez l'IHM vu à l'étape 1 ainsi qu'un serveur clé en main qui vous sera fournit pour pouvoir tester votre client. Enn, si vous allez au bout de ce TP, vous écrirez vous-même la partie serveur. Exercice 1 (prise en main de l'IHM) La classe IHM est disponible à l'adresse http ://nicolas.baudru.perso.esil.univmed.fr/Enseignement/enseignement.html Sa structure est la suivante : public class IHM implements ActionListener { public IHM () { // Initialise l ' IHM } public void go () { // Une interface graphique type msn s ' affiche à l ' écran } synchronized public String getNextMessageToSend () { // Affiche dans un terminal le message tapé dans l ' IHM // puis retourne ce message } public void writeMessage ( String mess ) { // écrit dans la fenêtre de discussion // ET dans le terminal le chaîne de caracères } } // fin class IHM mess Remarque : la méthode getMessageToSend() est bloquante. Cela signie que le thread invoquant cette méthode reste bloqué sur cette méthode (son exécution est suspendue) jusqu'à ce que l'utilisateur presse la touche envoi de l'IHM. 1. Ecrire une classe TestIHM1 qui successivement ache dans un terminal le message écrit dans l'IHM, puis demande à l'utilisateur de taper une phrase dans le terminal, et enn, ache cette dernière phrase dans l'IHM. 2. Ecrire une classe TestIHM2 qui de manière concurrente ache dans un terminal tous les messages écrits dans l'IHM par l'utilisateur ache dans l'IHM tous les messages écrits par l'utilisateur dans le terminal. Indication : l'utilisation de Threads est nécessaire. Exercice 2 (coté client) L'écriture du programme client nécessite de savoir faire trois choses : 2 comment établir une connexion avec le serveur comment envoyer des messages au serveur comment recevoir des messages du serveur Établir une connexion en Java : Pour établir une connexion à un serveur, il faut connaître le serveur , i.e. son adresse IP et son numéro de port TCP. Autrement dit il faut connaître la s ocket du serveur (c-à-d le couple (IP,port)). En Java c'est la seule information nécessaire à l'établissement d'une connexion. Tout le reste se fait automatiquement au travers de la classe Socket du package java.net : Socket maSocket = new Socket ( " 192.164.2.21 " , 5000); Cette simple ligne crée une socket du coté client nommée maSocket puis établie une connexion entre maSocket et la socket du serveur ("192.164.2.21", 5000). Recevoir des données sur une socket : Toutes les classes et méthodes qui suivent se trouvent dans le package java.io.* qui est donc à inclure dans votre programme. Pour recevoir des données, il sut de demander à la socket de nous donner ce qu'elle a reçu : monFlotBasNiveau = maSocket . getInputStream (); Cette méthode renvoie un objet de type InputStream permettant de récupérer un ot d'E/S de bas niveau (un truc du type 010011...). An de récupérer un ot plus "sympa", à base de caractères, on va chaîner un objet de type InputStreamReader à ce ot de bas niveau : InputStreamReader monFlotDeCaracteres = new InputStreamReader ( monFlotBasNiveau ); L'objet monFlotDeCaracteres permet de récupérer le ot de caractères correspondant au ot de bas niveau (cela se fait caractère par caractère à l'aide de la méthode read()). An d'utiliser facilement ce ot de caractères et pour plus d'ecacité, nous pouvons le "bueriser" : BufferedReader monBuffer = new BufferedReader ( monFlotDeCaractere ) Nous pouvons alors récupérer les messages lignes par lignes par exemple : String monMessage = monBuffer . readLine (); Pour fermer l'ensemble des ots de données, il sut de taper monBuffer . close (); Envoyer des données sur une socket : Plusieurs méthodes sont possibles. Par exemple nous pouvons chaîner un BueredWritter à un OutputStreamWritter, lui même chaîné au ot de bas niveau de sortie du socket : BufferedWritter monBuffer = new BufferedWritter ( new OutputStreamWritter ( monSocket . getOutputStream ())); 3 Pour écrire sur la socket : monBuffer . write ( " blabla " ); Les messages écrits seront envoyés à la socket une fois le buer plein (on peut aussi utiliser la méthode ush() pour forcer le buer à ce vider) . Plus simplement, on peut aussi utiliser PrintWritter qui se chargera de transformer une chaîne de caractères en ot de bas niveau. Cet objet doit être chaîné au ot de bas niveau de la socket : PrintWritter maChaine = new PrintWritter ( monSocket . getOutputStream ()); Et pour écrire sur la socket : maChaine . println ( " blabla " ); // suivit de maChaine . flush (); // pour envoyer les données . Pour fermer l'ensemble des ots de données, il sut de taper maChaine . close (); Questions : Pour pouvoir tester votre programme client, vous aurez besoin d'un serveur test. Celui-ci est disponible à l'adresse http ://nicolas.baudru.perso.esil.univmed.fr/Enseignement/enseignement.html Pour le faire fonctionner, vous devez télécharger le chier Serveur.zip, le décompresser, puis taper java Serveur dans un terminal (à partir du répertoire décompressé). Le port par défaut utilisé par le serveur est le port 5000. Si ce port n'est pas disponible ou ne fonctionne pas sur votre machine, vous pouvez choisir un autre port pour le serveur en le rajoutant simplement en argument. Par exemple java Serveur 12321 démarre le serveur sur le port 12321. 1. Écrire la classe ClientSimplie décrite ci-dessous. Cette classe est un petit programme qui permet de se connecter au serveur (en lui précisant l'adresse IP et le port du serveur), de lui envoyer un message et enn de recevoir un message du serveur. 2. Lorsque la classe compile, tester-la en utilisant le serveur test. Pour cela lancer le programme Serveur dans un premier terminal. Dans un autre terminal, lancer votre programme ClientSimplie. Vérier que vous recevez bien le message envoyé. 3. Pousser le test un peu plus loin en exécutant de nouveau le programme ClientSimplie dans deux nouveaux terminaux simultanément. Que se passe-t-il ? 4. Écrire une nouvelle classe ClientSansIHM basée sur le modèle de la classe ClientSinplie mais utilisant deux threads dans son main() : l'un permet d'envoyer en boucle des messages au serveur (les messages à envoyer sont tapés par l'utilisateur dans le terminal) l'autre permet de receptionner tous les messages en provenance du serveur. 5. Tester de nouveau votre classe avec le serveur test. 6. Enn, écriver la classe Client nale en intégrant l'IHM à la classe ClientSansIHM. 4 import java . io .*; import java . net .*; import java . util .*; public class ClientSimplifie { BufferedReader lecture ; // pour le flot d ' entrée venant du serveur PrintWriter ecriture ; // pour le flot de sortie vers le serveur Socket sock ; // le socket client public ClientSimplifie () { // établie une connexion au serveur par un appel à connexionServeur () } private void connexionServeur ( String adresseIPServeur , portServeur ) { // créer un objet socket lié au socket serveur et l ' affecte à sock // puis établie les chaînages de flot nécessaires // pour l ' envoi et la reception de messages } public static void main ( String [] args ) { // crée un nouveau client // envoi un seul message au serveur // puis dans une boucle infini , affiche les messages reçus du serveur } } // fin classe ClientSimplifie Exercice 3 (coté serveur) Quatre choses à savoir faire : créer une "socket d'écoute" qui attend une demande de connexion accepter et établir une connexion avec le client comment envoyer des messages au client comment recevoir des messages du client Créer une socket d'écoute : Très simple en Java. La ligne suivante crée une socket d'écoute sur un port donné. Cette socket écoute les requêtes entrantes d'un client qui souhaiterait se connecter. ServerSocket monSocketEcoute = new ServeurSocket (6345); Accepter et établir une connexion avec le client : Socket monSocket = monSocketEcoute . accept (); Cette ligne crée une nouvelle socket chez le serveur (diérente du socket d'écoute) et établie une connexion avec le client. La méthode accept() est bloquante, c'est-à-dire que le thread courant exécutant cette méthode va rester bloqué jusqu'à ce qu'une demande de connexion provenant d'un client se produise. La méthode accept() va alors créer la nouvelle socket chez le serveur et établir 5 la connexion entre cette nouvelle socket et la socket client (le serveur connaît la socket client car la demande de connexion d'un client contient toujours la socket de ce client). Remarques : Notez bien que la connexion s'eectue sur un port diérent de la socket d'écoute. Cette dernière peut donc continuer d'attendre d'autres demandes de connexion. Pour chaque demande de connexion, une nouvelle socket est créée coté serveur. Donc chez le serveur, un client peut être identié à la socket créée lors de l'acception de la requête de connexion. Recevoir et envoyer des données sur la socket : L'envoi et la réception de données du coté serveur s'eectuent de la même manière que pour le client. Il sut d'utiliser la socket correspondant à un client donné pour communiquer avec ce client. Question : Tenter d'écrire votre propre serveur. Celui-ci ressemble au Client mais doit en plus pouvoir gérer une liste de clients. Son comportement est des plus simples. Le serveur attend un message d'un client quelconque puis diuse ce message à l'ensemble des clients. 6