TCP vs UDP
Connection-oriented vs connectionless, flow control, congestion control
TCP (Transmission Control Protocol) provides reliable, ordered, connection-oriented delivery using a three-way handshake (SYN → SYN-ACK → ACK), flow control (receive window), and congestion control (CUBIC, BBR). UDP (User Datagram Protocol) is connectionless with no delivery guarantees, ordering, or flow control — trading reliability for raw speed and simplicity. Modern protocols like QUIC (HTTP/3) implement reliability and multiplexing at the application layer over UDP, combining the benefits of both.
Key Points
- TCP three-way handshake adds 1.5 RTT before data can be sent; TLS 1.3 adds another 1 RTT — total connection setup cost is 2.5 RTT before the first byte of application data.
- TCP receive window: default ~65 KB; scaled via TCP Window Scaling to up to 1 GB — on high-bandwidth, high-latency links (satellite), a small window becomes the bottleneck (bandwidth-delay product).
- TCP congestion control: slow start doubles cwnd each RTT until ssthresh; CUBIC (default Linux) uses cubic function for window growth; BBR (Google) models bandwidth-delay product instead of loss.
- TCP TIME_WAIT (2 × MSL, typically 60–120 s) prevents reuse of port tuples for stale segments — high connection rate servers can exhaust ephemeral ports (32,768–60,999 on Linux); tune with SO_REUSEADDR.
- UDP packet size: keep payloads ≤1,472 bytes (Ethernet MTU 1500 − IP header 20 − UDP header 8) to avoid IP fragmentation, which causes severe performance degradation.
- UDP is used where loss is preferable to delay: DNS queries (single round-trip tolerable), DHCP, VoIP (RTP over UDP), video streaming (RTCP), online gaming, and real-time financial data feeds.
- QUIC (RFC 9000) runs over UDP port 443, implements multiplexed streams, 0-RTT connection resumption, and built-in TLS 1.3 — eliminating TCP's head-of-line blocking at the transport layer.
- TCP keepalive: if not configured, TCP connections can remain half-open indefinitely — set tcp_keepalive_time (~60 s in production) to detect failed peers and free resources.
| Property | TCP | UDP | QUIC (HTTP/3) |
|---|---|---|---|
| Connection setup | 3-way handshake (1.5 RTT) | None (0 RTT) | 0-RTT (resumed) / 1-RTT (new) |
| Reliability | Guaranteed (ACKs + retx) | Best-effort | Per-stream reliability |
| Ordering | Strict in-order delivery | No ordering | Per-stream ordering |
| Head-of-line blocking | Yes (stream level) | No | No (streams are independent) |
| Flow control | Yes (receive window) | No | Yes (per-stream) |
| Congestion control | CUBIC / BBR / Reno | None built-in | BBR (in most impls) |
| Typical latency added | 1.5+ RTT setup | ~0 ms | 0–1 RTT setup |
| Use cases | HTTP/1-2, SSH, SMTP, FTP | DNS, VoIP, Gaming, RTP | HTTP/3, WebRTC data |
| Header size | 20–60 bytes | 8 bytes | Variable (~1,200–1,400 bytes initial) |
| Multiplexing | No (one stream) | Application layer only | Yes (built-in streams) |
Real-World Example
Google reported a 15% improvement in YouTube video buffering and 3% reduction in search latency after switching to QUIC — primarily because QUIC's per-stream loss recovery avoids TCP's connection-level head-of-line blocking on lossy mobile networks.