# TCP vs UDP

## 1. Two Transport-Layer Protocols, One Job: Deliver Data Between Processes

Both TCP and UDP sit at the transport layer (see the TCP/IP model topic), and both do the same basic addressing job: get data from a **process** on one host to a **process** on another, using **ports** to identify which process (their own topic). Beyond that, they make opposite tradeoffs.

| | TCP | UDP |
|---|---|---|
| Full name | Transmission Control Protocol | User Datagram Protocol |
| Connection | Connection-oriented — handshake required before data flows | Connectionless — just send |
| Reliability | Guaranteed delivery, retransmits lost data | No guarantees — lost data stays lost |
| Ordering | Guaranteed in-order delivery | No ordering guarantee |
| Duplication | Guaranteed no duplicates | Duplicates possible |
| Flow control | Yes (sliding window) | No |
| Congestion control | Yes (slows down under network congestion) | No |
| Header overhead | 20 bytes minimum | 8 bytes |
| Speed / latency | Slower to start (handshake), overhead per packet | Faster — no setup, minimal overhead |
| Typical uses | HTTP/1.1, HTTP/2, TLS, file transfer, databases, email | DNS, video/voice calls, live streaming, gaming, HTTP/3 (QUIC, over UDP) |

## 2. Why Both Still Exist

It's tempting to think TCP is strictly "better" since it adds guarantees UDP lacks — but those guarantees cost time and bandwidth. If a video call drops one frame, retransmitting it arrives too late to be useful anyway — the call should just keep going with the next frame (UDP's model). If a bank transfer drops one byte, that's unacceptable — it must be retransmitted and delivered correctly no matter the cost (TCP's model). The choice between them is a real engineering tradeoff based on whether **correctness** or **timeliness** matters more for a given use case.

## 3. Where Each Is Actually Used

- **TCP**: virtually all web traffic through HTTP/1.1 and HTTP/2, TLS handshakes, SSH, database connections (Postgres, MySQL), email (SMTP/IMAP) — anywhere silently losing or reordering bytes would corrupt the application's data.
- **UDP**: DNS queries (a single small request/response — cheaper to just retry than to hold a connection open), VoIP and video calls (a late packet is worse than a dropped one), online gaming (same reasoning), and increasingly **HTTP/3**, which is built on **QUIC** — a protocol that runs over UDP but reimplements TCP-like reliability *in user space* to avoid some of TCP's specific limitations (this is covered in the HTTP versions topic).

## Under the Hood

### The TCP three-way handshake — the actual mechanics

![TCP three-way handshake: SYN, SYN-ACK, ACK exchanging sequence numbers before data flows](diagrams/01-tcp-three-way-handshake.svg)

Before any data flows, TCP performs a handshake to synchronize both sides' **sequence numbers** — the counters each side uses to track exactly which bytes have been sent and received:

1. **SYN**: the client picks a random initial sequence number `x` and sends a segment with the `SYN` flag set and `seq=x`. This says "I want to open a connection, and my byte stream starts counting from x."
2. **SYN-ACK**: the server picks its own random initial sequence number `y`, and replies with both `SYN` and `ACK` flags set: `seq=y, ack=x+1`. The `ack=x+1` acknowledges it received the client's SYN (the "+1" is because a SYN itself consumes one sequence number); the `seq=y` starts the server's own byte counting.
3. **ACK**: the client replies with `ACK`, `ack=y+1`, acknowledging the server's SYN. No `SYN` flag this time — the handshake is over.

After this exchange, both sides know the other's starting sequence number, and both have confirmed the network path works in both directions before a single byte of actual application data has been sent. This is real cost — an extra full round trip before any data flows — which is exactly why TCP connection reuse (keep-alive) and TLS session resumption exist: to avoid repeating this cost on every request.

### How TCP achieves reliability, concretely

