RE:NODE
ჰოსტინგი

ქსელი13 წუთის საკითხავი

TCP vs UDP for game servers: why games use UDP

Why almost every game speaks UDP, what that costs you in firewall rules and proxies, and how to test a UDP port properly when telnet is useless.

ეს სტატია ჯერ ინგლისურადაა. ვთარგმნით.

0 მკითხველი

Almost every action game on the internet sends its gameplay over UDP, and the reason is one sentence long: in a game, a packet that arrives late is worse than a packet that never arrives at all. TCP cannot express that idea. It guarantees that everything you sent arrives, in order, which means that when one packet is lost, everything behind it waits for the retransmission - and by the time that retransmitted position update arrives, the player has moved three times. UDP has no such guarantee and no such delay, so games take the raw datagram and build exactly the reliability they need on top of it. That single choice explains most of the practical things you will run into: why your firewall rule did not work, why you cannot hide a game server behind the same proxy as your website, and why telnet tells you nothing useful about a game port.

The difference that matters: head-of-line blocking#

The textbook comparison lists five differences. Only one of them decides anything.

TCP is a stream. It numbers every byte, acknowledges what arrived, retransmits what did not, and hands the application a perfectly ordered sequence. To do that it has to hold back data that arrived out of order until the missing piece turns up. That is head-of-line blocking, and on a lossy connection it converts a 1% packet loss into a visible freeze, because the receiving application is not allowed to see packets 5, 6 and 7 until packet 4 has been recovered. On Linux the minimum retransmission timeout is 200 ms, so a single loss at the wrong moment costs at least that.

UDP is a datagram. It adds a source port, a destination port, a length and a checksum to your data and sends it. Nothing is acknowledged, nothing is retransmitted, nothing is reordered, and nothing waits. A lost packet is simply gone, and the application finds out by noticing that the sequence number jumped.

The other differences follow from that:

TCPUDP
Header20 bytes minimum8 bytes
ConnectionThree-way handshake firstNone
DeliveryGuaranteed, in orderNeither
On lossRetransmit, block the streamNothing happens
Source addressVerified by the handshakeTrivially forged
Congestion controlBuilt inYours to implement

The forged-source row is the one with consequences beyond performance: because UDP has no handshake, an attacker can put your address on a packet and have a third party reply to you. That is the whole basis of reflection attacks, covered in DDoS attacks on game servers explained.

Why a shooter cannot use TCP#

Work through the timing and the argument makes itself.

A server running at 64 ticks per second produces a new snapshot of the world every 15.6 ms. Each snapshot describes where everybody is right now. Suppose snapshot 100 is lost on the way to a player.

Over UDP: nothing happens. Snapshot 101 arrives 15.6 ms later with newer positions, the client interpolates over the gap, and nobody notices. The lost snapshot was obsolete before anyone could have asked for it again.

Over TCP: the client's kernel has snapshots 101, 102 and 103 sitting in a buffer and refuses to hand any of them to the game, because snapshot 100 is missing and order must be preserved. It waits for the retransmission, which takes at least one round trip and possibly a 200 ms timeout. Then the game receives four snapshots at once, three of which describe the past. The player sees a freeze followed by everyone teleporting. This is exactly what "rubber-banding" looks like.

There is no configuration that fixes this. TCP_NODELAY turns off Nagle's algorithm and removes a different delay - the one where small writes are held back to be coalesced - and it is worth setting on any TCP game server, but it does not touch head-of-line blocking, which is a property of the guarantee itself.

So games send state over UDP and accept loss as normal. What they lose in reliability they make up with technique: full snapshots so that any single packet is enough to resynchronise, delta compression against the last acknowledged state, client-side prediction so that your own movement is instant, and interpolation so that other players move smoothly across gaps. Latency, jitter and packet loss covers how those three failure modes feel differently to a player, and what tick rate actually means covers the sending side.

Which games use which#

The pattern is not random. Games where the world is a continuous simulation use UDP. Games where the world is a series of discrete, individually important events can afford TCP.

GameGameplay protocolNotes
Minecraft (Java)TCP 25565Every block change matters; optional UDP query
TerrariaTCP 7777Same reasoning, smaller world
RimWorld multiplayerTCPLockstep simulation, order is everything
Counter-Strike 2, TF2, Garry's ModUDP 27015RCON is TCP on the same number
ValheimUDP 2456-2457Game and query both UDP
FactorioUDP 34197Deterministic lockstep, custom reliability
PalworldUDP 8211Unreal networking
Arma 3, DayZUDP 2302 and upSeveral consecutive ports
Project ZomboidUDP 16261RCON TCP on 27015
7 Days to DieTCP and UDP 26900Plus telnet on TCP
FiveMTCP and UDP 30120HTTP endpoints share the number
SatisfactoryTCP and UDP 7777Since the 1.0 networking change
BeamMPTCP and UDP 30814Both, on one number
Assetto CorsaUDP 9600, TCP 9600 and 8081The last one is the web interface

