Programmation réseau en Java
Transcription
Programmation réseau en Java
Programmation réseau en Java Programmation réseau en Java 0 – Introduction Les réseaux sont devenus une partie intégrale de notre vie quotidienne que nous soyons des professionnels, des employés ou tout simplement des utilisateurs d’Internet. Tout langage de programmation qui se respecte devra en quelque sorte offrir au programmeur une interface de programmation réseau. Java ne fait pas exception. En effet un paquetage spécial (java.net) est fourni pour faciliter la programmation réseau. Tous ceux qui ont travaillé avec la programmation réseau en langage C (ou d’autres langages de « bas-niveau ») constateront que Java fournit une interface très intuitive et facile à comprendre en ce qui concerne la programmation réseau. Dans ce chapitre nous illustrerons les principaux concepts de la programmation réseau dans Java. Dans une première section nous parlerons de la transmission de données en mode datagramme. Dans une seconde section nous parlerons de la transmission en mode connecté. 1 – Mode datagramme Dans ce chapitre nous assumons que le lecteur est familier avec les principes des protocoles TCP et UDP notamment la différence entre mode datagramme et mode connecté. Dans certains cas de programmation réseau il est uniquement nécessaire d’envoyer des données d’un ordinateur à un autre sans avoir à se soucier de la bonne réception de ces données par le destinataire. Parfois les contraintes temps-réel d’une certaine application exigent l’emploi d’un tel mode de fonctionnement. Dans de tels cas il suffit de grouper les données dans une entité qu’on appelle datagramme et qu’on envoi sur le réseau. Un datagramme peut être définit comme étant un message qu’on envoie sur le réseau et dont l’arrivée, le temps d’arrivée et le contenu ne sont pas garantis. Dans l’Internet le protocole UDP (User Datagram Protocol) assure cette fonctionnalité. Le paquetage java.net fourni deux classes pour faciliter la programmation réseau en mode datagramme : DatagramPacket et DatagramSocket. Les données sont mises dans un objet de type DatagramPacket qui est envoyé sur le réseau par le biais d’un objet de type DatagramSocket. Avant d’aborder en détail la programmation en mode datagramme, nous allons introduire la classe InetAddress et les services qu’elle nous fournit. Cette classe met à notre disposition un ensemble de méthodes permettant la résolution des noms de machines en adresses IP et vice versa. © 2001 Toni Soueid 1 Programmation réseau en Java Nous allons montrer certaines de ces méthodes par un exemple pratique. exemple 1 Dans cet exemple nous fournissons au programme sur la ligne de commande comme argument le nom d’une machine. Le programme se chargera alors de trouver l’adresse IP correspondant à ce nom de machine. import java.net.* ; import java.io.* ; public class ResoudreNom { public static void main(String[] args) { InetAddress adresse ; try { adresse=InetAddress.getByName(args[0]) System.out.println(args[0]+adresse.getHostAddress()) ; } catch(UnknownHostException e) { } } } Deux méthodes de la classe InetAddress sont illustrées dans cet exemple : getByName() et getHostName(). La première permet de résoudre l’adresse IP d’une machine étant donné son nom. La seconde permet de retourner sous forme de chaîne de caractères l’adresse IP mémorisé dans l’objet de type InetAddress. Ainsi dans cet exemple nous trouvons l’adresse IP d’une machine dont le nom est passé en argument, mémorisons cette adresse dans un objet de type InetAddress et enfin l’imprimons sur l’écran. La classe InetAddress fournit une multitude d’autres méthodes utiles. Nous laissons le soin au lecteur de consulter la documentation officielle. Revenons au mode datagramme proprement dit. Nous allons dans ce qui suit donner le principe de la programmation réseau en mode datagramme en Java ensuite illustrer par un exemple. Pour envoyer des datagrammes en Java nous devons suivre les étapes suivantes : • Obtenir l’adresse du destinataire et la mettre dans une instance de la classe InetAddress. • Mettre les données et l’adresse dans une instance de la classe DatagramPacket © 2001 Toni Soueid 2 Programmation réseau en Java • Créer une instance de la classe DatagramSocket et lui confier l’envoi du datagramme. Pour recevoir des datagrammes en Java nous devons suivre les étapes suivantes : • Créer une instance de la classe de la classe DatagramSocket qui devra attendre l’arrivée de données à travers le réseau. • Créer une instance de la classe DatagramPacket qui recevra données qui lui seront passées par l’instance de DatagramSocket. Passons directement à un exemple. Exemple 2 Dans cet exemple nous allons écrire deux programmes. L’un permettant d’envoyer vers l’autre des messages à travers le réseau. L’autre se chargeant alors de les afficher sur l’écran. Commençons par le programme côté émetteur. import java.net.*; import java.io.*; public class CoteEnvoi { static int port=5000; // Le port UDP de destination static InetAddress adr=null; static DatagramSocket sock=null; static String message="Salut!, mode datagramme en illustration"; static byte[] tampon=new byte[message.length()]; public static void main(String[] args) throws SocketException, IOException { if(args.length!=1) { System.out.println("Donnez comme argument le nom du destinataire"); System.exit(1); } try { adr=InetAddress.getByName(args[0]); } catch(UnknownHostException e) { System.out.println("resolution impossible"); System.exit(2); } © 2001 Toni Soueid 3 Programmation réseau en Java tampon=message.getBytes(); DatagramPacket paquet=new DatagramPacket(tampon,tampon.length,adr,port); sock=new DatagramSocket(); sock.send(paquet); } } La première chose que nous faisons est de résoudre le nom de la machine destinatrice en adresse réseau. Cette adresse sera placée dans un objet de type InetAddress. Ensuite nous transformons notre chaîne de caractères en une suite d’octets afin de les transmettre sur le réseau. N’oublions pas que le réseau ne comprend pas les caractères Unicode (ni toute autre forme d’objet ou de variable). En effet tout ce qu’un réseau comprend est une suite d’octets qui transitent. Cette transformation se fait par la méthode getBytes() de la classe String. Il nous faut ensuite construire un datagramme en indiquant l’emplacement des données à transmettre, leur longeur et enfin l’adresse et le port de destination. Enfin nous ouvrons une DatagramSocket et utilisons sa méthode send() pour envoyer notre datagramme. Passons au programme côté récepteur. import java.net.*; import java.io.*; public class CoteReception { static int port=5000; public static void main(String[] args) throws IOException, SocketException { byte[] tampon=new byte[200]; String message; DatagramSocket sock=new DatagramSocket(port); DatagramPacket paquet=new DatagramPacket(tampon,tampon.length); sock.receive(paquet); message=new String(tampon); System.out.println(message); } } Côté réception les choses sont plus faciles. Nous préparons une zone mémoire tampon pour recevoir les données. Ensuite nous créons un DatagramSocket pour écouter sur le port de destination en attente de données. L’ordre d’attente se fait par la méthode receive(). © 2001 Toni Soueid 4 Programmation réseau en Java Cette méthode se charge de placer les données dans un DatagramPacket gérant lui même notre tampon. Enfin les données sont extraites du tampon sous forme de chaîne de caractères et imprimées à l’écran. Nous avons suffisamment abordé le mode datagramme ; compliquons un peu les choses en passant au mode connecté. 2 – Le Mode Connecté Contrairement au mode datagramme, le mode connecté garanti l’arrivée en séquence des données émises en plus de la fiabilité. Dans l’Internet ce mode est assuré au niveau de la couche de transport selon le modèle OSI par le protocole TCP (Transmission Control Protocol). Un tel mode est utile lors d’un transfert de fichiers par exemple. Le paquetage java.net fourni les classes Socket et ServerSocket pour travailler avec le mode connecté. Nous allons dans la suite illustrer par un exemple l’utilisation du mode connecté en Java. Mais avant d’aborder l’exemple expliquons les étapes que doit effectuer un serveur et un client pour assurer une communication en mode connecté. Le serveur doit : • Instancier la classe ServerSocket et l’instruire à écouter sur un certain port. • • • Accepter les connexions par la méthode accept() et créer un objet Socket pour faire référence à cette nouvelle connexion. Faire passer cette nouvelle connexion au programme approprié. Lorsque le traitement est fini fermer la connexion par la méthode close(). Le client doit : • Se connecter au service approprié en instanciant la classe Socket et en lui passant comme paramètres l’adresse du serveur et le port. • Lorsque l’échange est terminé ferme la connexion par la méthode close(). Après cette explication nous passons à l’exemple. Exemple 3 Dans cet exemple nous disposons d’un programme client et d’un programme serveur. Le client se charge de générer une chaîne de caractères et de l’envoyer au serveur qui se charge alors de l’afficher à l’écran. Passons directement au code du client. import java.net.*; © 2001 Toni Soueid 5 Programmation réseau en Java import java.io.*; public class ClientConnecte { Socket sock=null; public ClientConnecte(String host, int port, String data) { try { sock=new Socket(host, port); PrintStream output=new PrintStream(sock.getOutputStream()); output.println(data); sock.close(); } catch(IOException e) { } } public static void main(String[] args) { ClientConnecte client=new ClientConnecte("127.0.0.1", 10000, "Hello"); } } Le constructeur de la classe ClientConnecte se charge d’ouvrir une connexion vers le serveur dont le nom et le numéro de port sont passés en paramètre. Pour pouvoir envoyer des données sur le réseau nous devons disposer d’un objet de type OutputStream. Cela se fait alors par la fonction getOutputStream() de la classe Socket. Lorsque les données sont envoyées la connexion est fermée par la méthode close(). Il est important de noter qu’il est impératif de traiter les exceptions qui peuvent survenir. Du côté du serveur les choses se compliquent un peu plus. Tout d’abord nous devons expliquer la théorie du travail d’un serveur pour pouvoir comprendre notre programme. En général un programme serveur se charge d’écouter sur un port spécifique les demandes de connexion effectuées par les clients. A chaque nouvelle connexion il crée alors un nouveau processus (ou processus léger) qui se charge alors de cette connexion. Le programme principal quant à lui retourne à l’écoute de nouvelles connexions. Commençons par la partie du serveur qui se charge d’écouter pour de nouvelles connexions. import java.net.*; import java.io.*; class ServeurConnecte extends Thread { ServerSocket reception; © 2001 Toni Soueid 6 Programmation réseau en Java static final int port=10000; public ServeurConnecte() { try { reception=new ServerSocket(port); } catch(IOException e) { System.exit(1); } this.start(); } public void run() { Socket sock; Traitement t; try { while(true) { sock=reception.accept(); t=new Traitement(sock); } } catch(IOException e) { } } } Un instance de la classe ServerSocket est créée permettant ainsi au serveur de s’enregistrer auprès du système d’exploitation affin d’écouter les demandes de connexion sur un port spécifique. En cas d’impossibilité le programme se termine. Puisque notre serveur est un processus léger nous le lançons par la méthode start(). Dans la méthode run() (appelée par start()), nous rentrons dans une boucle infinie en attente de nouvelles connexions. La méthode accept() assure cela. La nouvelle instance de Socket ainsi retournée est alors passée à une instance de la classe Traitement (voir ci-dessous) qui se charge alors de cette nouvelle connexion. Notre serveur retourne alors à l’état d’attente. La partie qui se charge du traitement proprement dit est donné ci-après. class Traitement extends Thread { Socket sock; BufferedReader entree; public Traitement(Socket socket) { sock=socket; © 2001 Toni Soueid 7 Programmation réseau en Java try { entree=new BufferedReader(new InputStreamReader(sock.getInputStream())); } catch(IOException e) { } this.start(); } public void run() { String text; try { text=entree.readLine(); System.out.println(text); sock.close(); } catch(IOException e) { } } } Notre classe Traitement est la classe qui se charge d’assurer le service pour le connexion acceptée par le serveur. Cette classe est un processus léger. L’objet de type Socket passé à notre constructeur nous permettra alors d’obtenir, par la méthode getInputStream(), un objet de type InputStream. Cela vas nous permettre de lire les données envoyées par le client. Enfin nous lançons notre programme par la méthode main(). public class TestServeurConnecte { public static void main(String[] args) { new ServeurConnecte(); } } 4 – Conclusion Dans ce chapitre nous avons tenté d’exposer au lecteur les principes de la programmation réseau dans Java. Il est clair que Java fournit un accès haut-niveau aux fonctionnalités de programmation réseau fournies par les systèmes d’exploitation. Java fournit aussi des classes permettant d’accéder directement aux pages Web par le biais du protocole HTTP. Nous laissons le soin au lecteur de les consulter. © 2001 Toni Soueid 8