Quand on écrit un serveur de jeu, la première décision d'architecture qu'on finit par rencontrer est le protocole de la couche transport : pour un serveur de jeu TCP vs UDP, lequel est le bon choix ? La réponse n'est pas aussi simple que « toujours UDP » ou « toujours TCP ». Les deux fonctionnent sur le même réseau IP, mais transportent vos données avec des garanties totalement différentes, et ces garanties déterminent directement la réactivité ressentie de votre jeu. Dans cet article, j'explique ce que font réellement les deux protocoles, quel genre de jeu convient à quel protocole, et comment je décide en pratique.
Que font réellement TCP et UDP ?
TCP (Transmission Control Protocol) est orienté connexion et fiable. Il établit une connexion par une poignée de main en trois étapes (SYN, SYN-ACK, ACK) et garantit que chaque octet envoyé arrive à l'autre bout dans l'ordre et complet. Il retransmet automatiquement les paquets perdus, corrige l'ordre et réduit lui-même son débit lorsque le réseau est congestionné.
UDP (User Datagram Protocol) est sans connexion et suit la logique du « envoyer et oublier ». Il envoie un datagramme et ne s'occupe pas du reste : un paquet peut être perdu, arriver dans le désordre, ou même apparaître deux fois. En contrepartie, il n'ajoute presque aucune surcharge ; pas de poignée de main, pas d'attente de retransmission.
En résumé, le vrai compromis entre les deux est le suivant :
- TCP : des données toujours correctes et ordonnées, mais de la gigue (jitter) et le risque de blocage en tête de file.
- UDP : une latence minimale et un contrôle total, mais vous devez construire la fiabilité vous-même.
Ce qui détermine la latence dans les jeux : le blocage en tête de file
Pour comprendre pourquoi UDP est tant apprécié dans les jeux, il faut voir le problème le plus sournois de TCP. Comme TCP garantit la livraison ordonnée, lorsqu'un seul paquet est perdu, tous les paquets arrivés après lui restent dans le tampon du système d'exploitation ; aucun n'est remis à votre application tant que le paquet perdu n'est pas retransmis et reçu. On appelle cela le head-of-line blocking (blocage en tête de file).
Dans un jeu de tir rapide, c'est catastrophique : un paquet perdu contenant la position du joueur d'il y a 40 ms retient aussi les paquets de position les plus récents, alors que cet ancien est déjà périmé. Or, dans la logique du jeu, c'est généralement l'état le plus récent qui compte ; perdre la position de trois images plus tôt n'a aucune importance. UDP vous permet ici de traiter chaque datagramme indépendamment : vous jetez l'ancien et utilisez immédiatement le plus frais.
Quel protocole pour quel genre ?
Un cadre de décision pratique peut s'établir ainsi :
- Temps réel, rythme rapide (FPS, MOBA, combat, course, MMO d'action) : UDP. Pour le flux constant de mises à jour de position/saisie, la faible latence prime sur tout, et la perte d'un seul paquet est tolérable.
- Tour par tour / rythme lent (jeu de cartes, échecs, stratégie sur navigateur, social/chat) : TCP. Chaque action est critique, les millisecondes n'ont aucune importance, et la simplicité plus la livraison garantie l'emportent.
- MMORPG classiques (comme Metin2 ou Tibia) : en pratique, la plupart utilisent TCP. L'inventaire, le commerce, le chat et l'état du monde exigent de la fiabilité ; même si le déplacement pourrait être un peu plus fluide, la cohérence de TCP suffit pour ce genre et simplifie grandement le code serveur.
Un point important : beaucoup de jeux modernes utilisent les deux à la fois. Le déplacement et la saisie circulent en UDP, tandis que l'authentification à la connexion, les achats, le chat et l'état persistant passent en TCP. Ne pensez pas « je dois choisir un seul protocole ».
Une comparaison simple sous Linux
Voir à quel point les deux protocoles se configurent différemment au niveau des sockets rend la décision concrète. Voici les squelettes d'un écouteur TCP et d'un écouteur UDP en C++ (sockets POSIX). La différence tient au SOCK_STREAM contre SOCK_DGRAM et aux étapes listen/accept de TCP :
// Squelette serveur TCP
int fd = socket(AF_INET, SOCK_STREAM, 0);
bind(fd, (sockaddr*)&addr, sizeof(addr));
listen(fd, 128);
int client = accept(fd, nullptr, nullptr); // accepter une connexion
recv(client, buf, sizeof(buf), 0); // flux fiable et ordonné
// Squelette serveur UDP
int fd = socket(AF_INET, SOCK_DGRAM, 0);
bind(fd, (sockaddr*)&addr, sizeof(addr));
sockaddr_in from; socklen_t len = sizeof(from);
recvfrom(fd, buf, sizeof(buf), 0,
(sockaddr*)&from, &len); // datagramme sans connexion
Avec UDP, il n'y a pas d'accept ni de connexion persistante ; chaque datagramme vous indique sa provenance via recvfrom. C'est pourquoi vous devez construire vous-même la correspondance « qui est quel joueur », la gestion des sessions et la fiabilité, au niveau de la couche applicative.
Si vous avez choisi UDP : vous construisez la fiabilité vous-même
La « vitesse gratuite » d'UDP n'est pas vraiment gratuite ; vous codez à la main une partie de ce que TCP vous offre d'emblée. Vous aurez généralement besoin de :
- Numéros de séquence : estampiller chaque paquet d'un numéro croissant pour filtrer les paquets anciens ou en double.
- Fiabilité sélective : garantir par ACK uniquement ce qui ne peut être perdu (par ex. les événements « tir effectué », « mort »), pas tout.
- Contrôle de congestion et de débit : ajuster le débit d'envoi pour ne pas saturer le réseau.
Bonne nouvelle : vous n'avez pas à écrire cela de zéro. Des bibliothèques matures comme ENet, GameNetworkingSockets (Valve) ou KCP ajoutent des canaux fiables/ordonnés optionnels par-dessus UDP. Pour la plupart des équipes, la bonne voie est d'utiliser l'une d'elles plutôt que de l'UDP brut.
Une liste de décision pratique
- Le jeu est-il rapide et l'« état le plus récent » vaut-il plus que l'ancien ? → UDP.
- Chaque message est-il critique et l'ordre indispensable, alors que les millisecondes n'importent pas ? → TCP.
- Le temps de développement est-il limité, faites-vous un prototype ? → Commencez en TCP, déplacez le mouvement vers UDP plus tard si besoin.
- Un hybride a-t-il du sens ? → Oui, la plupart des jeux en production font exactement cela.
Questions fréquentes
UDP est-il toujours plus rapide que TCP ?
En termes de débit brut (bande passante), il n'y a pas de différence significative ; les deux utilisent le même réseau. L'avantage d'UDP est la constance de la latence : comme il ne se bloque pas en cas de perte de paquet, il évite la gigue et le head-of-line blocking. Il est donc plus juste de dire « plus réactif et prévisible » que « plus rapide ».
Puis-je utiliser UDP dans un jeu sur navigateur ?
On ne peut pas ouvrir d'UDP brut directement depuis un navigateur. WebSocket fonctionne sur TCP ; pour un transport à faible latence de type UDP, on utilise un canal de données WebRTC (sur UDP, avec des modes fiables/non ordonnés optionnels). La plupart des jeux sur navigateur fonctionnent très bien avec WebSocket si la vitesse n'est pas critique.
TCP suffit-il pour un MMORPG comme Metin2 ?
Oui. L'architecture d'origine de ces jeux repose sur TCP et fonctionne sans problème avec des milliers de joueurs. Le flux n'est pas assez haute fréquence pour que la livraison ordonnée de TCP devienne un problème ; et pour le commerce, l'inventaire et la cohérence du monde, les garanties de TCP sont exactement ce qu'on veut.
Conclusion : La bonne réponse dépend du rythme de votre jeu ; UDP pour le rythme rapide, TCP pour les messages critiques et ordonnés, et une combinaison des deux dans la plupart des environnements de production. Envie de clarifier ensemble la décision TCP/UDP, la montée en charge ou la couche réseau de votre architecture serveur ? Contactez-moi et parlons des besoins de votre projet.