Le protocole UDP
Présentation de UDP (User Datagram Protocol)
- Permet d'envoyer des paquets de données IP indépendants
- La source et la destination d'un paquet sont définis par une adresse IP et un numéro de port (socket)
- Protocole d'implantation simple (latence faible) mais aucune garantie sur la bonne réception du paquet (ainsi que sur le délai de livraison) : pas de contrôle de flux et de congestion
- Vulnérable à l'usurpation d'@IP source
- Supporte le multicast et le broadcast contrairement à TCP
-
Utilisé pour les applications nécessitant une latence faible avec une tolérance aux pertes de paquets :
- Envoi de petits messages informatifs sur un réseau local
- Streaming audiovisuel
- Voix sur IP
- Jeux en réseau
- ...
Format du datagramme UDP
- Port source sur 16 bits
- Port destination sur 16 bits
- Longueur sur 16 bits (soit 65535 octets au maximum)
- Somme de contrôle sur 16 bits (calculée sur l'en-tête comprenant la fin de l'en-tête IP et les données)
- Champ de données
Les sockets UDP en Java
Sockets
- Les piles IP implantent un mécanisme de socket (association d'adresse IP et de port de communication ouvert sur une machine)
- Réalisation d'une connexion en envoyant un paquet d'une socket vers une autre
Sur les systèmes POSIX
- int socket(int domain, int type, int protocol) pour ouvrir une socket
- int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) pour lier une socket à une adresse et un port
- int send(int socket, const void *buf, size_t len, int flags) pour envoyer un datagramme
- ssize_t recv(int s, void *buf, size_t len, int flags) pour extraire un datagramme de la file d'attente de réception
- man 7 socket, ... pour en savoir plus
Les interfaces réseau
- Pour chaque interface réseau active d'une machine : au moins une @IP
-
Une socket doit se fixer sur une ou plusieurs interfaces réseau (pour s'intéresser uniquement aux données provenant de cette interface) :
- Choix d'une interface réseau par son @IP (e.g. 127.0.0.1 pour lo en IPv4)
- Choix de toutes les interfaces réseau avec l'adresse joker (adresse nulle)
Pour en savoir plus sur la manipulation des interfaces réseau en Java
DatagramSocket
Socket réseau UDP : classe DatagramSocket
Association d'une adresse et d'un port pour recevoir ou envoyer des datagrammes UDP
Constructeurs disponibles :
- DatagramSocket() : création d'une socket sur l'adresse joker sur un des ports disponibles
- DatagramSocket(int port) : permet de choisir le port d'attache, utilisation de l'adresse joker
- DatagramSocket(int port, InetAddress addr) : permet de s'attacher sur une adresse spécifique de la machine (n'écoute pas sur toutes les interfaces)
Ces constructeurs peuvent lever une SocketException si la socket ne peut être allouée.
Après utilisation, une socket doit être fermée avec close() pour libérer les ressources systèmes associées.
Datagramme UDP : classe DatagramPacket
Cette classe permet de manipuler des datagrammes UDP.
- Ils peuvent être créés par des constructeurs utilisant un tableau d'octets (byte[]) soit pour recevoir des données, soit pour en envoyer.
- Pour recevoir des données, le tableau d'octets communiqué doit être de taille suffisante pour recevoir les données (sinon le paquet est tronqué !).
-
Une instance peut être utilisée pour envoyer plusieurs paquets ou pour en recevoir. Possibilité de modifier :
- L'adresse de destination avec setAddress(InetAddress) ainsi que le port avec setPort(int).
- Le tampon de données avec setData(byte[] buffer, [int offset, int length]).
- Les données peuvent être consultées avec byte[] getData() et int getLength() (taille du paquet reçu).
DatagramPacket a un double usage :
- Paquet à envoyer : l'adresse (p.getAddress()) et le port (p.getPort()) sont les coordonnées du destinataire
- Paquet reçu : l'adresse et le port sont les coordonnées de l'expéditeur
Caractères et octets
- Les données sont échangées sur le réseau par séquence d'octets
-
Pour transmettre une chaîne de caractères de A vers B :
- Codage de la chaîne en octets par A
- Transmission d'un paquet avec le tableau d'octets
- Réception du paquet avec tableau d'octets par B
- Décodage des octets en chaîne par B
Des caractères vers les octets (et vice-versa)
- Convention de conversion caractère-octet implantée par classe Charset
- Quelques jeux de caractères : ASCII, iso-8859-15, UTF-8, UTF-16LE, UTF-16BE, ...
- Un caractère peut être codé par un ou plusieurs octets
- De la chaîne aux octets : byte[] String.getBytes(String charset)
- Des octets à la chaîne : new String(byte[] tableauOctets, int depart, int longueur, String charset)
- Si le jeu de caractère n'est pas indiqué : utilisation du jeu par défaut (static Charset Charset.defaultCharset())
- Pour obtenir tous les jeux disponibles : static SortedMap<String,Charset> Charset.availableCharsets()
Envoyer et recevoir des paquets avec DatagramSocket
- Méthode send(DatagramPacket) pour envoyer une paquet : paquet ajouté dans la file d'attente d'émission
-
Méthode receive(DatagramPacket) pour recevoir un paquet en file d'attente de réception
- Appel bloquant jusqu'à la réception d'un paquet
- setSoTimeout(int timeoutInMillis) fixe un délai limite de réception de paquet (au-delà, levée d'une java.net.SocketTimeoutException)
- Ces méthodes peuvent lever une java.io.IOException qui doit être gérée.
-
La taille des files d'attente est consultable et configurable avec :
- int get{Send,Receive}BufferSize()
- et void set{Send,Receive}BufferSize().
Envoyons un paquet avec des données aléatoires
public static void sendRandomPacket(DatagramSocket socket, String addressee, int port, int dataLength) throws IOException { DatagramPacket dp = new DatagramPacket(new byte[dataLength], dataLength); // setting the coordinates dp.setAddress(addressee); dp.setPort(port); // put random data into the packet Random r = new Random(); r.nextBytes(dp.getData()); // sending the packet socket.send(dp); }
Recevons un paquet avec un timeout
public static boolean receiveAndDisplayPacket(DatagramSocket socket, int timeout) throws IOException { socket.setSoTimeout(timeout); DatagramPacket dp = new DatagramPacket(new byte[2048], 2048); // can receive packets containing at most 2048 bytes (trailing bytes are truncated) try { socket.receive(dp); // we receive the packet (unless timeout) System.out.println("Have received packet from address=" + dp.getAddress() + " at port " + dp.getPort()); System.out.println("This packet contains the following bytes: "); for (int i = dp.getOffset(); i < dp.getOffset() + dp.getLength(); i++) { System.out.print(((int)dp.getData()[i]) & 0xff); // display the byte using an unsigned form System.out.print(" "); } System.out.println(); // for line return return true; } catch (SocketTimeoutException e) { return false; // timeout occurred } }
Envoyer des paquets UDP contenant du texte
Exemple Java : envoyer un paquet contenant l'heure à une plage d'adresses
Content not available
Quelques conseils pour l'échange de paquets
-
Recycler des DatagramPacket déjà créés (en changeant les données hébergées)
- Typiquement, on peut créer un DatagramPacket que l'on utilise pour tous les paquets à recevoir et un autre DatagramPacket pour tous les paquets à envoyer
- Ne pas créer des DatagramSocket inutiles, les libérer lorsqu'on n'en a plus besoin
- Toujours vérifier que la taille de réception est suffisante
- → erreur classique : envoyer un paquet de k octets puis recevoir sur ce même paquet sans changer de tableau ni de taille ; on est alors limité à k octets pour la réception
- Ne pas oublier d'utiliser la taille des données reçues : la fin du tableau n'est pas exploitable
- Vérifier l'adresse IP/port émetteur d'un paquet reçu (même si cela n'apporte qu'une sécurité faible)
Mécanisme de pseudo-connexion
- Possibilité de pseudo-connecter deux sockets UDP avec void DatagramSocket.connect(InetAddress addr, int port) : filtrage automatique des paquets émis et reçus
- Vérification de la connexion avec boolean isConnected()
- Déconnexion avec void disconnect()
Quelques tests avec Netcat
Netcat : application simple pour communiquer en UDP (et aussi en TCP)
- On lance le serveur sur le port N en écoute en UDP sur toutes les interfaces (adresse joker) : nc -l -u N
- On exécute un client se connectant sur le port N de la machine : nc -u nomMachine N
- Le client envoie chaque ligne de l'entrée standard dans un datagramme UDP : le serveur la reçoit et l'affiche sur sa sortie standard
- Envoi de datagrammes du serveur vers le client également possible
Limitation pour le serveur netcat : communication avec un seul client
Exemple Java : recevoir des datagrammes sur un port avec un délai limite
Content not available
Multicast
Multicast UDP
-
Quelques rappels sur les adresses de multicast :
- 224.0.0.0/4 en IPv4 (préfixe 1110)
- FF00::0/8 en IPv6 (\rfc{4291})
- Certaines adresses à usage prédéfinis (groupe des hôtes, routeurs, uPnP...)
Principe :
- Les hôtes s'abonnent/se désabonnent à une adresse de multicast A.
- Une trame émise vers A est adressée à tous les membres du groupe.
Avantages :
- En théorie, mutualise les paquets échangés sur des segments de réseau contrairement à l'unicast.
- Évite d'envoyer des paquets à tous les hôtes comme en broadcast (broadcast incompatible avec un réseau étendu).
Usages pratiques :
- Encore peu utilisé globalement sur Internet
-
Protocoles d'autoconfiguration et de découverte de ressources sur un réseau local
- uPnP (utilisation de HTTP sur UDP), ZeroConf, Bonjour...
-
Diffusion de données audiovisuelles sur un réseau local
- Exemple: utilisation par des FAI pour diffuser la TV sur IP sur des boxes
Protocoles de multicast
- IGMP (Internet Group Management Protocol) permet de signaler à la passerelle du réseau local les membres de chaque groupe. Certains commutateurs de couche 2 l'utilisent.
- Les commutateurs basiques ne supportant pas IGMP peuvent transformer les trames multicast en trames broadcast
- Sur Internet, un arbre de distribution multicast est construit avec PIM (Protocol Independent Multicast) en Sparse Mode, Dense Mode, mixte Sparse-Dense ou Source Specific Mode.
java.net.MulticastSocket
- Version améliorée de DatagramSocket avec support du multicast
- Envoi classique d'un datagramme avec send(DatagramPacket) (à une IP unicast ou multicast)
-
Réception de datagrammes en multicast requierant un abonnement préalable au groupe de multicast
- void joinGroup(InetAddress ia) pour joindre un groupe
- void leaveGroup(InetAddress ia) pour quitter un groupe
-
Quelques paramètres utiles pour le multicast
- void setInterface(InetAddress ia) ou void setNetworkInterface(NetworkInterface netIf) pour fixer l'interface utilisée pour la communication multicast
- void setTimeToLive(int ttl) pour indiquer la portée des paquets multicast envoyés