:: Enseignements :: Master :: M1 :: 2008-2009 :: Architecture et Programmation Réseau ::
[LOGO]

UDP, client/server, DatagramSocket, DatagramChannel, MulticastSocket


Les adresses de socket

Une socket identifie un point d'attachement à une machine. Selon le protocole, des classes sont dédiées pour représenter ces objets :
  • java.net.DatagramSocket pour UDP
  • java.net.Socket pour TCP
Les adresses de sockets identifient ces points d'attachement indépendamment du protocole : java.net.SocketAddress (classe abstraite). A priori indépendant du protocole réseau, mais la seule classe concrète est dédiée aux adresses Internet (IP) : java.net.InetSocketAddress.

Le rôle des sockets

Pour UDP et TCP, les sockets jouent le même rôle et sont identifiées par une adresse IP et un numéro de port. Au moins 2 sockets sont impliquées dans une communication.

Classe InetSocketAddress

Trois constructeurs :
  • InetSocketAddress(InetAddress addr, int port) (si addr est null, la socket est liée à l'adresse wildcard (non spécifiée) ;
  • InetSocketAddress(int port) (l'adresse IP est l'@ wildcard) ;
  • InetSocketAddress(String hostName, int port) (si hostName non résolu, l'adresse de socket est marquée comme étant non résolue ; peut être testé grâce à la méthode isUnresolved()).

Les Datagrammes

Les données circulent sur Internet sous forme de datagrammes (on parle aussi de paquets ). Les datagrammes sont des données encapsulées, c'est-à-dire des données auxquelles on a ajouté des en-têtes correspondant à des informations sur leur transport (telles que l'adresse IP de destination).
Les données contenues dans les datagrammes sont analysées (et éventuellement modifiées) par les routeurs permettant leur transit.
La taille max des données transportées est de (216 − 1 − 8). Le Checksum est optionnel en IP v4, mais obligatoire en IP v6. Voici ce à quoi ressemble un datagramme :

Créer un DatagramSocket

Représente un objet permettant d'envoyer ou recevoir des datagrammes UDP. Il existent différents constructeurs acceptant des arguments : InetSocketAddress (@IP+port, éventuellement wildcard)
Les méthodes getLocalPort(), getLocalAddress() et getLocalSocketAddress() retournent les infos sur la socket attachée localement (numéro de port, InetAddress et InetSocketAddress locaux). La méthode bind(SocketAddress) attache la socket si elle ne l'est pas déjà (isBound() retourne false). La méthode close() ferme la socket (libère les ressources système associées). isClosed() permet de savoir si la socket est fermée.

Créer un DatagramPacket

Représente un objet spécifiant les données qui doivent transiter ainsi que l'interlocuteur. Un objet, deux usages :
  • En émission, le DatagramPacket spécifie les données à envoyer et la machine (et le port) vers qui les envoyer
  • En réception, le DatagramPacket spécifie la zone de données permettant de recevoir et mettra à jour, lors de la réception, la machine et le port depuis lesquels ces données ont été reçues
Un DatagramPacket est fourni à (et pris en charge par) un DatagramSocket, pour émettre (datagramSocket.send(datagramPacket);) ou pour recevoir (datagramSocket.receive(datagramPacket); - appel bloquant tant que rien n'est reçu)

Quelques précisions

Les données reçues au delà de la taille de la zone de stockage sont perdues. Le système peut fixer des tailles des tampons de réception et d'émission. On peut demander à les changer, sans garantie de succès ([get/set]ReceiveBufferSize() et [get/set]SendBufferSize()). Il y a un risque de perte de da- tagramme : on peut vouloir limiter l'attente en réception (socket.setSoTimeout(int milliseconds) interrompt l'attente de réception au delà de milliseconds) ; ce qui lève alors une exception SocketTimeoutException.

User Datagram Protocol

User Datagram Protocol (ou UDP, protocole de datagramme utilisateur) est un des principaux protocoles de télécommunication utilisé par Internet.
Le rôle de ce protocole est de permettre la transmission de paquets (aussi appelés datagrammes ) de manière très simple entre deux entités, chacune étant définie par une adresse IP et un numéro de port (pour différencier différents utilisateurs sur la même machine). Contrairement au protocole TCP, il travaille en mode non-connecté : il n'y a pas de moyen de vérifier si tous les paquets envoyés sont bien arrivés à destination et ni dans quel ordre. C'est pour cela qu'il est souvent décrit comme étant un protocole non-fiable. Par contre, pour un datagramme UDP donné, l'exactitude du contenu des données est assuré grâce à une somme de contrôle (checksum ).

Structure d'un datagramme UDP

Le paquet UDP est encapsulé dans un paquet IP. Il comporte un en-tête suivi des données propre- ment dites à transporter.

L'en-tête (header en anglais) d'un datagramme UDP est bien plus simple que celui de TCP :

Il contient les 4 champs suivants :
  • Port Source : il indique depuis quel port le paquet a été envoyé.
  • Port de Destination : il indique à quel port le paquet doit être envoyé
  • Longueur : il indique la longueur totale du datagramme UDP (en-tête et données). La longueur minimale est donc de 8 octets (taille de l'en-tête)
  • Somme de contrôle : celle-ci (CRC, Cyclic Redundancy Check ) permet de s'assurer de l'intégrité du paquet reçu. Elle est calculée sur l'ensemble de l'en-tête UDP et des données, mais aussi sur un pseudo en-tête (extrait de l'en-tête IP)

Utilisation

Il est utilisé quand il est nécessaire soit de transmettre des données très rapidement, et où la perte d'une partie de ces données n'a pas grande importance, soit de transmettre des petites quantités de données, là où la connexion 3-WAY TCP serait trop lourde. Par exemple, dans le cas de la transmission de la voix sur IP, ce n'est pas grave si l'un ou l'autre paquet se perd (il existe des mécanismes de substitution des données manquante), par contre la rapidité de transmission est un critère primordial pour la qualité d'écoute.
Exemples d'utilisation : le programme traceroute, les protocoles DNS, TFTP, les jeux en réseau (exemple : jeux de tir subjectifs), le streaming : il est indispensable pour des applications multimédias de par sa faible latence.

Liens Externes

RFC 768 : User Datagram Protocol : http ://www.ietf.org/rfc/rfc768.txt IANA Port Assignments : liste des ports prédéfinis et de leurs utilisations, par l'IANA : http ://www.iana.org/assignments/port-numbers

Communications Client/Serveur en UDP

Exemple de Client utilisant IO :

Exemple de Serveur utilisant IO :

Exemple de Client utilisant NIO (Channel) :

Exemple de Serveur utilisant NIO (Channel) :

Accès à UDP via les canaux

La classe java.nio.channels.DatagramChannel permet de créer un Canal vers une socket UDP. On peut créer une java.net.DatagramSocket à partir d'un DatagramChannel, mais pas le contraire. Si une socket UDP su a été créée à partir d'un canal, on peut récupérer ce dernier par su.getChannel(). Sinon, cette méthode retourne null. La méthode DatagramChannel.open() crée et retourne un canal associé à une socket UDP (non attachée). DatagramChannel n'est pas une abstraction complète des sockets UDP : pour les opérations précises (binding, etc...) on récupère l'objet DatagramSocket sous- jacent.

DatagramChannel

Par défaut, un canal dc récupéré par DatagramChannel.open() est bloquant. Il peut être configuré non bloquant. dc.socket() récupère alors la DatagramSocket correspondante. Elle n'est pas attachée. On peut faire bind(SocketAddress) sur cette socket. Les méthodes send() et receive() sont accessibles depuis le canal. Elles manipulent des ByteBuffer et receive() retourne un objet SocketAddress identifiant l'émetteur des données reçues. On doit faire une pseudo-connexion pour pouvoir utiliser les méthodes read() et write() avec des ByteBuffer, plus classiques sur les canaux (interlocuteur implicite pour ces méthodes).

Envoi sur un DatagramChannel

int send(ByteBuffer src, SocketAddress target) provoque l'envoi des données restantes du tampon src vers target (semblable à un write() du point de vue du canal). Si canal bloquant, la méthode retourne lorsque tous les octets ont été émis (leur nombre est retourné). Si canal non bloquant, la méthode émet tous les octets ou aucun. Si une autre écriture est en cours sur la socket par une autre thread, l'invocation de cette méthode bloque jusqu'à ce que la première opération soit terminée.

Réception depuis un DatagramChannel

SocketAddress receive(ByteBuffer dst) est, par défaut, une méthode bloquante tant que rien n'est reçu par la socket. Au plus dst.remaining() octets peuvent être reçus ; le reste est tronqué. Cette méthode retourne l'adresse de socket (IP+port) de l'émetteur des données. Si canal non bloquant, soit tout est reçu, soit le tampon n'est pas modifié et la méthode retourne null.

Exemple de client UDP avec canaux : envoi


Exemple de client UDP avec canaux : réception


Exemple d'émission (client)

Exemple de réception (client)

Exemple de réception (serveur)

Dans le cas d'un client, le choix du numéro de port d'attachement de la socket n'a pas d'importance, il sera connu par le serveur à la réception du datagramme émis par le client. En revanche, dans le cas d'un serveur, il doit pouvoir être communiqué aux clients, afin qu'ils puissent l'interroger. Il est nécessaire d'attacher la socket d'écoute (d'attente des clients) à un port et une adresse spécifique et connue ainsi que d'utiliser un constructeur de DatagramSocket le spécifiant (il y a un risque que le port ne soit pas libre : SocketException).

Réception serveur (suite)

Broadcast et Multicast

Le broadcast est un terme anglais (en français on utilise le terme diffusion) définissant une diffusion de données à un ensemble d'ordinateurs connectés à un réseau informatique. Les protocoles de communications réseau prévoient une méthode simple pour diffuser des données à plusieurs machines en même temps. Au contraire d'une communication Point à Point (unicast), il est possible d'adresser des paquets de données à un ensemble de machines d'un même réseau uniquement par des adresses spécifiques qui seront interceptées par toutes les machines du réseau ou sous-réseau.
L'étendue de diffusion sera restreinte au domaine de diffusion. Par exemple, en IPv4, une adresse IP de diffusion telle que 192.168.1.255 sera interceptée par toutes les machines ayant une adresse IP entre 192.168.1.1 et 192.168.1.254, pour autant que le masque de sous-réseau de l'interface soit défini comme 255.255.255.0.
Un commutateur recevant une trame broadcast sur l'un de ses ports la diffusera sur tous les autres ports. Les routeurs ne transmettent pas les paquets broadcast.
Pour une diffusion de données moins générale, on utilisera les adresses Multicast
On entend par multicast le fait de communiquer simultanément avec un groupe d'ordinateurs identifiés par une adresse spécifique (adresse de groupe).

Communication en diffusion

D'un émetteur vers un groupe ou un ensemble de récepteurs :
  • le broadcast utilise les mêmes classes DatagramSocket et DatagramPacket que pour la commu- nication unicast, avec une adresse destination de broadcast
  • le multicast utilise la classe DatagramPacket pour les datagrammes mais la classe MulticastSocket, spécifique pour les sockets

Le broadcast

La classe DatagramSocket dispose de méthodes [set/get]Broadcast() (autorisation pour la socket d'émettre des broadcasts). En réception, une socket ne peut recevoir des broadcasts que si elle est attachée à l'adresse non spécifiée (wildcard).

Le multicast

On s'adresse à un ensemble de machines (ou applications) ayant explicitement adhéré au groupe (adresses de classe D en IP v4 : 224.0.0.0/4, adresses du réseau FF00 : :/8 en IP v6). Les machines ayant rejoint un tel groupe acceptent les datagrammes à destination de l'adresse correspondante.

java.net.MulticastSocket

Cette classe hérite de DatagramSocket. Elle possèdent trois constructeurs de MulticastSocket : sans arguments : attache à un port libre et à l'@ wildcard ; numéro de port : attache à ce port et à l'@ wildcard ; SocketAddress : attache à cette adresse de socket, ou bien n'attache pas la socket si l'argument vaut null.
Les constructeurs appellent la méthode setReuseAddress(true) qui autorise plusieurs sockets à s'attacher à la même adresse (MÊME PORT pour tous les membres du groupe).

MulticastSocket en émission

En émission, MulticastSocket s'utilise comme DatagramSocket. On peut spécifier une durée de vie pour le datagramme : plus exactement, un nombre de sauts (0=émetteur, 1=réseau local, 16= site, 32=région, 48=pays, 64=continent, 128=monde).

MulticastSocket en réception

Il faut explicitement rejoindre le groupe (joinGroup() avec l'adresse IP multicast du groupe en argument (existe aussi avec SocketAddress et/ou NetworkInterface). Cela permet alors à la socket de recevoir tous les datagrammes destinés à cette adresse multicast. On peut rejoindre plusieurs groupes de multicast. La méthode leaveGroup() permet de quitter le groupe d'adresse spécifiée en argument.