Fondamentaux du protocole UDP
Le protocole UDP (User Datagram Protocol) est l'un des piliers de la couche transport dans la suite TCP/IP. Contrairement à TCP, il fonctionne en mode "sans connexion", s'appuyant directement sur le protocole IP pour acheminer des paquets entre les processus applicatifs. UDP ne garantit ni la livraison, ni l'ordre des données, ce qui en fait un protocole idéal pour les applications exigeant une faible latence au détriment d'une fiabilité absolue.
Alors que la couche réseau (IP) s'occupe de l'acheminement de "point à point" (entre hôtes), UDP gère la communication de "bout en bout" (entre processus) en utilisant des numéros de ports pour identifier les applications cibles sur une machine donnée.
Caractéristiques principales
- Mode sans connexino : Aucune phase d'établissement de liaison (comme le "three-way handshake" de TCP) n'est requise. Les données sont envoyées immédiatement.
- Légèreté : L'en-tête UDP est minimaliste (8 octets), réduisant la surcharge protocolaire et le temps de traitement noyau.
- Absence de contrôle de flux : UDP ne régule pas le débit d'envoi en fonction de la congestion du réseau, ce qui est crucial pour le streaming temps réel.
- Orientation Datagramme : Les messages sont envoyés sous forme de blocs complets. Le récepteur doit lire un datagramme entier à chaque opération de lecture.
Structure de l'en-tête UDP
L'en-tête UDP est composé de quatre champs de 16 bits chacun, pour un total fixe de 8 octets :
| Champ | Taille (bits) | Description |
|---|---|---|
| Port Source | 16 | Identifie le port de l'expéditeur (optionnel, 0 si inutilisé). |
| Port Destination | 16 | Identifie le port du service destinataire sur l'hôte distant. |
| Longueur totale | 16 | Taille de l'en-tête plus les données (min 8 octets, max 65535). |
| Somme de contrôle (Checksum) | 16 | Vérification de l'intégrité du paquet (inclut un pseudo-en-tête IP). |
Note technique sur la taille maximale : Bien que la limite théorique d'un datagramme UDP soit de 65 507 octets (65535 - 8 octets d'en-tête UDP - 20 octets d'en-tête IP), il est fortement recommandé de ne pas dépasser la MTU standard d'Ethernet (souvent 1500 octets). Une charge utile de 1472 octets évite généralement la fragmentation IP.
Interface de programmation Sockets sous Linux
La programmation réseau UDP en langage C repose sur plusieurs fonctions systèmes clés définies dans sys/socket.h et arpa/inet.h.
1. Création de la socket
La fonction socket() initialise un point de terminaison pour la communication.
int fd_reseau = socket(AF_INET, SOCK_DGRAM, 0);
if (fd_reseau < 0) {
perror("Erreur d'initialisation de la socket");
return -1;
}
2. Liaison à une adresse locale (bind)
Pour qu'un serveur puisse recevoir des données, il doit lier sa socket à un port spécifique.
struct sockaddr_in config_serveur;
memset(&config_serveur, 0, sizeof(config_serveur));
config_serveur.sin_family = AF_INET;
config_serveur.sin_port = htons(9000); // Conversion en format réseau (Big-endian)
config_serveur.sin_addr.s_addr = htonl(INADDR_ANY); // Écoute sur toutes les interfaces
if (bind(fd_reseau, (struct sockaddr *)&config_serveur, sizeof(config_serveur)) < 0) {
perror("Échec du bind");
close(fd_reseau);
return -1;
}
3. Envoi et Réception
UDP étant sans connexion, chaque opération d'envoi doit spécifier l'adresse de destination.
// Envoi de données
struct sockaddr_in dest_info;
char *msg = "Données applicatives";
sendto(fd_reseau, msg, strlen(msg), 0, (struct sockaddr *)&dest_info, sizeof(dest_info));
// Réception de données
char tampon[2048];
struct sockaddr_in client_info;
socklen_t taille_client = sizeof(client_info);
ssize_t n = recvfrom(fd_reseau, tampon, sizeof(tampon) - 1, 0, (struct sockaddr *)&client_info, &taille_client);
if (n > 0) {
tampon[n] = '\0';
printf("Reçu de %s : %s\n", inet_ntoa(client_info.sin_addr), tampon);
}
Fonctions de convertion d'adresses
Les adresses IP et les ports doivent être convertis entre l'ordre des octets de l'hôte (souvent Little-endian sur x86) et l'ordre réseau (Big-endian).
htons()/ntohs(): Conversion de ports (16 bits).htonl()/ntohl(): Conversion d'adresses IP (32 bits).inet_pton(): Convertit une chaîne de caractères (ex: "127.0.0.1") en binaire réseau.inet_ntop(): Convertit une adresse binaire en chaîne lisible.
Communication avancée : Diffusion (Broadcast) et Multidiffusion (Multicast)
Broadcast
Permet d'envoyer un paquet à tous les hôtes d'un réseau local. L'option SO_BROADCAST doit être activée sur la socket.
int option_active = 1;
setsockopt(fd_reseau, SOL_SOCKET, SO_BROADCAST, &option_active, sizeof(option_active));
struct sockaddr_in adr_diffusion;
adr_diffusion.sin_addr.s_addr = inet_addr("255.255.255.255");
Multicast
Cible un groupe spécifique d'hôtes ayant rejoint une adresse de classe D (224.0.0.0 à 239.255.255.255). Cela réduit la charge réseau par rapport au broadcast.
struct ip_mreq groupe;
groupe.imr_multiaddr.s_addr = inet_addr("226.1.1.1");
groupe.imr_interface.s_addr = htonl(INADDR_ANY);
// Rejoindre le groupe
setsockopt(fd_reseau, IPPROTO_IP, IP_ADD_MEMBERSHIP, &groupe, sizeof(groupe));
Comparaison structurelle : UDP vs TCP
| Fonctionnalité | UDP | TCP |
|---|---|---|
| Connexion | Non | Oui (Handshake) |
| Fiabilité | Faible (Aucun acquittement) | Haute (Réexpédition auto) |
| Ordre des données | Non garanti | Garanti |
| Débit | Élevé / Temps réel | Modéré par le contrôle de flux |
| Usage type | VoIP, Streaming, DNS, Gaming | Web (HTTP), Email (SMTP), Transfert de fichiers (FTP) |
Applications pratiques
- DNS (Domain Name System) : Utilise UDP pour sa rapidité sur de petites requêtes. Si une réponse est perdue, le client réessaie simplement après un timeout.
- Streaming Vidéo/Audio : La perte d'une fraction de seconde de données est préférable à une pause dans la lecture due à une retransmission TCP.
- IoT et Capteurs : Les appareils à ressources limitées privilégient UDP pour minimiser la consommation d'énergie et la mémoire utilisée par la pile protocolaire.
- Protocoles de routage : Certains protocoles comme RIP utilisent UDP pour échanger des tables de routage périodiquement.