TCP vs UDP
Reliable-ordered vs fire-and-forget — and when you want the second.
In 1974, Vint Cerf and Bob Kahn published a paper titled "A Protocol for Packet Network Intercommunication." It described a single unified protocol they called TCP — Transmission Control Protocol. The idea: a computer sends a stream of bytes to another computer, and the protocol guarantees they arrive in order, with no duplicates, no missing bytes, and no corruption. If a packet is dropped by the network, TCP retransmits it. If two packets arrive out of order, TCP reorders them. If a network is congested, TCP slows down.
Six years later, in 1980, Jon Postel wrote RFC 768 — a 3-page specification for a different protocol called UDP — User Datagram Protocol. UDP does exactly one thing: sends a packet from one process to another. No retransmit. No ordering. No congestion control. No connection. If a packet is dropped, it's gone. If two packets arrive out of order, tough. UDP is 8 bytes of header wrapped around your data.
Why would anyone use UDP? Because for some workloads, reliability costs more than it's worth. Video streaming: a dropped frame from 2 seconds ago is worse than useless — it's stale. Online gaming: a re-sent "player position" from 100ms ago is nonsense. DNS: a single UDP packet is faster than 3 round trips of TCP handshake for a 40-byte query. When latency matters more than reliability, UDP wins. When reliability matters more than latency, TCP wins.
The papers
- Cerf & Kahn (1974) — "A Protocol for Packet Network Intercommunication." The paper that won them the Turing Award in 2004.
- RFC 793 (1981) — the definitive TCP specification.
- RFC 768 (1980, Postel) — the entire UDP specification. 3 pages.
- RFC 9000 (2021) — QUIC. A new UDP-based transport that combines TCP's reliability with UDP's speed. Powers HTTP/3.
What layer? Both are Layer 4
TCP and UDP are both Layer 4 (Transport) protocols in the OSI model. They sit above IP (Layer 3) and below the application (Layer 7). Their job is the same: get data from a process on one host to a process on another host. They just do it differently.
The TCP 3-way handshake — how a connection starts
Every TCP connection begins with a 3-way handshake. This is why the very first packet of an HTTP/1.1 or HTTP/2 request has ~1 round-trip of latency before any real data flows.
The handshake exchanges initial sequence numbers (ISNs) so both sides can track what's been sent, received, and needs retransmit. It also lets each side confirm the other is reachable and willing to talk. The cost: 1 RTT of latency before your HTTP GET even leaves the client. On transatlantic links (~80ms RTT), that's 80ms of overheadper connection. This is why HTTP/1.1 introduced keep-alive (reuse connections), why HTTP/2 multiplexes many streams per connection, and why HTTP/3 moved to UDP-based QUIC (which combines handshake with TLS handshake for 0-RTT).
TCP reliability — how it works
TCP provides reliable, ordered, exactly-once byte delivery over an inherently unreliable network. Four mechanisms make this work:
- Sequence numbers — every byte has a unique 32-bit sequence number. Receiver can detect gaps and duplicates.
- Acknowledgments (ACKs) — receiver periodically sends back "I've got everything up to byte N." Sender retransmits anything not ACKed within a timeout (RTO — Retransmission Timeout).
- Sliding window flow control — receiver tells sender "I have room for the next 64 KB right now." Sender never overwhelms receiver.
- Congestion control — sender starts slow, ramps up until packet loss is detected, then backs off. CUBIC (Linux default 2004+) and BBR (Google, 2016+) are the modern algorithms.
UDP — everything TCP is not
UDP has none of TCP's mechanisms. No handshake. No sequence numbers. No ACKs. No retransmit. No flow control. No congestion control. Just 8 bytes of header (source port, destination port, length, checksum) around your data. You send a UDP packet; it arrives, or it doesn't. The kernel doesn't track it. There's no connection state.
UDP's minimalism is its power. A UDP packet has no setup cost — first byte can be application data. UDP has no head-of-line blocking — if packet 47 is dropped, packet 48 is still delivered immediately. UDP has no congestion control — but modern UDP-based protocols (QUIC, WebRTC) implement their own, smarter than TCP's.
Side by side — the head-to-head
| Dimension | TCP | UDP |
|---|---|---|
| Connection setup | 3-way handshake (SYN → SYN-ACK → ACK) | None — first packet is data |
| Header size | 20 bytes (min), can go to 60 with options | 8 bytes |
| Reliability | Guaranteed — retransmits lost packets | None — dropped packets are lost forever |
| Ordering | Guaranteed — receiver reorders packets | None — packets arrive in any order |
| Duplicate detection | Yes (sequence numbers) | None |
| Flow control | Yes (sliding window) | None |
| Congestion control | Yes (CUBIC, BBR, Reno, …) | None (but QUIC adds its own) |
| Head-of-line blocking? | Yes — one lost packet stalls the stream | No — every packet independent |
| Latency (steady state) | 1 RTT setup + per-packet ~RTT | 0 setup + per-packet ~RTT |
| Latency (packet loss) | Adds ~1 RTO (~100-500ms) | No penalty (packet just gone) |
| State per connection | Sequence, window, RTT, congestion state | None |
| Uses | HTTP/1.1, HTTP/2, SSH, SMTP, databases, file transfer | DNS, video streaming, VoIP, gaming, NTP, SNMP, QUIC |
When to pick each — a real-world guide
QUIC — the third option (2021)
For 40 years the choice was binary: TCP or UDP. In 2021, Google shipped QUIC (Quick UDP Internet Connections, RFC 9000) — a new transport protocol built on UDP that gives you TCP's reliability + ordering + flow control plus UDP's absence of head-of-line blocking, 0-RTT connection setup, and integrated TLS 1.3.
QUIC lives in userspace, not the kernel — so it can be updated faster (Google ships QUIC changes weekly to Chrome). QUIC streams are independent — if one stream stalls, others keep flowing. QUIC survives IP changes (your phone switches from WiFi to LTE, QUIC keeps the connection going via a connection ID). HTTP/3 runs entirely on QUIC.
Applied in real systems — a lot of examples
The choice between TCP and UDP shows up in every system design. Here's a wide sample of real-world usage.
HTTP → TCP
Every HTTP/1.1 and HTTP/2 request runs on TCP. Reliability is essential — you can't have missing bytes in an HTML page. Ordering is essential — bytes must arrive in order. This is why HTTP has always been TCP. Until HTTP/3.
HTTP/3 → QUIC (on UDP)
HTTP/3 abandoned TCP. It runs on QUIC, which runs on UDP. This solves TCP head-of-line blocking: with HTTP/2 on TCP, one dropped packet stalls every stream. With HTTP/3 on QUIC, only the affected stream stalls.
Netflix → TCP (HLS/DASH over HTTPS)
Surprising: Netflix streams video over TCP, not UDP. Because it's not live (it's pre-encoded), you can afford small pauses to retransmit. Netflix uses Adaptive Bitrate over HTTPS chunks. Netflix Open Connect serves this at the ISP edge.
Zoom → UDP (RTP over UDP)
Real-time video is UDP. Zoom uses SRTP (Secure Real-Time Protocol) over UDP for the audio/video. Dropped frames are tolerated (video briefly glitches, audio has a click) — but retransmit would be worse (out-of-sync audio and video).
DNS → UDP (mostly)
DNS queries fit in a single ~50-byte UDP packet. Response in another. Round-trip: 1. TCP would need 3 round-trips (handshake + query). That's why DNS defaults to UDP. DNS uses TCP only for large responses (>512 bytes) or zone transfers.
DNS over HTTPS (2018) → TCP
DoH (RFC 8484) runs DNS queries over HTTPS on TCP for privacy (ISPs can't spy). Cloudflare 1.1.1.1 and Google 8.8.8.8 both offer DoH. Trades DNS's traditional UDP speed for privacy.
Fortnite, LoL, CS:GO → UDP
Every competitive online game uses UDP. Player positions update 60x per second. Missing one update is fine (interpolate). Retransmitting a stale one is worse than useless. Games implement their own lightweight reliability layer on top of UDP for critical events (kills, chat messages).
Databases → TCP
Every SQL query hits the database over TCP. The queries and responses must be reliable and ordered — you can't tolerate a corrupted or reordered result set. Port 5432 (Postgres), 3306 (MySQL), 27017 (MongoDB). Some newer stores (Aerospike, ScyllaDB) offer UDP for reads with in-app retry.
SSH → TCP
Every SSH connection is TCP on port 22. Missing bytes in a shell session would be catastrophic. Newer Mosh uses UDP for lower-latency mobile connections (survives IP changes) with a smart resync layer on top.
Kafka producers/consumers → TCP
Every Kafka client-broker connection is TCP. Kafka's at-least-once and exactly-once semantics are only possible because TCP guarantees byte delivery. Broker replication is also TCP.
WebRTC media → UDP (SRTP)
WebRTC (Chrome, Meet, browser video calling) uses UDP for media. Data channels can use either. WebRTC negotiates via ICE/STUN/TURN to find working UDP paths through NATs.
Google services → QUIC (UDP-based)
Google migrated the majority of their client traffic to QUIC starting in 2013 (it was called gQUIC internally before IETF standardized it in 2021). YouTube, Search, Maps, Gmail all prefer QUIC when the client supports it. ~40% of internet traffic today is QUIC.
NTP → UDP
NTP (Network Time Protocol) uses UDP because clock sync must be sub-millisecond precise — TCP's handshake would introduce more error than the sync itself is trying to correct.
SNMP, syslog → UDP
Network monitoring uses UDP because reliability is not worth the overhead — if a monitoring metric is dropped, the next one arrives in 15 seconds. Datadog agent, Prometheus node_exporter, and syslog all default to UDP.
Key takeaways
- Both are Layer 4 (Transport). TCP and UDP both sit above IP and below your application. Ports are their addressing scheme.
- TCP = reliable, ordered, connection-oriented. 3-way handshake, sequence numbers, ACKs, retransmit, congestion control. Adds ~40 bytes header + 1 RTT setup.
- UDP = fire-and-forget, connectionless. 8 bytes header, no setup. Packet delivered or not — no promises.
- Pick TCP when reliability > latency: HTTP, databases, SSH, file transfer, email. Pick UDP when latency > reliability: real-time video, gaming, DNS, NTP.
- QUIC (2021) is a third option — reliability + speed by running on UDP with its own smarter reliability layer. HTTP/3 uses QUIC. ~40% of internet traffic now.
- Head-of-line blocking is TCP's Achilles heel: one dropped packet stalls every subsequent packet on that connection. QUIC / HTTP/3 solved this.
- TCP is slower to start (handshake) and slower under packet loss (retransmit + window shrinking). UDP has no such overhead.
References
- Cerf & Kahn (1974) — A Protocol for Packet Network Intercommunication. IEEE.
- RFC 793 (1981) — TCP specification.
- RFC 768 (1980) — UDP specification. 3 pages.
- Jacobson (1988) — "Congestion Avoidance and Control." The paper that made TCP work under congestion.
- RFC 9000 (2021) — QUIC v1 specification.
- Cardwell et al. (2016) — "BBR: Congestion-Based Congestion Control." Google's modern TCP congestion algorithm.
Practice what you just read
Every foundation concept has a companion quiz to close the loop.