toolhq.io

All posts
July 8, 20268 min read

TCP vs UDP: differences, ports, and when each one wins

Everything you send over the internet rides inside IP packets, and IP promises almost nothing: packets may arrive out of order, duplicated, or not at all. TCP and UDP are the two transport layers that sit on top and answer the same question differently: how much of that mess should be fixed before your application sees the data?

TCP fixes all of it and charges you latency. UDP fixes none of it and gets out of the way. Every protocol you use daily picked a side based on that trade.

The core difference

TCP (Transmission Control Protocol) is connection oriented and reliable. Before data flows, client and server complete a three way handshake (SYN, SYN-ACK, ACK). Every byte is numbered, acknowledged, and retransmitted if lost. Data arrives in order, exactly once, or the connection errors out. TCP also paces itself: congestion control slows the sender when the network is struggling.

UDP (User Datagram Protocol) is connectionless. You address a datagram and send it. There is no handshake, no acknowledgment, no retransmission, no ordering, and no congestion control. The entire UDP header is 8 bytes. If the packet is lost, nobody tells you.

TCPUDP
Connectionhandshake firstnone, just send
Reliabilityguaranteed deliverybest effort
Orderingstrict byte ordernone
Speed to first byteone round trip minimumimmediate
Header size20+ bytes8 bytes
Failure modestalls, retries, then errorssilent loss

Why anyone would choose unreliable

Because sometimes late data is worse than lost data.

In a video call or a game, a lost packet describes a moment that has already passed. Retransmitting it, as TCP insists on doing, means fresh data queues up behind stale data (head of line blocking) and the whole stream stutters. Better to drop the frame and move on, which is exactly what UDP permits. The application layers its own recovery on top only where it is worth it.

The second reason is the round trip. A TCP connection costs a full handshake before the first byte of data, which matters enormously for tiny exchanges. A DNS query is one small packet out and one small packet back; wrapping that in connection setup and teardown would triple the work. So DNS runs on UDP port 53 by default, and a typical lookup completes in a single round trip. You can watch these single-packet answers come back with the DNS Lookup tool.

DNS does fall back to TCP when a response is too large for one datagram (common with DNSSEC) or for zone transfers, which is why firewalls must leave TCP 53 open too, a detail that has caused many mysterious resolution failures.

Who uses which

TCP: HTTP/1.1 and HTTP/2 (ports 80 and 443), TLS as the web has long used it, SSH (22), SMTP (25, 587), IMAP (993), PostgreSQL (5432), MySQL (3306), and anything where every byte matters and order is meaningful.

UDP: DNS (53), DHCP (67/68), NTP (123), VoIP and WebRTC media, most game netcode, WireGuard VPN (51820), and QUIC.

The port numbers are independent namespaces: TCP 53 and UDP 53 are different sockets that happen to share a number. When a firewall rule says "open port 443" you still have to say for which transport.

QUIC: UDP eats the web

The most important recent development is HTTP/3, which abandons TCP entirely. It runs on QUIC, a protocol built on UDP that reimplements reliability, ordering, and congestion control in user space, but per stream rather than per connection. One lost packet no longer blocks every parallel request the way it does in TCP, and the handshake merges transport and TLS setup into a single round trip.

So the modern answer to "TCP or UDP?" has a twist: the web's newest protocol chose UDP precisely so it could rebuild TCP's guarantees with better granularity. Roughly a third of web traffic already flows this way. The same logic applies to WireGuard, which runs over UDP for similar reasons; the WireGuard vs OpenVPN comparison touches on why OpenVPN over TCP is usually the slowest option (TCP-over-TCP retransmission amplifies badly).

Practical debugging notes

  • UDP failures are silent. A blocked TCP port usually returns a clear connection refused or timeout. A blocked UDP port just eats packets, and the application sees nothing until its own timer expires. When DNS "hangs," this is often why.
  • Timeouts differ. TCP stalls show up as slow responses that eventually error. UDP loss shows up as retries at the application layer, visible as repeated identical queries.
  • NAT treats them differently. UDP "connections" in a NAT table expire quickly since there is no FIN to signal the end, which is why long lived UDP sessions (VoIP, VPNs, games) send keepalive packets.

For the bigger picture of where transport fits when a page loads, from DNS through TCP and TLS to the first byte, see what happens when you type a URL.