Introduction à la
Programmation réseau avec Java

Dans ce tutorial nous verrons la programmation réseau avec Java. Ne vous inquiètez pas si vous n'êtes pas familier avec la programmation réseau, ce tutorial n'est qu'une brève introduction. Nous examinerons quelques unes des classes du package java.net, et verrons comment écrire une simple programme client en Java. Mais tout d'abord, nous avons besoin de connaître un peu de théorie.

Comment les ordinateurs communiquent-t-ils sur Internet ?

L'Internet est composé de millions d'ordinateurs, situés sur toute la planète, qui communiquent transmettent des informations à travers un grand nombre d'ordinateurs, plateformes et équipements de réseau différents. Chacun de ces ordinateurs, à moins qu'il soit connecté via un intranet, dispose d'une adresse IP unique.

Les adresses IP sont des nombres de 32-bits, composés de 4 octets (nombres de 8 bits) séparés par un octet de stop. Chaque ordinateur directement relié à Internet doit avoir unr adresse IP unique (par exemple 207.68.156.61). Certains ordinateurs ont une adresse IP¨temporaire, comme ceux qui se connectent chez un FAI avec un modem. D'autres ont une adresse IP permanente et certains ont même leur propre nom de domaine (par exemple www.microsoft.com).

Une adresse IP nous permet d'identifier à coup sur un périphérique ou un système connecté à Internet. An IP address allows us to uniquely identify a device or system connected to the Internet.Il est ainsi possible de se connecter à une adresse IP spécifique et envoyer un message. Sans adresse IP, le message n'aurait aucun moyen d'atteindre sa destination, un peu comme si on ne mettait pas d'adresse sur une enveloppe classique.

Souvent les ordinateurs connectés à l'Internet fournissent des services. Cette page est fournies par un serveur web par exemple. Comme les ordinateurs sont capables de fournir plus d'un type de service, on a besoin d'un moyen pour identifier chaque services séparemment. Comme pour les adresses IP on utilise un nombre. On appelle ce nombre un port. Les services les plus courants (comme HTTP, FTP, Telnet, SMTP) ont ds numéros de ports définis. Par exemple la plupart des serveurs webs utilisent le port 80. Bien sur, vous pouvez utiliser n'importe quel port si vous le souhaitez : aucun règle ne dit que vous devez utiliser le port 80.

Il y a plusieurs techniques de communications utilisables pour fournir un service réseau. Il est possible d'utiliser UDP (unreliable datagram protocol), ou TCP (transfer-control protocol). Pour les besoins de ce tutorila on choisit TCP, car il est plus facile à programmer. TCP garantit que les messages sont bien arrivés à destination. UDP n'est pas fiable, et votre application n'est pas prévenue si un message est perdu durant le transport. Ainsi, beaucoup de protocoles (comme HTTP, SMTP, POP & FTP) utilisent TCP, c'est pourquoi il est important de se familiariser avec pour faire de la programmation réseau avec Java.

Adressage Internet avec Java

La gestion des adresses internet (noms de domaines, et adresses IP) devient facile avec Java. Les adresses Internet sont représentées en Java avec la classe InetAddress. InetAddress fournit des méthodes simples pour la conversion entre noms de domaines et adresses numériques.

On commence par importer le package java.net, qui contient un jeu de routines orientées réseaux prédéfinies (dont InetAddress).

import java.net.*;

Ensuite, on déclare une nouvelle variable de type InetAddress, à laquelle on affecte l'adresse IP de la machine locale (pour les machines non reliées à un réseau cela représente 127.0.0.1). Comme InetAddresses peut générer des exceptions, on doit mettre ce code dans un bloc try .. catch UnknownHostException.

// Obtenir l'adresse de l'odinateur sur lequel le programme est exécuté
InetAddress localaddr = InetAddress.getLocalHost();

La classe InetAddress dispose de méthodes pour renvoyer l'adresse IP sous forme d'un tableau d'octets (qui peut aisément être converti en chaîne), ainsi que sous forme d'une représentation en chaîne de son nom de domaine (par exemple mydomain.org ). On peut écrire l'InternetAddress, ainsi que le nom de domaine de l'adresse locale.

System.out.println ("Local IP Address : " + localaddr );
System.out.println ("Local hostname : " + localaddr.getHostName());

Télécharger le code source

public class MyFirstInternetAddress
{
	public static void main(String args[])
	{
		try
		{
			InetAddress localaddr = InetAddress.getLocalHost();
			
			System.out.println ("Local IP Address : " + localaddr );
			System.out.println ("Local hostname   : " + localaddr.getHostName());
		}
		catch (UnknownHostException e)
		{
			System.err.println ("Can't detect localhost : " + e);
		}
		
	}

