Wanneer je een game-server schrijft, is de eerste architecturale beslissing die je uiteindelijk tegenkomt het protocol op de transportlaag: voor een TCP vs UDP game-server, welke is de juiste keuze? Het antwoord is niet zo simpel als "altijd UDP" of "altijd TCP". Beide draaien over hetzelfde IP-netwerk, maar ze dragen je data met totaal verschillende garanties, en die garanties bepalen rechtstreeks hoe responsief je game aanvoelt. In dit artikel leg ik uit wat de twee protocollen werkelijk doen, welk gamegenre bij welke past, en hoe ik in de praktijk beslis.
Wat doen TCP en UDP eigenlijk?
TCP (Transmission Control Protocol) is verbindingsgericht en betrouwbaar. Het brengt een verbinding tot stand met een drievoudige handshake (SYN, SYN-ACK, ACK) en garandeert dat elke byte die je verstuurt op volgorde en volledig aan de andere kant aankomt. Het verstuurt verloren pakketten automatisch opnieuw, herstelt de volgorde en remt zijn eigen snelheid af wanneer het netwerk verstopt raakt.
UDP (User Datagram Protocol) is verbindingsloos en volgt de "fire and forget"-logica. Het verstuurt een datagram en bemoeit zich niet met de rest: een pakket kan verloren gaan, in de verkeerde volgorde aankomen of zelfs twee keer opduiken. In ruil daarvoor voegt het bijna geen overhead toe; geen handshake, geen wachten op hertransmissie.
Kortom, de echte afweging tussen beide is deze:
- TCP: altijd correcte en geordende data, maar jitter en het risico op head-of-line blocking.
- UDP: minimale latency en volledige controle, maar je moet de betrouwbaarheid zelf bouwen.
Wat de latency in games bepaalt: head-of-line blocking
Om te begrijpen waarom UDP zo geliefd is in games, moet je TCP's sluwste probleem zien. Omdat TCP geordende levering garandeert, blijven bij het verlies van één enkel pakket alle pakketten die erna aankomen in de buffer van het besturingssysteem staan; geen ervan wordt aan je applicatie geleverd totdat het verloren pakket opnieuw is verstuurd en aankomt. Dit heet head-of-line blocking.
In een snelle shooter is dit rampzalig: een verloren pakket met de positie van de speler van 40 ms geleden houdt ook de meest recente positiepakketten tegen, ook al is dat oude pakket al verouderd. Maar in de gamelogica telt meestal de nieuwste status; het verliezen van de positie van drie frames geleden maakt niet uit. UDP laat je hier elk datagram onafhankelijk verwerken: je gooit het oude weg en gebruikt onmiddellijk het versste.
Welke voor welk genre?
Een praktisch beslissingskader ziet er zo uit:
- Realtime, snel tempo (FPS, MOBA, vechtgames, racen, action-MMO): UDP. Voor de constante stroom van positie-/invoerupdates is lage latency boven alles belangrijk, en het verlies van één pakket is te tolereren.
- Turn-based / traag tempo (kaartspel, schaken, browsergebaseerde strategie, social/chat): TCP. Elke actie is kritiek, milliseconden doen er niet toe, en eenvoud plus gegarandeerde levering winnen.
- Klassieke MMORPG's (zoals Metin2 of Tibia): in de praktijk gebruiken de meeste TCP. Inventaris, handel, chat en wereldstatus vereisen betrouwbaarheid; al zou de beweging wat vloeiender kunnen, de consistentie van TCP volstaat voor dit genre en vereenvoudigt de servercode aanzienlijk.
Een belangrijk punt: veel moderne games gebruiken beide tegelijk. Beweging en invoer stromen over UDP, terwijl login-authenticatie, aankopen, chat en persistente status over TCP gaan. Denk niet "ik moet één protocol kiezen".
Een eenvoudige vergelijking op Linux
Zien hoe verschillend de twee protocollen op socketniveau worden opgezet, maakt de beslissing concreet. Hieronder staan de skeletten van een TCP-listener en een UDP-listener in C++ (POSIX-sockets). Het verschil zit in SOCK_STREAM tegenover SOCK_DGRAM en de listen/accept-stappen in TCP:
// TCP-server skelet
int fd = socket(AF_INET, SOCK_STREAM, 0);
bind(fd, (sockaddr*)&addr, sizeof(addr));
listen(fd, 128);
int client = accept(fd, nullptr, nullptr); // verbinding accepteren
recv(client, buf, sizeof(buf), 0); // betrouwbare, geordende stroom
// UDP-server skelet
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); // verbindingsloos datagram
Bij UDP is er geen accept en geen persistente verbinding; elk datagram vertelt je via recvfrom waar het vandaan komt. Daarom moet je de "wie is welke speler"-koppeling, het sessiebeheer en de betrouwbaarheid zelf bouwen, op de applicatielaag.
Als je UDP koos: je bouwt de betrouwbaarheid zelf
UDP's "gratis snelheid" is eigenlijk niet gratis; je schrijft een deel van wat TCP je kant-en-klaar geeft met de hand. Doorgaans heb je nodig:
- Volgnummers: elk pakket voorzien van een oplopend nummer om oude of dubbele pakketten eruit te filteren.
- Selectieve betrouwbaarheid: alleen wat niet verloren mag gaan met een ACK garanderen (bijv. "geschoten", "dood"-events), niet alles.
- Congestie- en snelheidsbeheer: de verzendsnelheid afstemmen zodat je het netwerk niet overspoelt.
Het goede nieuws: je hoeft dit niet vanaf nul te schrijven. Volwassen bibliotheken zoals ENet, GameNetworkingSockets (Valve) of KCP voegen optionele betrouwbare/geordende kanalen bovenop UDP toe. Voor de meeste teams is de juiste weg er een van deze te gebruiken in plaats van ruwe UDP.
Een praktische beslissingschecklist
- Is de game snel en is de "nieuwste status" waardevoller dan de oude? → UDP.
- Is elk bericht kritiek en is volgorde essentieel, terwijl milliseconden er niet toe doen? → TCP.
- Is de ontwikkeltijd beperkt, bouw je een prototype? → Begin met TCP, verplaats beweging later naar UDP indien nodig.
- Is een hybride zinvol? → Ja, de meeste productiegames doen precies dat.
Veelgestelde vragen
Is UDP altijd sneller dan TCP?
Wat ruwe doorvoer (bandbreedte) betreft is er geen betekenisvol verschil; beide gebruiken hetzelfde netwerk. Het voordeel van UDP is de consistentie van de latency: omdat het niet stilstaat bij pakketverlies, vermijdt het jitter en head-of-line blocking. Het is dus nauwkeuriger om "responsiever en voorspelbaarder" te zeggen dan "sneller".
Kan ik UDP gebruiken in een browsergebaseerde game?
Je kunt geen ruwe UDP rechtstreeks vanuit een browser openen. WebSocket draait over TCP; voor transport met lage latency in UDP-stijl gebruik je een WebRTC-datakanaal (over UDP, met optionele betrouwbare/ongeordende modi). De meeste browsergames werken prima met WebSocket als snelheid niet kritiek is.
Is TCP genoeg voor een MMORPG zoals Metin2?
Ja. De oorspronkelijke architectuur van deze games is op TCP gebouwd en draait probleemloos met duizenden spelers. De stroom is niet zo hoogfrequent dat de geordende levering van TCP een probleem wordt; en voor handel, inventaris en wereldconsistentie zijn de garanties van TCP precies wat je wilt.
Conclusie: Het juiste antwoord hangt af van het ritme van je game; UDP voor een snel tempo, TCP voor kritieke en geordende berichten, en een combinatie van beide in de meeste productieomgevingen. Wil je samen de TCP/UDP-beslissing, de schaalbaarheid of de netwerklaag van je server-architectuur vastleggen? Neem contact op en laten we de vereisten van je project doorspreken.