Minecraft is the instructive exception. It is not a twitch game: a block placement, an inventory move or a chat line is a discrete event that must arrive exactly once and in order, and there is no useful way to interpolate a missing one. TCP is the right tool for that, and the cost is that a player on a bad connection gets a freeze rather than a rubber-band. The Bedrock codebase is a different program that uses UDP on 19132, which is why the two have never shared a port.

Anything listed as both usually means gameplay on UDP plus something administrative or HTTP-shaped on TCP, on the same number. That is why those games ask for a single allocation covering both protocols rather than two.

Reliability rebuilt on top of UDP#

"UDP has no reliability" is true of UDP and false of every game that uses it. What games actually do is implement selective reliability, because different messages need different guarantees.

A typical engine has at least two channels. An unreliable channel carries position and state snapshots, where the newest one supersedes everything before it and a loss is free. A reliable channel carries events that must arrive - you picked up an item, the round ended, you were kicked - with sequence numbers, acknowledgements and retransmission handled inside the game, which can drop a stale retransmission that TCP would have insisted on delivering.

The libraries that do this are worth recognising in a log or a config file: ENet, RakNet, Steam Networking Sockets and its Datagram Relay, and increasingly QUIC, which is a reliable, encrypted, multiplexed transport built on UDP and is what HTTP/3 runs over. QUIC is the clearest proof of the argument: when the people who maintain TCP wanted to fix head-of-line blocking for the web, they did not fix TCP, they rebuilt on UDP.

A practical consequence for sizing: because games manage their own retransmission, a lossy route degrades gracefully rather than collapsing, but it degrades. 2% loss on a game server is a visible problem where 2% loss on a file download is invisible. When somebody says the server is laggy, loss is one of the three things to rule out, and reading traceroute and mtr is how you find which hop is doing it.

What this means for firewall rules#

Every firewall rule names a protocol, and the single most common configuration mistake in game hosting is naming the wrong one. A server whose TCP port is open and whose UDP port is not starts perfectly, logs nothing unusual, and cannot be joined by anybody.

bash
$ ufw allow 25565/tcp            # Minecraft Java$ ufw allow 2456:2457/udp        # Valheim game and query$ ufw allow 27015/udp            # a Source game$ ufw allow 27015/tcp            # the same game's RCON$ ufw allow 30814                # both protocols, for BeamMP$ ufw status numbered

Leaving the protocol off, as in the fifth line, opens both. That is correct for a game that genuinely uses both and sloppy otherwise. The ufw firewall guide has the rest, including the Docker caveat that catches people with an exposed database.

Two UDP-specific behaviours that cause confusion:

There is no such thing as a UDP connection, but your firewall pretends there is. Connection tracking creates a pseudo-entry when it sees the first packet of a flow and expires it after a timeout - on Linux, nf_conntrack_udp_timeout defaults to 30 seconds, and nf_conntrack_udp_timeout_stream to 120 seconds once traffic is flowing both ways. A game that sends nothing for longer than that loses its entry, and the next inbound packet is treated as new. Games send keepalives partly for this reason.

Home routers do the same thing, worse. A NAT binding for UDP expires quickly, which is why a player behind certain routers drops out of a quiet lobby. On the server side, if you are hosting from home, a port forward for UDP is a separate rule from a port forward for TCP, and CGNAT means you may not be able to forward anything at all. Self-hosting at home versus renting is the honest comparison.

On a panel-based host, none of this is yours to configure: each plan states its allocations, and the Network tab adds or removes ports with query and RCON included. The protocol is part of the allocation rather than something you get wrong.

Why you cannot put a game behind a normal reverse proxy#

This is the question that arrives about once a week, usually phrased as "can I put my Minecraft server behind Cloudflare".

An HTTP reverse proxy - nginx in its normal mode, Caddy, Traefik, or the orange cloud in front of a website - terminates a TCP connection, reads an HTTP request, and makes its own request to your backend. Every part of that sentence assumes TCP and HTTP. A game sending UDP datagrams with its own binary protocol offers neither, so there is nothing for an HTTP proxy to do. What a reverse proxy does is the general explanation, and Cloudflare for websites and game servers is specific about where the line falls.

What does exist:

  • Layer 4 TCP proxying. nginx's stream module, HAProxy in TCP mode, or a game-specific proxy such as Velocity for Minecraft. This works for TCP games and is genuinely useful: a Velocity network puts several backends behind one public port.
  • UDP proxying. nginx's stream module can forward UDP with listen 25565 udp; and a proxy_pass, and it works for simple cases. The catch is the source address: without transparent proxying the game server sees the proxy's address for every player, which breaks bans, logs and anything per-player.
  • Vendor relay networks. Steam Datagram Relay and similar services carry UDP over an operator's own backbone and hide the server address. They are part of the game's platform rather than something you can bolt on.