	/** Convertit un tableau d'octets en chaine */
	public static String byteToStr( byte[] byte_arr )
	{
		StringBuffer internal_buffer = new StringBuffer();

		// Boucler, et ajouter les octets à l'adresse IP
		for (int index = 0; index < byte_arr.length -1; index++)
		{
			internal_buffer.append ( String.valueOf(byte_arr[index]) + ".");
		}
		
		// Ajouter le dernier octet, sans '.'
		internal_buffer.append ( String.valueOf (byte_arr.length) );

		return internal_buffer.toString();
	}
}

Compiler et exécuter cette aplication, vous devriez obtenir votre adresse IP et votre nom de machine. Ne vous inquiètez pas si l'ordinateur n'est pas connecté à l'Internet. Tant que votre système à une pile TCP il doit vous retourner une adresse IP même si vous n'êtes pas actuellement connecté. Sur la plupart des systèmes, vous pouvez vous référer à la machine locale (qui porte souvent le nom "localhost") à l'adresse IP 127.0.0.1

Commet se fait-il que les machines non connectées à l'Internet puissent avoir la même adresse ? Cette adresse est connue sous le nom d'adresse de bouclage (loopback adress). chaque fois que vous vous connectez à cette adresse, vous vous connectez à la machine locale. Ainsi si vous exécuter un serveur web local, et que vous allez avec votre navigateur à l'adresse http://127.0.0.1, vous devriez voir votre site web. Si une autre personne va à cette adresse, elle sera dirigée à un endroit différent, sur le serveur web de sa propre machine.

cela est pratique pour le développement d'applications Java. vous n'avez pas besoin d'une connection permanente à l'Internet, vous pouvez faire exécuter les applications clientes et serveur sur votre propre machine. Cela est pratique car l'écriture et le test de telles applications peut prendre un peu de temps, et à moins que vous n'ayez une connection Internet permanente vous n'aimeriez pas payer la note à votre FAI !

Ecrire une client TCP en Java

Ecrire un client en Java est très simple. Si vous avez déjà écrit un client entier en C, vous pouvez imaginer à quel point cela est compliqué. Il faut se soucier des structures, des pointeurs... Java tranche avec cette complexité. grâce à l'utilisation de la classe java.net.Socket. Pour illuster comme cela est facile avec Java, nous allons voir comment faire un client finger.

Pour ceux qui ne seraient pas familier avec le protocole finger, voici un petit rappel sur ce protocole. Finger permet à un utilisateur distant de demander des informations à une machine hôte, soit à propos de la machine en général, soit à propos d'un utilisateur spécifique. La plupart des systèmes Unix supportent finger, et beaucoup de systèmes non-Unix le supportent aussi. La plupart des applications finger prend un argument de la forme 'nom_utilisateur@nom_machine'.

Les clients finger se connectent au serveur sur le port 79 et établissent un flux TCP. Le client envoit son nom d'utilisateur (ou un vide pour des information générales), suivi d'un caractère de fin de ligne. Le serveur envoit alors les informations sur l'utilisateur sous la forme d'un flux de texte. Celui-ci est affiché à l'utilisateur et la connection est temrinée.

comme pour n'importe quelle application réseau en Java, nous devons d'abord importer les paquetages de réseaux et d'entrées-sorties.

import java.io.*;
import java.net.*;

Notre application aura une seule méthode, main, qui sera responsable de l'envoi de la requête finger. La premièere étape est de valider et interpréter les paramètres de la ligne de commande, à la recherche d'une nom d'utilisateur et d'un nom de machine.

public static void main ( String args[] )
{
	// Verifier les parametres de la ligne de commande
	if (args.length != 1)
	{
		System.err.println ("Parametres invalides");
		System.exit(1);
	}
	else
	// Verifier l'existance d'un @ dans les parametres
	if (args[0].indexOf("@") == -1)
	{
		System.err.println ("Parametres invalide : syntaxe utilisateur@machine");
		System.exit(1);
	}

	// Diviser les parametres de la ligne de commande au niveau du @
	String username = args[0].substring(0, args[0].indexOf("@") );
	String hostname = args[0].substring(args[0].indexOf("@") +1, args[0].length());

	........
}

Dans le code précédent, nous avons vérifié qu'un seul paramètre a été saisi et aussi qu'il existe un '@' dans le paramètre. Ensuite, il faut séparer le paramètre pour extraire le nom d'utilisateur et le nom de la machine. Pour cela, on s'appuit sur la méthode substring, qui peut être appliquée à n'importe quelle chaîne. Le nom d'utilisateur commence au rang 0, jusqu'au rang du caractère '@'. La nom de la machine est la chaîne située à partir du rang du caractére '@', jusqu'à la longueur du paramètre original.

