Programmation Réseaux Pr. BEN LAHMAR El habib
Le modèle OSI Le modèle OSI a été défini par l'Internatinal Standardization Organisation (ISO). Ce modèle se décompose en 7 couches. Les services de chaque couche peuvent interagir uniquement avec les services des couches contigües, via un Service Access Point (SAP). Chaque couche définit un protocole de niveau N et échange des Protocol Data Unit de niveau N (N- PDU). Chaque N-PDU est encapsulé dans (N-1)- PDU.
Le modèle OSI Messages Segments 7 Application 6 Présentation 5 Session Datagrammes 4 Transport Paquets 3 Réseau Trames 2 Liaison de Données Bits 1 Physique Description des couches Physique -> Brochage, Tensions... Liaison de Données -> Structuration, Correction, Accès au medium... Réseau -> Routage, Fragmentation. Communication entre systèmes Transport -> Communication entre processus Session -> Connexion, Déconnexion. Présentation -> Structuration des données typées. Application -> Communication entre "utilisateurs "
Structure des réseaux Les canaux de communication Il existe deux types de canaux de communication: canaux point à point canaux de diffusion Les topologies réseau; Bus, Annaux, Etoile,….
LE MODELE CLIENT/SERVEUR Problème de rendez-vous entre processus. Impossible de synchroniser des processus afin d'obtenir une connexion symétrique. Un processus commence donc son exécution et attend une connexion provenant d'un autre processus Serveur : processus en attente de connexion (daemon) Client : processus initiant la communication
TCP/IP
Exemple
Notion de port Connexion réseau Pourquoi les ports ? Adresse internet de la machine Numéro du port Pourquoi les ports ? Sur une même machine, plusieurs services sont accessibles simultanément (web, email, etc.) Points d’accès : ports logiques (65535) Rien à avoir avec les ports physiques (série et parallèle)
Port d’écoute 16-bits unsigned integer, Ports utilisateur >= 1024. de 1 à 65535. Ports utilisateur >= 1024. Exples de ports réservés, FTP, 21/TCP Telnet, 23/TCP SMTP, 25/TCP Login, 513/TCP HTTP, 80/TCP
Adresse IP Tient sur 32 bits (IPv4) un quadruplet de nombres de 8 bits chaque, séparés par des points. Exple: 127.0.0.1 ICANN –Internet Corporation for Assigned Names and Numbers attribue les adresses IP du réseau public internet @IP 127.0.0.1 adresse de rebouclage (ang. loopback) désigne la machine locale (ang. localhost) 5 classes Classe A: de 1.0.0.1 à 126.255.255.254 127 réseaux de plus que 16 millions d’ordinateurs chaque Réservées au grands réseaux 1er octet réseau (NetID), 3 octets pour l’@ de l’hôte (hostID)
…Adresse IP IPv6 Classe B: de 128.0.0.1 à 191.255.255.254 16575 réseaux de plus que 6500 ordinateurs chaque Réservées aux moyens réseaux les deux 1ers octets réseau, 2 octets suivants pour l’@ de l’hôte Classe C: de 192.0.0.1 à 223.255.255.254 + que 2 millions de réseaux de 254 ordinateurs chaque Réservées au petits réseaux d’entreprise les trois 1ers octets réseau, 4éme octet pour l’@ de l’hôte Classe D: de 224.0.0.1 à 239.255.255.255 groupes multicast Classe E réservées pour futur usage IPv6 Résout le problème de pénurie d’adresses IP @IP sur 128 bits
Socket Communication Un programme Serveur s’exécute sur une machine (@IP) et écoute sur un port d’écoute. (ang. Listening Port) Un programme Client s’exécute sur une machine (@IP) et envoie sur un port d’envoi vers le programme Serveur, sachant son port d’écoute et son @IP. Serveur Client port protocole
Internet Protocol Le protocole IP subdivise un tampon de données en paquets de taille max de 65535 octets, et dont 20 octets constituent l’entête. Chaque paquet est individuellement routé de source à destination sans garantie de livraison. Application TCP (HTTP, FTP, Telnet,…) Application UDP (DNS, NFS…) Transport (TCP, UDP,..) Réseau (IP,..) Liaison
API Réseau de Java Classes et Interfaces du paquetage java.net Adresses IP InetAddress Sockets TCP Socket ServerSocket Sockets UDP DatagramSocket DatagramPacket Sockets Multicast MulticastSocket
Les adresses Internet : java.net.InetAddress Il n'existe pas de constructeur. Pour obtenir une instance de cette classe, il est nécessaire d'utiliser des méthodes de classe. Ces méthodes de classe sont les suivantes : public static InetAddress getLocalHost() throws UnknownHostExeception Retourne un objet contenant l'adresse Internet de la machine locale. public static synchronized InetAddress getByName(String host_name) throws UnknownHostExeception Retourne un objet contenant l'adressse Internet de la machine dont le nom est passé en paramètre. public static synchronized InetAddress[] getAllByName(String host_name) throws UnknownHostExeception Retourne un tableau d'objets contenant l'ensemble des adressses Internet de la machine qui répond au nom passé en paramètre.
Les adresses Internet Les méthodes que l'on peut appliquer à un objet de cette classe sont : public String getHostName () Retourne le nom de la machine dont l'adresse est stockée dans l'objet. public byte[] getAddress () Retourne l'adresse internet stockée dans l'objet sous forme d'un tableau de 4 octets dans l'ordre réseau. public String toString () Retourne une chaîne de caractères qui liste le nom de la machine et son adresse.
Identifier la machine locale import java.net.*; public class localhost{ public static void main (String arg[]){ try{ InetAddress adresse=InetAddress.getLocalHost(); byte[] IP=adresse.getAddress(); System.out.print("IP="); int i; for(i=0;i<IP.length-1;i++) System.out.print(IP[i]+"."); System.out.println(IP[i]); System.out.println("adresse="+adresse.getHostAddress()); System.out.println("nom="+adresse.getHostName()); System.out.println("identité="+adresse); } catch (UnknownHostException e){ System.out.println ("Erreur getLocalHost : "+e); }// fin try }// fin main }// fin class
Identifier une machine quelconque import java.net.*; public class getbyname{ public static void main (String arg[]){ String nomMachine; // on récupère l'argument if(arg.length==0) nomMachine="localhost"; else nomMachine=arg[0]; // on tente d'obtenir l'adresse de la machine try{ InetAddress adresse=InetAddress.getByName(nomMachine); System.out.println("IP : "+ adresse.getHostAddress()); System.out.println("nom : "+ adresse.getHostName()); System.out.println("identité : "+ adresse); } catch (UnknownHostException e){ System.out.println ("Erreur getByName : "+e); }// fin try }// fin main }// fin class
UDP -User Datagram Protocol Sockets en mode non connecté Protocole rapide, Mais non fiable Contrôle d’erreurs sans reprise si erreur, pas d’acquittement, séquencement de paquets non garanti. Taille max paquet = 65507 octets paquet IP 65535 octets – 20 octets entête IP – 8 octets entête UDP Requis @IP du destinataire Port d’écoute du destinataire
…UDP La fiabilité peut être assurée au niveau applicatif, par l’ajout de Numéro de séquence aux paquets Heure et date Re-envoi après expiration Time-out Pour utiliser le protocole de transport UDP, il est nécessaire en Java de manipuler deux classes DatagramPacket et DatagramSocket
La classe DatagramPacket Cette classe permet de créer des objets qui contiendront les données envoyées ou reçues ainsi que l'adresse de destination ou de provenance du datagramme.
La classe DatagramPacket Deux constructeurs sont disponibles, un pour les paquets à recevoir, l'autre pour les paquets à envoyer. public DatagramPacket(byte buffer[], int taille) Construit un objet pour recevoir un datagramme. Le paramètre buffer correspond à la zone où doit être stocké le datagramme reçu et le paramètre taille correspond à la taille maximale des datagrammes à recevoir. public DatagramPacket(byte buffer[], int taille, InetAddress adresse, int port) Construit un objet pour envoyer un datagramme. Le paramètre buffer correspond à la zone où est stocké le datagramme à envoyer, le paramètre taille correspond à la taille du datagramme à envoyer, adresse correspond à l'adresse de la machine à qui envoyer le datagramme et port sur quel port UDP.
Les méthodes public synchronized InetAddress getAddress () Retourne l'adresse stockée dans le paquet. public synchronized int getPort () Retourne le port stocké dans le paquet. public synchronized byte[] getData () Retourne les données stockées dans le paquet. public synchronized int getLength () Retourne la taille des données stockées dans le paquet.
Les méthodes public synchronized void setAddress(InetAddress iaddr) Modifie ou affecte l'adresse de destination. public synchronized void setPort(int iport) Modifie ou affecte le port de destination. public synchronized void setData(byte ibuf[]) Modifie ou affecte la référence de la zone contenant les données. public synchronized void setLength(int ilength) Modifie ou affecte la taille de la zone contenant les données.
La classe DatagramSocket Cette classe permet de créer des sockets UDP qui permettent d'envoyer et de recevoir des datagrammes UDP. Avant toute communication en mode UDP il est nécessaire de créer une socket aussi bien du coté client que du coté serveur. Pour cela Java propose trois constructeurs
La classe DatagramSocket public DatagramSocket () throws SocketException Crée un objet de type socket et l'attache à un port disponible de la machine locale. Ce constructeur doit être utilisé dans les clients pour lesquels le port d'attachement n'a pas besoin d'être connu. public DatagramSocket (int port) throws SocketException Crée un objet de type socket et l'attache au port UDP local passé en paramètre. En particulier, ce constructeur doit être utilisé dans les serveurs pour lesquels le port d'attachement a besoin d'être fixé préalablement afin qu'il soit connu des clients. public DatagramSocket(int port, InetAddress laddr) throws SocketException Crée un objet de type socket, l'attache au port UDP local passé en paramètre et à une adresse spécifique de la machine locale. Ce constructeur n'est utile que si la machine locale dispose de plusieurs adresses Internet.
Les méthodes public void send(DatagramPacket data) throws IOException Permet d'envoyer les données contenues dans la variable data vers la machine et le port dont les valeurs ont été préalablement spécifiées dans la variable data. public synchronized void receive(DatagramPacket data) throws IOException Permet de recevoir un datagramme qui sera stocké dans data. Après appel, data contient les données reçues, leur taille, l'adresse de l'envoyeur ainsi que son port d'attachement. Cette méthode est bloquante tant qu'il n'y a rien à recevoir. Si le message est trop long pour être stocké, celui-ci est tronqué, et le reste est perdu. Il n'est donc pas possible de recevoir des messages dont on ne connait pas préalablement la taille.
Les méthodes Il est possible de spécifier un délai d'attente maximal en reception. Pour cela, il faut positionner une variable de timeout sur la socket au moyen de la méthode : public synchronized void setSoTimeout(int timeout) throws SocketException Une valeur de 0 (zéro) correspond à ne pas avoir de timeout. D'autres méthodes sont disponibles pour manipuler ces sockets : public void close () Ferme la socket et libère les ressources qui lui sont associées. La socket ne pourra plus être utilisée ni pour envoyer, ni pour recevoir des datagrammes. public int getLocalPort () Retourne le port d'attachement de la socket. public synchronized int getSoTimeout() throws SocketException Retourne la valeur courante du timeout associé à la socket.
Exemple ….UDP Dans cet exemple nous créons deux classes : une classe ServeurEcho qui attend une chaîne de caractères et la retourne une classe ClientEcho qui envoye une chaîne de caractères, attend que le serveur la lui retourne et l'affiche.
Le serveur import java.io.*; import java.net.*; class ServeurEcho { final static int port = 8532; final static int taille = 1024; final static byte buffer[] = new byte[taille]; public static void main(String argv[]) throws Exception { DatagramSocket socket = new DatagramSocket(port); while(true) { DatagramPacket data = new DatagramPacket(buffer,buffer.length); socket.receive(data); System.out.println(data.getAddress()); socket.send(data); }//fin while }
Le client import java.io.*; import java.net.*; public class ClientEcho { final static int taille = 1024; final static byte buffer[] = new byte[taille]; public static void main(String argv[]) throws Exception { InetAddress serveur = InetAddress.getByName(argv[0]); int length = argv[1].length(); byte buffer[] = argv[1].getBytes(); DatagramPacket dataSent = new DatagramPacket(buffer,length,serveur,ServeurEcho.port); DatagramSocket socket = new DatagramSocket(); socket.send(dataSent); DatagramPacket dataRecieved = new DatagramPacket(new byte[length],length); socket.receive(dataRecieved); System.out.println("Data recieved : " + new String(dataRecieved.getData())); System.out.println("From : " + dataRecieved.getAddress() + ":" + dataRecieved.getPort()); } }
TP2 Ecrire deux programme (sender et recived) tq: Le sender envoi des messages au recived sur un port donner en argument Le recived reçoit les message et les affiche
Les sockets en mode connecté
Les sockets en mode connecté java.net.Socket et java.net.ServerSocket Représente une connexion fiable TCP/IP entre 2 processus (qui peuvent ne être des JVM !) la connexion est fiable (contrôle d ’erreur, … ) et 2 flots de données sont établis entre les deux machines La connexion est asymétrique Une machine est serveur : classe ServerSocket elle attend les demandes de connexion et les accepte Une machine est client : classe Socket elle demande l ’établissement de la connexion avec le serveur Terminologie La connexion est dite en mode connecté
La classe Socket La classe Socket est utilisée par les clients TCP. Pour créer un objet de la classe Socket, il est nécessaire d'utiliser un des constructeurs suivant : public Socket (String machine, int port) throws UnknownHostException, IOException public Socket (InetAddress adresse, int port) throws UnknownHostException, IOException public Socket(String machine, int port, InetAddress local, int portLocal) throws IOException public Socket(InetAddress adresse, int port, InetAddress local, int portLocal) throws IOException
La classe Socket La création d'un objet Socket entraîne la création d'un point de connexion (la socket) et la connexion vers une autre socket (le serveur). L'adresse du serveur est composée de l'adresse d'une machine (sous forme d'un nom ou d'un objet de la classe InetAddress) et d'un numéro de port. L'adresse locale et le port local peuvent également être spécifiés. Par défaut, l'appel au constructeur est bloquant tant que la connexion TCP n'est pas établie.
Lire et Ecrire Une fois la connexion établie, il est possible de récupérer le flux d'entrée et le flux de sortie de la connexion TCP vers le serveur au moyen des méthodes : public InputStream getInputStream () throws IOException public OutputStream getOutputStream () throws IOException Il est alors possible d'echanger des données avec le serveur au moyen de toutes les primitives de lecture et d'écriture des différentes classes du package java.io.
Lire et Ecrire Par défaut les primitives de lecture, tel que read(), sont bloquantes tant que rien n'est lisible sur le flux. La primitive suivante permet de définir un temps maximal d'attente : public synchronized void setSoTimeout(int timeout) throws SocketException
Fermer la connexion Une fois la connexion terminée il est très important de fermer le flux d'entrée et le flux de sortie, mais aussi la socket au moyen de la méthode : public synchronized void close() throws IOException Par défaut cette primitive n'est pas bloquante mais la socket reste ouverte tant qu'il reste des paquets à envoyer (en particulier le datagramme FIN).
Fermer la connexion La primitive suivante permet de définir un temps maximum d'attente avant de vraiment fermer la socket. public void setSoLinger(boolean on, int val) throws SocketException Par défaut, les implémentations du protocole TCP essaye de remplir au maximum chaque paquet (Nagle algorithm) afin de diminuer le trafic réseau. Pour éviter ce comportement, ce qui est souvent souhaitable dans le cas d'application interactives¸ une primitive est disponible : public void setTcpNoDelay(boolean on) throws SocketException
Et aussi D'autres méthodes sont également disponibles et en particulier : public InetAddress getInetAddress () public int getPort () public InetAddress getLocalAddress () public int getLocalPort () int getLocalPort () public int getSoLinger() throws SocketException public synchronized int getSoTimeout() throws SocketException public boolean getTcpNoDelay() throws SocketException
import java. io. ; import java. net import java.io.*; import java.net.*; public class SmtpClient { public static void main(String argv[]) throws Exception { sendmail("aaa","roussel@univ-mlv.fr"); } static void sendmail(String message, String to) throws Exception { Socket s = new Socket(InetAddress.getByName("pixel.univ- mlv.fr"),25); PrintStream output = new PrintStream(s.getOutputStream()); output.println("HELO qqcvd.univ-mlv.fr\r"); output.println("MAIL FROM: \r"); output.println("RCPT TO:<" + to + ">\r"); output.println("DATA\r"); output.println(message); output.println("\r\n.\r"); } }
La classe ServerSocket Cette classe est utilisée (comme son nom l'indique) pour créer une socket du coté serveur. La classe ServerSocket permet de créer un point de communication, sur un port particulier, en attente de connexions en provenance de clients. Contrairement à la classe Socket elle n'ouvre pas de connexion.
Constructeurs Pour créer un tel objet, il faut utiliser un des constructeurs suivants, où port correspond au port d'attente et count au nombre maximum de connexions en attente, non encore acceptées. public ServerSocket(int port) throws IOException public ServerSocket(int port, int count)throws IOException public ServerSocket(int port,int count, InetAddress locale) throws IOException
Connexion Une fois la socket créée, on attend les connexions de clients avec la méthode bloquante : public Socket accept() throws IOException Il est possible de spécifier un temps maximal d'attente. Pour cela il faut appeler la méthode suivante avant l'appel à accept() : public synchronized void setSoTimeout(int timeout) throws SocketException
Connexion La méthode accept() retourne un nouvel objet de la classe Socket qui est connecté avec un client particulier, celui qui a demandé la connexion. Il est alors possible de récupérer le flôt d'entrée et de sortie comme pour la socket du client
Exemple public class DaytimeServeur { public static void main(String argv[]) throws Exception { ServerSocket s = new ServerSocket(0); System.out.println(s.getLocalPort()); while(true) { Socket serviceSocket = s.accept(); PrintStream output = new PrintStream(serviceSocket.getOutputStream()); output.println(new Date()); serviceSocket.close(); } } }
Et aussi D'autres méthodes de manipulation sont également disponibles : public void close() throws IOException public int getLocalPort() public InetAddress getInetAddress() public synchronized int getSoTimeout() throws IOException
Exemple - code client import java.net.*; import java.io.*; public class EchoClient { public static void main(String[] args) { Socket theSocket; DataInputStream theInputStream; DataInputStream userInput; PrintStream theOutputStream; String theLine; try { theSocket = new Socket(args[0], Integer.parseInt(args[1])); theInputStream = new DataInputStream(theSocket.getInputStream()); theOutputStream = new PrintStream(theSocket.getOutputStream()); userInput = new DataInputStream(System.in); while (true) { theLine = userInput.readLine(); if (theLine.equals(".")) break; theOutputStream.println(theLine); System.out.println(theInputStream.readLine()); } } catch (UnknownHostException e) { System.err.println(e); } catch (IOException e) { System.err.println(e); } } }
Code serveur import java.net.*; import java.io.*; public class EchoServer { void doService(Socket clientSocket) { DataInputStream in = new DataInputStream(clientSocket.getInputStream()); PrintStream out = new PrintStream(clientSocket.getOutputStream()); while (true) {String theLine=in.readLine(); out.println(theLine); } } public static void main(String[] args) { ServerSocket listenSocket; try { listenSocket = new ServerSocket(Integer.parseInt(args[0])); // port while(true) { Socket clientSocket = listenSocket.accept(); System.err.println("Connexion from:" + clientSocket.getInetAddress()); doService(clientSocket); } catch (Exception e) { System.err.println(e); }
La classe Socket
Ouverture d'une connexion avec une machine Serveur Le constructeur: public Socket(String host, int port); crée une socket et la connecte à la machine host sur le port port. Ce constructeur génère une exception dans différents cas : - mauvaise adresse - mauvais port - demande refusée - … Il nous faut gérer cette exception : Socket sClient=null; try{ sClient=new Socket(host,port); } catch(Exception e){ // la connexion a échoué - on traite l'erreur …. }
Ouverture d'une connexion Si la demande de connexion réussit, le client se voit localement attribuer un port pour communiquer avec la machine B. Une fois la connexion établie, on peut connaître ce port avec la méthode : public int getLocalPort(); Si la connexion réussit, nous avons vu que, de son côté, le serveur fait assurer le service par une autre tâche travaillant sur un port dit de service. Ce numéro de port peut être connu avec la méthode : public int getPort();
Envoyer des informations sur le réseau On peut obtenir un flux d'écriture sur la socket et donc sur le réseau avec la méthode : public OutputStream getOutputStream(); De nombreuses applications ont un dialogue sous forme de lignes de texte terminées par un passage à a ligne. Aussi la méthode println est-elle bien pratique dans ces cas là. On transforme alors le flux de sortie OutputStream en flux PrintWriter qui possède la méthode println. L'écriture peut générer une exception
Fermeture de la connexion Elle se fait avec la méthode : public void close(); La méthode peut générer une exception. Les ressources utilisées, notamment le port réseau, sont libérées.
Lire des informations venant du réseau On peut obtenir un flux de lecture des informations arrivant sur la socket avec la méthode : public InputStream getInputStream(); Pour les applications ayant un dialogue sous forme de lignes de texte terminées par un passage à la ligne on aimera utiliser la méthode readLine. Pour cela on transforme le flux d'entrée InputStream en flux BufferedReader qui possède la méthode readLine(). La lecture peut générer une exception
Architecture d'un client L'architecture d'un programme réseau demandant les services d'une application serveur sera la suivante : ouvrir la connexion avec le service SB1 de la machine B si réussite alors tant que ce n'est pas fini préparer une demande l'émettre vers la machine B attendre et récupérer la réponse la traiter fin tant que finsi
Architecture d'un serveur L'architecture d'un programme offrant des services sera la suivante : ouvrir le service sur la machine locale tant que le service est ouvert se mettre à l'écoute des demandes de connexion sur un port dit port d'écoute lorsqu'il y a une demande, la faire traiter par une autre tâche sur un autre port dit port de service fin tant que Une tâche de service aura la structure suivante : tant que le service n'a pas été rendu totalement attendre une demande sur le port de service lorsqu'il y en a une, élaborer la réponse transmettre la réponse via le port de service fin tant que libérer le port de service