The practical summary: a web front end can hide behind a proxy, a game server on UDP cannot, and if you run both, they should not share an address. On RE:NODE the proxy slot on app and web plans is exactly this HTTP-shaped thing - point an A record at the address shown and the certificate is issued and renewed automatically, with the real client address arriving in X-Forwarded-For. It is for your website and your API, not for the game port.

Query ports, RCON and the mixed case#

Most games run more than one protocol at once, and knowing which is which saves an afternoon.

The game port carries gameplay and is whatever the game chose. The query port answers server browsers and listing sites with the player count, map and name. In the Source family that is the A2S protocol over UDP, usually on the same number as the game. In Minecraft there are two: a TCP status ping on the game port, which is what the in-game server list uses, and an optional UDP query protocol enabled with enable-query=true and query.port in server.properties, which is what external status sites use.

RCON is remote administration and is almost always TCP, because a command must arrive exactly once and in order. Minecraft's is 25575, Source games use the game port number over TCP, Project Zomboid and several others default to 27015. It is the one port you should treat as sensitive - see using RCON safely.

DNS knows about the distinction too. An A record is protocol-agnostic: it maps a name to an address and says nothing about ports or protocols. An SRV record includes both, which is why a Minecraft SRV record is written as _minecraft._tcp.example.com and why the equivalent for a UDP game would name _udp. SRV records for Minecraft covers the one case where this is routinely used.

Testing a UDP port properly#

Here is where most diagnosis goes wrong. You cannot telnet a UDP port, and nc -z -u is nearly useless, because silence is the normal response to a UDP packet that was delivered successfully. Absence of a reply proves nothing at all.

Start on the server, where the answer is unambiguous:

bash
$ ss -lunp | grep 27015        # is anything listening on UDP?$ ss -ltnp | grep 25575        # and the TCP side$ ss -lunp

ss -lunp lists listening UDP sockets with the process that owns them. If your game is not in that list, the problem is the game's configuration, not the network, and no firewall change will help. That single command resolves more "port not working" tickets than anything else.

If the socket is there, watch whether packets arrive at all:

bash
$ tcpdump -n -i any udp port 27015$ tcpdump -n -i any udp port 27015 -c 20 -w /tmp/capture.pcap

Traffic arriving but no reply means the game is not answering, which is a game problem. No traffic arriving means it is being dropped before you, which is a firewall or upstream problem.

From outside, the honest options are limited:

bash
$ nmap -sU -p 27015 203.0.113.10        # needs root, and is slow

nmap reports open|filtered when nothing comes back, and it means exactly what it says: either the port is open and the service chose not to answer, or something dropped the probe. The only definitive external test is a client that speaks the game's own protocol - the game itself, a server query tool, or a public status checker for that game. If the query port is reachable, a listing site can see the server, and that is the answer you actually wanted.

One more UDP-specific gotcha: a closed UDP port normally replies with an ICMP port-unreachable message, so a host that drops ICMP makes every closed port look filtered. This is also why some games behave oddly on networks with aggressive ICMP filtering, and why "ping works but the game does not" and "ping fails but the game works" are both entirely possible sentences.

FAQ#

Why do games use UDP instead of TCP?

Because a late packet is worse than a lost one. TCP guarantees ordered delivery, so one lost packet stalls everything behind it for at least a round trip while it is retransmitted, by which time the information is obsolete. UDP delivers what arrives when it arrives, and the game fills gaps with prediction and interpolation.

Is UDP less reliable than TCP?

UDP itself offers no guarantees, but the games using it implement their own, selectively: important events are acknowledged and retransmitted inside the game, while position updates are allowed to be lost because a newer one is already on its way. The result is more suitable for gameplay, not less reliable in practice.

Do I need to open both TCP and UDP?

It depends on the game and it is worth checking rather than guessing. Most games need UDP for gameplay and TCP only for RCON or a web interface. A few, including 7 Days to Die, FiveM, Satisfactory and BeamMP, genuinely use both on the same number. Opening only TCP for a UDP game produces a server that runs and cannot be joined.

Can I put my game server behind Cloudflare?

Not the standard proxy, which handles HTTP and HTTPS over TCP. A game sending UDP has nothing for it to terminate. Use it for your website and keep that on a different address from the game, because the website is usually how people find the game address.

Why does telnet say the port is closed when the server works?

Because telnet speaks TCP and the game port is UDP. The test is meaningless. Check ss -lunp on the server to see whether anything is listening, then test from outside with a tool that speaks the game's own query protocol.

Does UDP use less bandwidth than TCP?

A little, from the smaller header and the absence of acknowledgements, but the saving is minor next to the traffic the game itself generates. Bandwidth is rarely the constraint on a game server anyway - bandwidth and fair use works through what a player actually costs per month.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000