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