Communication en utilisant des datagrammes                  

Communication en utilisant des datagrammes

     

L'envoi et la réception de messages par datagrammes nécessitent d'effectuer les opérations décrites ci-dessous.

Nous proposons un exemple composé de deux classes comportant chacune une méthode main.

Le programme EnvoiMessage envoie des messages par des datagrammes sur une machine dont le nom est indiqué sur la ligne de commande ; le numéro de port de la machine destinatrice sur lequel les messages seront attendus est aussi indiqué sur la ligne de commande ; ce numéro doit être compris entre 1024 et 65535. Chaque message est donné par l'entrée standard sur une seule ligne. Après l'envoi de chaque message, le programme attend un accusé de réception.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

class EnvoiMessage {
	public static void main(String[] arg) {
		int portDestinataire;
		InetAddress adresseIP;
		DatagramSocket socketUDP;
		DatagramPacket message;
		BufferedReader entree;
		String ligne;
		int longueur;
		byte[] tampon;

		try {
			socketUDP = new DatagramSocket();
			System.out.println
("Port local : " + socketUDP.getLocalPort());
			adresseIP = InetAddress.getByName(arg[0]);
			portDestinataire = Integer.parseInt(arg[1]);
			entree = new BufferedReader(new InputStreamReader(System.in));

			while(true) {
				ligne = entree.readLine();

				// on construit le paquet a envoyer
				tampon = ligne.getBytes();
				longueur = tampon.length;
				message = new DatagramPacket(tampon, longueur, adresseIP, portDestinataire);
				socketUDP.send(message);

				// on attend un accusé de réception
				tampon = new byte[256];
				message = new DatagramPacket(tampon, tampon.length);
				socketUDP.receive(message);
				ligne = new String(tampon);
				ligne = ligne.substring(0, message.getLength());
				System.out.println("Du port " + message.getPort() + " de la machine " + 
                                        message.getAddress().getHostName() + " : " + ligne);
			}
		}
		catch(ArrayIndexOutOfBoundsException exc) {
			System.out.println("Avez-vous donne le nom de la machine destinatrice et le numero de port du client ?");
		}
		catch(UnknownHostException exc) {
			System.out.println("Destinataire inconnu");
		}
		catch(SocketException exc) {
			System.out.println("Probleme d'ouverture de la socket");
		}
		catch(IOException exc) {
			System.out.println("Probleme sur la reception ou l'envoi du message");
		}
		catch(NumberFormatException exc) {
			System.out.println("Le second argument doit etre un entier");
		}
	}
}
Le programme ReceptionMessage sert à recevoir les messages. Il doit être lancé sur la machine destinatrice en donnant en argument le numéro de port où sont attendus les messages. Lorsqu'un message arrive, il est affichéàà l'écran précédé de l'adresse de la machine et du numéro de port dont est issu le message ; après chaque réception de message, le programme envoie un accusé de réception.

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

class ReceptionMessage {
	public static void main(String[] arg) {
		DatagramSocket socketUDP;
		DatagramPacket message;
		byte[] tampon;
		int portLocal;
		byte[] tamponAccuse = "accuse de reception".getBytes();
		int longueurAccuse = tamponAccuse.length;
		String texte ;

		try {
			portLocal = Integer.parseInt(arg[0]);
			socketUDP = new DatagramSocket(portLocal);
			while(true) {
				// on se prepare à recevoir un datagramme
				tampon = new byte[256];
				message = new DatagramPacket(tampon, tampon.length);
				socketUDP.receive(message);
				InetAddress adresseIP = message.getAddress();
				int portDistant = message.getPort();
				texte = new String(tampon) ;
				texte = texte.substring(0, message.getLength());
				System.out.println("Reception du port " + portDistant + " de la machine " + adresseIP.getHostName() + " : " + texte);

				// On envoie un accuse de reception
				message = new DatagramPacket(tamponAccuse, longueurAccuse, adresseIP, portDistant);
				socketUDP.send(message);
			}
		}

		catch(ArrayIndexOutOfBoundsException exc) {
			System.out.println("Avez-vous donne le numero de port sur lequel vous attendez le message ?");
		}
		catch(SocketException exc) { System.out.println("Probleme d'ouverture du socket");
		}
		catch(IOException exc) {
			System.out.println("Probleme sur la reception ou l'envoi du message");
		}
	}
}

Si on lance sur la machine gargantua.enst.fr :
    java ReceptionMessage 10302
puis si, sur la machine mars.eons.fr, on lance la commande :
    java EnvoiMessage gargantua.enst.fr 10302
puis qu'on indique par le clavier de cette machine :
    bonjour
alors, sur gargantua, on peut obtenir :
    Reception du port 56327 de la machine mars.eons.fr : bonjour
et sur mars :
    Port local : 56327
    Du port 10302 de la machine gargantua.enst.fr : accuse de reception

Vous pouvez accéder à :


InetAddress : cette classe de java.net modélise des adresses Internet.
DatagramSocket : cette classe de java.net modélise un socket qui peut recevoir ou envoyer des datagrammes.
socketUDP = new DatagramSocket(); : crée un socket pour datagrammes et la rattache à un port libre de la machine locale ; elle peut envoyer une exception de type SocketException si le socket ne peut pas être ouvert et une exception du type SecurityException si un gestionnaire de sécurité existe et n'autorise pas la création de ce socket (il n'est pas nécessaire d'attraper une exception de type SecurityException ou d'en signaler, par une clause throws, le possible lancement, car la classe SecurityException hérite de la classe RuntimeException).
getByName(arg[0]) : cette méthode statique de la classe InetAddress renvoie l'instance de InetAddress correspondant à une machine spécifiée par son nom. Elle peut envoyer une exception de type UnknownHostException.
new DatagramPacket(tampon, longueur, adresseIP, portDestinataire) : on fabrique le paquet à envoyer en indiquant en arguments le message sous forme d'un tableau d'octets, la longueur du message, l'adresse IP et le port de destination.
new DatagramPacket(tampon, tampon.length); : l'objet DatagramPacket construit est destiné à recevoir le message d'accusé de réception.
ligne.substring(0, message.getLength()) : cet appel de méthode permet d'extraire de ligne les caractères effectivement envoyés ; cela fonctionne parce qu'ici chaque caractère a été codé sur un seul octet et donc la longueur du message est égale au nombre de caractères envoyés
getAddress() : la méthode getAddress de la classe DatagramPacket renvoie, sous la forme d'une instance de la classe InetAddress, l'adresse IP de la machine d'où vient un datagramme, ou vers laquelle un datagramme va être envoyé.
getHostName() : la méthode getHostName de la classe InetAddress renvoie le nom de la machine correspondant à l'adresse contenue par l'instance de InetAddress concernée ; elle peut envoyer une exception de type SecurityException si un gestionnaire de sécurité existe et n'autorise pas la connexion avec cette machine. <

© Irène Charon, TéléParisTech 2011