- **Sequence numbers**: every byte TCP sends is numbered. The receiver can detect exactly which bytes it has and hasn't received, and reassemble out-of-order segments into the correct order using these numbers — this is the actual mechanism behind "guaranteed ordering," not magic.
- **Acknowledgments (ACKs)**: the receiver continuously tells the sender the sequence number of the next byte it expects. If the sender doesn't receive an ACK for data within a timeout (or receives duplicate ACKs signaling a gap), it concludes that data was lost.
- **Retransmission**: on a missing/timed-out ACK, TCP resends the unacknowledged data. This is the actual mechanism behind "guaranteed delivery" — it's not that packets can't be lost on the wire (they still can, IP is still best-effort underneath), it's that TCP detects the loss and resends until it's confirmed received.
- **Sliding window flow control**: the receiver advertises a "window size" — how many unacknowledged bytes it's currently willing to buffer. The sender must not send more than that before getting an ACK. This prevents a fast sender from overwhelming a slow receiver's buffer, and the window slides forward as ACKs arrive, allowing more data to be sent.
- **Congestion control**: separately from flow control (which protects the *receiver*), TCP also tracks network congestion (protecting the *network path*) via algorithms like slow start and congestion avoidance, ramping the send rate up gradually and backing off sharply on detected loss.

### What UDP genuinely does not have

UDP is not "TCP with fewer features enabled" — it structurally lacks all of the above. A UDP datagram is sent with an 8-byte header (source port, destination port, length, checksum) and nothing more:

- No handshake — the very first datagram sent can contain application data immediately.
- No sequence numbers in the UDP header itself — if datagrams arrive out of order, out of order is exactly how the application receives them, unless the application layer adds its own sequencing (which real-time protocols like RTP do, entirely at the application layer, not inside UDP).
- No ACKs, no retransmission — a dropped datagram is gone; UDP never notices and never resends it.
- No flow or congestion control — a UDP sender can blast datagrams as fast as the application asks, with no built-in backpressure; this is also why misbehaving UDP applications can be more dangerous to shared network links than misbehaving TCP applications, which self-throttle under congestion.

This is precisely why protocols that need TCP-like reliability but want more control than TCP gives (like QUIC, underlying HTTP/3) build their own reliability layer **on top of UDP** in user-space code, rather than using raw UDP directly — they want UDP's lack of head-of-line blocking at the OS/kernel level, while still implementing their own sequencing and retransmission logic tailored to their needs.

## Common Pitfalls

1. **Assuming UDP is "unreliable" in the sense of "broken"** — it's reliable in the sense that it correctly does exactly what it promises (best-effort delivery, no more); it's a deliberate design choice, not a defect.
2. **Assuming TCP guarantees data arrives, period** — TCP guarantees that if the *connection* completes normally, delivered data arrived correctly and in order; it cannot guarantee delivery if the connection is abruptly killed (e.g., cable unplugged) — the application layer must still handle that failure mode.
3. **Forgetting the handshake cost is a real, measurable latency hit** — one extra round trip before data flows matters a lot for short-lived, latency-sensitive connections (this is a major part of why HTTP/2 pushed connection reuse, and why HTTP/3 moved to QUIC/UDP to reduce handshake round trips further — see the HTTP versions topic).
4. **Believing flow control and congestion control are the same mechanism** — flow control protects the *receiver's buffer*; congestion control protects the *shared network path*; TCP runs both, independently.
5. **Thinking UDP can't be reliable at all** — reliability can be built on top of UDP at the application layer (as QUIC/HTTP-3 and RTP do); UDP itself just doesn't provide it for free.

## Quick Reference

```
TCP: connection-oriented, reliable, ordered, flow+congestion controlled, higher overhead
     3-way handshake: SYN → SYN-ACK → ACK
     reliability = sequence numbers + ACKs + retransmission + sliding window

UDP: connectionless, best-effort, no ordering, no flow/congestion control, minimal overhead
     no handshake — first datagram can carry data immediately

Use TCP when correctness > timeliness (web, DB, file transfer, TLS)
Use UDP when timeliness > correctness (voice/video, gaming, DNS, QUIC/HTTP-3)
```
