Programmation client/serveur (sockets (sockets sur sur TCP/IP) TCP/IP) java.net © Philippe GENOUD UJF Novembre 2006 1 Communications Communications sur sur Internet Internet Pour communiquer sur Internet les ordinateurs utilisent différents protocoles : TCP (Transfert Control Protocol) protocole destiné aux applications nécessitant une communication fiable et robuste. Permet d’établir une connexion fiable entre deux machines pas de perte d’information respect de l’ordre dans lequel les informations sont envoyées et reçues les applications sont prévenues si la communication est interrompue UDP (User Datagram Protocol) protocole qui permet l’envoi de paquets indépendants (datagrammes) d’un ordinateur à un autre sans garantie quand à leur arrivée plus rapide mais moins fiable © Philippe GENOUD UJF Novembre 2006 2 1 Applications Applications réseau réseau Java Java Application Java Applications HTTP, ftp, telnet, … java.net Transport Package standard qui permet aux programmes Java de réaliser des communications réseau de façon indépendante du système TCP, UDP, … De nombreuses applications se basent sur ces protocoles Réseau IP, … Couche de base Object Device driver Couche Physique Ethernet, … InetAdress Représente une adresse IP Socket DatagramSocket URL ServerSocket Connexions TCP/IP DatagramPacket Connexions UDP URLConnection Connexions à un serveur HHTP HttpURLConnection © Philippe GENOUD Novembre 2006 UJF 3 Sockets Sockets et et applications applications Client/Serveur Client/Serveur Serveur Serveur port 1) Le programme serveur tourne sur une machine hôte spécifique Client Client Le programme serveur possède un « socket » associé à un numéro de port spécifique Le serveur est en attente d’une demande de connexion Serveur Serveur port 2) Le client effectue une demande de connexion demande de connexion Client Client Le client connaît la machine hôte (hostname ou numéro IP) et le numéro de port auquel est connecté le serveur nn ex ion Attribution d’un nouveau socket (et d’un nouveau numéro de port) © Philippe GENOUD Client Client Attribution d’un socket (et d’un numéro de port) co port port Serveur Serveur port 3) Le serveur accepte la connexion 2 flots de données sont établis entre les deux machines : Le client et le serveur peuvent communiquer en écrivant ou en lisant dans leur socket UJF Novembre 2006 4 2 Sockets Sockets TCP TCP dans dans java.net java.net Assymétrie des connexions Deux classes pour les sockets en mode connecté (TCP) ServerSocket Encapsule le socket du serveur Pour s’initialiser un ServerSocket a besoin d’un numéro de port Attente et acceptation des demandes de connexion Socket Encapsule le socket du client Pour s’initialiser un Socket a besoin d’une adresse IP et d’un numéro de port Demande de connexion avec le serveur © Philippe GENOUD 5 Novembre 2006 UJF Client Client // Serveur Serveur avec avec java.net java.net :: principe principe (1) Le serveur enregistre son service (2) Le serveur se met en attente de connexion Serveur Le client doit être démarré après le serveur. Si le serveur n’a pas encore démarré, levée d’une exception après un time-out ServerSocket svs = new ServerSocket(port#) Client (3) Etablir la connexion Socket sc = new Socket(host, port#) svs.accept() Sortie de accept retour d’un objet Socket (4) Utilisation du socket (4) Utilisation du socket Socket s = InputStream OutputStream OutputStream socket Canal TCP socket InputStream (5’) Fermer la connexion (5) Fermer la connexion s.close() sc.close() (6) Fermer le service svs.close() close() ferme (détruit) le socket côté client comme côté serveur © Philippe GENOUD UJF Novembre 2006 6 3 Echanges Echanges entre entre client client et et serveur serveur Pour échanger des données le serveur et le client utilisent les flux d’entrée/sortie fournis par leur socket : Un objet java.io.InputStream pour la lecture retourné par la méthode getInputStream() de Socket Un objet java.io.OutputStream pour l’écriture retourné par la méthode getOutputStream() de Socket InputStream et OutputStream sont les classes du package java.io proposant les primitives de base pour la lecture et l’écriture sur un flux d’entrée ou de sortie Deux types d’échanges sont possibles Échanges en mode ligne (chaînes de caractères) Echanges en mode bloc d’octets (bytes) © Philippe GENOUD UJF 7 Novembre 2006 Les Les classes classes d'entrées/sorties d'entrées/sorties Quelques classe du package java.io Flux de sortie Flux d'entrée Flux d'octets Flux de caractères Reader InputStream FilterInputStream FileInputStream ByteArrayInputStream BufferedInputStream InputStreamReader FileReader Writer OutputStream FilterOutputStream BufferedReader FileOutputStream ByteArrayOutputStream BufferedOutputStream InputStreamWriter BufferedWriter FileWriter © Philippe GENOUD UJF Novembre 2006 8 4 Echanges Echanges en en mode mode ligne ligne Requêtes et réponses sont constituées d’une ou plusieurs lignes de texte (ASCII 7 bits, UTF-8, ISO-8859-1 (Latin-1) …) Exemple : HTTP, SMTP, … Utilisation des classes d’entrées/sorties du package java.io BufferedReader BufferedWriter Attention : Variation des terminateurs de ligne selon les OS LF '\n' , CR '\r' , CRLF '\r' '\n' Encodage n’est pas supporté par tous les langages Certains protocoles (HTTP, SMTP …) limitent la longueur des lignes © Philippe GENOUD Novembre 2006 UJF 9 Echanges Echanges en en mode mode ligne ligne Le flux d’entrée 1) Obtention d’un flux (stream) d’entrée simple : définit les opérations de base InputStream in = maSocket.getInputStream(); 2) Création d’un stream convertissant les octets (bytes) reçus en char InputStreamReader isReader = new InputStreamReader(in); BufferedReader String buffReader = new BufferedReader(isReader); 3) Création d’un stream de lecture avec tampon pour lire ligne par ligne dans un stream de caractères line = buffReader.readLine(); 4) Lecture d’une ligne (chaîne de caractères) © Philippe GENOUD UJF Novembre 2006 10 5 Echanges Echanges en en mode mode ligne ligne Le flux de sortie 1) Obtention d’un flux (stream) de sortie simple : définit les opérations de base OutputStream out = maSocket.getOutputStream(); 2) Création d’un stream convertissant chaînes de caractères en octets (bytes) PrintWriter writer = new PrintWriter(out); writer.println(line); 3) Envoi d’une ligne (line est une String) writer.flush(); 4) Force l’envoi effectif sur le réseau (force le « vidage » du buffer d’E/S) © Philippe GENOUD UJF Novembre 2006 11 Echanges Echanges en en mode mode bloc bloc de de données données Les données échangées sont des blocs d’octets (bytes) Les blocs sont d’une taille et d’un format connus des deux partenaires (le serveur et le client) un bloc peut avoir un entête qui contient un code d’opération et la longueur des données (talle du bloc) Exemple : RPC (Remote Procedure Call), RMI (Remove Method Invocation), … En Java, possibilité d’utiliser les classes java.io.DataInputStream java.io.DataOutputStream © Philippe GENOUD UJF Novembre 2006 12 6 Protocole Protocole d’échange d’échange Un protocole d’échange spécifiant la structure des requêtes et réponses échangées par le client et le serveur doit être fixé. Le client et le serveur doivent respecter ce protocole Exemples de problèmes possibles Format de données incorrect : le client envoie un entier alors que le serveur attend un flottant Interblocage : le client attend des données du serveur et le serveur attend des données du client Hétérogénéité des plateformes client et serveur différence de codage des données sur le serveur et le client © Philippe GENOUD 13 Novembre 2006 UJF Classes Classes d’exceptions d’exceptions de de java.net java.net C Coté client Principales classes d’exceptions de java.net S Coté serveur java.lang.Exception C S Problème générique de connexion Erreur du protocole TCP/IP (problème de configuration De la pile TCP/IP) java.io.IOException C S SocketException ProtocolException UnknownHostException ConnectException C Erreur du DNS : L’adresse BindException IP de l’hôte ne peut être déterminée NoRouteToHostException C Refus de connexion par l’hôte (aucun service associé au numéro de port) S Un service est déjà associé à ce numéro de port © Philippe GENOUD C UJF Novembre 2006 L’hôte n’est pas joignable. Le routeur ne trouve pas le serveur ou le serveur est protégé par un firewall 14 7 Servir Servir plusieurs plusieurs clients clients ServeurSocket sSock = new ServeurSocket(4444); Ne permet de servir qu’un seul client Socket s = sSock.accept(); // création d’une socket quand un demande de connexion a été acceptée // traiter le client associé à cette connexion … Si plusieurs demandes de connexion arrivent simultanément, une seule est acceptée, les autres sont mises en attente jusqu’au accept suivant (modulo les time-out) ServeurSocket sSock = new ServeurSocket(4444); boolean arretServeur = false; Sert plusieurs clients MAIS consécutivement while (! arretServeur) { Socket s = sSock.accept(); // création d’une socket quand un demande de connexion a été acceptée // traiter le client associé à cette connexion … } Pour accepter et traiter plusieurs connexions simultanées, il faut multithreader le serveur © Philippe GENOUD UJF Novembre 2006 15 Serveur Serveur Multithreadé Multithreadé ServeurSocket sSock = new ServeurSocket(4444); boolean arretServeur = false; while (! arretServeur) { Socket s = sSock.accept(); création d’une socket quand un demande de connexion a été acceptée // // traiter le client associé à cette connexion … Créer et Lancer un thread pour traiter ce client } Thread dispatcher Attente demande de connexion Récupération de la socket Dédiée à la connexion Création et lancement d’un thread de service Thread de service Client 1 Prise en charge charge du Prise Prise en en charge du du dialogue avec avec le client client 1 dialogue dialogue avec le le client 1 1 Attente demande de connexion Récupération de la socket Dédiée à la connexion Création et lancement d’un thread de service Attente demande de connexion Thread de service Client 2 Prise en en charge charge du du Prise dialogue dialogue avec avec le le client client … © Philippe GENOUD UJF Novembre 2006 16 8 Serveur Serveur Multithreadé Multithreadé ... ServeurSocket sSock = new ServeurSocket(4444); boolean arretServeur = false; while (! arretServeur) { Socket sock = sSock.accept(); // création d’une socket quand un demande de connexion a été acceptée // traiter le client associé à cette connexion // créer un thread pour traiter le client … ServiceClient sc = new ServiceClient(sock); Thread threadService = new Thread(sc); threadService.start(); } ... <interface> Runnable ServiceClient + run() + ServiceClient(Socket s) + run() Le traitement du client © Philippe GENOUD UJF Novembre 2006 17 Client Client Multitread Multitread public class ServiceClient implements Runnable { private Socket s; private BufferedReader in; private PrintWriter out; private boolean termine = false; ... public ServiceClient(Socket s) { this.s = s; in = new BufferedReader( new InputStreamReader((s.getInputStream())); out = new PrintWriter(s.getOutputStream()); ... } Création des flux d’entrée/sortie pour communiquer avec le serveur public void run() { Le traitement du try { client while (! termine) { String line = in.readLine(); ... Une gestion plus fine des out.println(...); exceptions est possible } Quel que soit le cas de figure } on essaye de fermer le catch (IOException ioe) { ... } socket avant de terminer le finally { traitement du client try { s.close(); } catch (IOException ioe) { ... } } } ... } © Philippe GENOUD UJF Novembre 2006 18 9 Exercice Exercice (1) Ecrire une application client serveur minimale Le client lit une chaîne au clavier qu’il envoie vers le serveur Le serveur affiche la chaîne lue et renvoie un accusé de réception au client qui l’affiche Pour terminer sa session le client envoie la chaîne "fin" (2) Modifier le programme de façon à avoir plusieurs clients © Philippe GENOUD UJF Novembre 2006 19 10