L'étape suivante est de se connecter au serveur finger, qui éctoue sur le port 79. Comme dans l'xemple précédent, on doit insérer le code réseau à l'intérieur d'un bloc try { ... } catch. Cela nous permet d'intercepter n'importe quelle erreur réseau (comme des noms de machines invalides, ou l'impossibilité de se connecter au serveur). Vous noterez que le code pour se connecter tient en une ligne - la programmation réseau en Java est vraiment très facile.

try
{
	// Créer une connection au serveur
	Socket s = new Socket(hostname, 79);


	// Le reste du code du client finger vient ici
	.........

}
catch (SocketException e )
{
	System.err.println ("Erreur de socket : " + e);
}
catch (UnknownHostException e )
{
	System.err.println ("Hote invalide !");
}
catch (IOException e )
{
	System.err.println ("Erreur d'E/S : " + e);
}

Après s'être connecté sur le port 79 du serveur finger, on peut obtenir les flux d'entrées et de sortie de la socket. On peut traiter ces flux de la même manière qu'un fichier ou des entrées/sorties texte. Pour se simplifier les opérations, nous convetissons ensuite le flux d'entrée en DataInputStream, et le flux de sortie en PrintStream. Cela nous permet d'utiliser les méthodes readLine et println sur les flux d'entrée/sortie de la socket.

// Créer les flux d'entree et de sortie de la socket
PrintStream out = new PrintStream( s.getOutputStream()) ;
DataInputStream in = new DataInputStream(s.getInputStream());

En utilisant la PrintStream en sortie, nous écrivons le nom de l'utilisateur sur lequel nous souhaitons obtenir les informations du serveur finger. Le serveur traite notre requête et renvoit le résultat, que l'on affiche sur l'écran de l'utilisateur.

// Ecrire le nom d'utilisateur sur la socket en sortie
out.println( username );

// Lire la reponse sur la socket
System.out.println ( in.readLine() );

// Lire le reste de la reponse finger
String line = in.readLine();

while (line != null)
{
	System.out.println ( line );

	// Lire la ligne suivante
	line = in.readLine();
}

La premièere ligne est lue du flux d'entrée et ensuite affichée à l'écran. Puis on entre dans la boucle, en vérifiant qu'il reste des lignes à afficher. La boucle while se termine lorsqu'il n(y a plus d'octets à lire.

Finalement, on doit dermer la connection au serveur finger. Avec cette dernière instrucation, le client finger est complet !

// Fermer la connection
s.close();

bien que le programme d'exemple fonctionne comme un client finger, il peut aisément être utilisé comme squelette de programme client TCP, en modifiant le port 79 par un autre port correspondant au portocole applicatif que vous souhaitez utiliser, et en modifiant les routines d'netrée/sorite pour envoyer des données différentes.

Exécution de l'exemple

Télécharger le code source

Pour exécuter le client, vous avez besoin d'être connecté à Internet (cet exemple requiert une connection directe et ne fonctionne pas derrière un firewall). Pour l'exécuter, vous avez besoin du nom d'un utilisateur, et du site sur lequel cet utilisateur réside. Par exemple, pour trouver l'utilisateur dod sur l'ordinateur fan.net.au (dodo@fan.net.au), vous feriez :

java TCP_Finger_Client dodo@fan.net.au

Note : Tous les FAI ne fournissent pas de serveur finger, il est possible que vous ne peuissiez pas trouver certains utilisateurs. L'exemple ci-dessus semble fonctionner (tant que mon FAI n'a pas modifié sa politique de sécrutié)

Résumé

L'écriture d'application réseaux en Java est très simple. Java prend soin des détails d'implèmentation spécifiques à chaque système d'exploitation. Il n'y a pas besoin de se tracasser à propos de la résolution des noms de machines, la taille des structures d'adresse IP ou des ointeurs. La paquetage java.net fournit toutes ces fonctionnalités pour vous, et une manièere simple d'écrire des clients réseaux. Cependant, lors de l'écriture d'applets Java (à la différence des applications), le navigateur peut imposer certaines restrictions, la plupart des applets ne peuvent par exemple communiquer qu'avec l'hôte à partir duquel elles ont été téléchargées.

Ressources additionnelles

Merlin Hughes, et al. Java Network Programming, Manning Publications, 1997.

Reilly, D. Java Network Programming FAQ (en ligne) disponible à http://www.davidreilly.com/java/java_network_programming/

Ce tutorial est une traduction de l'article en anglais disponible sur le site http://www.javacoffeebreak.com/java109/java109.html