Vasu Patel logoVasu Patel
← How the Bytes Travel: Networking
All chapters in this track
  1. 01Chapter 1: Why Networking Fundamentals Matter
  2. 02Chapter 2: The OSI Model and TCP/IP Stack, at a Working Level
  3. 03Chapter 3: Sockets, Ports, and the Connection Lifecycle
  4. 04Chapter 4: TCP vs UDP: Choosing the Right Transport
  5. 05Chapter 5: DNS Resolution, Step by Step
  6. 06Chapter 6: The HTTP Family: HTTP/1.1, HTTP/2, HTTP/3, and WebSockets
  7. 07Chapter 7: TLS/SSL and Certificates: The Handshake and Why HTTPS Matters
  8. 08Chapter 8: Load Balancers, Reverse Proxies, and CDNs
  9. 09Chapter 9: Latency, Bandwidth, and Congestion Control
  10. 10Chapter 10: API Protocol Choices: REST vs. gRPC vs. GraphQL, from a Networking Angle
  11. 11Chapter 11: Common Failure Modes: Timeouts, Connection Resets, Retry Storms, Thundering Herd
  12. 12Chapter 12: Where Networking Abstractions Leak: NAT, Middleboxes, and Other Surprises
  13. 13Chapter 13: Full Walkthrough: What Happens When You Type a URL and Press Enter
  14. 14Chapter 14: Common Networking Interview Questions
  15. 15Chapter 15: Networking Concepts in System Design and Production Debugging

Chapter 4 of 15

Chapter 4: TCP vs UDP: Choosing the Right Transport

Why this matters

TCP and UDP are the two dominant transport-layer protocols, and the choice between them is one of the few genuinely foundational, hard-to-reverse decisions in system design — it shapes everything built on top of it. Most engineers know "TCP is reliable, UDP is fast," but the real trade-off is more specific and more interesting than that soundbite, and understanding it properly explains why voice calls, video games, and DNS all reach for UDP despite it being "less reliable."

TCP: reliable, ordered, connection-oriented

[BEGINNER] TCP (Transmission Control Protocol) guarantees that data arrives complete, in order, and without duplication — or the application is told the connection failed. It achieves this via the three-way handshake (Chapter 3), sequence numbers on every byte sent, acknowledgments from the receiver, and automatic retransmission of anything not acknowledged within an expected time.

Sender sends packets 1, 2, 3, 4, 5.
Packet 3 is lost in transit.
Receiver acknowledges 1, 2, then notices a gap — packet 3 never arrives.
Sender doesn't receive an ack for 3 within the expected time, and RETRANSMITS it.
Receiver reassembles 1, 2, 3, 4, 5 in the CORRECT ORDER before handing data to the application —
the application never even knows a packet was lost and resent; TCP hides this entirely.

This reliability isn't free — it costs the connection setup round-trip (Chapter 3), ongoing acknowledgment traffic, and, critically, head-of-line blocking: if packet 3 is lost, packets 4 and 5 (which arrived just fine) still can't be delivered to the application until 3 is retransmitted and arrives, because TCP guarantees strict ordering.

UDP: fast, connectionless, no guarantees

[BEGINNER] UDP (User Datagram Protocol) sends discrete packets ("datagrams") with no handshake, no ordering guarantee, no retransmission, and no guarantee of delivery at all — a packet might arrive, arrive out of order, arrive duplicated, or simply never arrive, and UDP itself does nothing about any of these possibilities. In exchange, UDP has essentially no overhead beyond the data itself and a minimal header — no connection setup, no waiting for acknowledgments, no head-of-line blocking.

import socket

# UDP: no connect() needed, no handshake, just send.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b"a single, fire-and-forget datagram", ("example.com", 9999))
# No guarantee this arrives. No guarantee it arrives only once. No guarantee of order
# relative to any other datagram sent. This is not a bug — it's UDP's explicit contract.

Why UDP is the right choice for some things, not a worse choice for everything

[SENIOR INSIGHT] The key insight that "TCP is reliable, UDP is fast" misses: for some applications, TCP's reliability mechanism actively makes things worse, not just "unnecessary overhead." Consider a live voice call: if a packet representing 20 milliseconds of audio is lost, the correct behavior is to skip it and keep playing subsequent audio — a listener won't even notice a tiny gap. TCP's insistence on retransmitting the lost packet and delivering everything in strict order means that if packet 3 is lost, packets 4 and 5 (already received, already "late" by the time they'd be useful) are held back waiting for a retransmission of packet 3 that, once it finally arrives, describes audio that's now old news. For real-time media, a late packet is often worse than a lost one — exactly the opposite of TCP's guarantee, which is why voice/video calling protocols (like those built on RTP) run over UDP and implement their own, application-specific handling of loss (often: just accept it and move on, or use forward error correction) rather than TCP's blocking retransmission.

Use caseRight transportWhy
Loading a web pageTCPEvery byte of HTML/CSS/JS must arrive completely and correctly, or the page is broken — reliability matters more than raw speed here
A file downloadTCPSame reasoning — a corrupted or incomplete file is useless
A live voice/video callUDPA late packet is worse than a lost one; the application handles loss itself, gracefully
Online multiplayer gamesUDP (often with custom reliability layered on top for specific critical events)Player position updates are only useful if current — an old, retransmitted position update is stale and worthless by the time it arrives
DNS queriesUDP (with fallback to TCP for large responses)A single small request/response pair doesn't benefit from TCP's connection setup overhead; if a DNS response is lost, the resolver simply retries the whole query, which is cheap and simple at this scale
Video/audio streaming (as opposed to live calls)Often TCP-based (via HTTP, since most video streaming today is chunked HTTP downloads, not literally UDP)Buffering absorbs latency variance, so reliability matters more here than in a live call, despite both being "media"

QUIC: getting some of both

[IN PRACTICE] QUIC (the transport protocol underlying HTTP/3, covered in Chapter 6) is a modern attempt to combine UDP's low-latency, no-head-of-line-blocking properties with TCP-like reliability — built on top of UDP, but implementing its own reliability, ordering (per-stream, avoiding TCP's connection-wide head-of-line blocking), and congestion control (Chapter 9) at the application layer instead of relying on the kernel's TCP implementation. This is a direct, practical answer to TCP's head-of-line blocking limitation, achieved by choosing UDP as a foundation specifically because it doesn't impose TCP's ordering guarantee, then rebuilding a smarter, more selective version of that guarantee on top.

Common mistakes

  1. Choosing UDP purely because "it's faster" without asking whether the application actually tolerates loss and reordering gracefully. If the application has no strategy for missing/out-of-order data, UDP just relocates TCP's reliability problem into the application without solving it — often worse, since an ad hoc reliability layer is likely less robust than TCP's well-tested one.
  2. Choosing TCP by default for every case without considering whether head-of-line blocking specifically hurts the use case (real-time media, competitive gaming) — this is the scenario where "TCP is more reliable, so it's always the safer choice" is actually the wrong instinct.
  3. Not recognizing DNS's UDP-with-TCP-fallback design as a deliberate, sensible choice (Chapter 5 covers DNS in full) — small, simple queries over UDP for speed, falling back to TCP specifically when a response is too large for a single UDP datagram.
  4. Assuming "unreliable" means "broken" or "shouldn't be used in production." UDP is not a lesser protocol — it's a different, deliberate trade-off, used extensively and correctly in mainstream production systems for exactly the use cases where its trade-off fits.

Discussion / interview questions

  1. "When would you choose UDP over TCP for a new system, and why?" Strong answer: When the application can tolerate loss/reordering gracefully and a late-arriving retransmission would be worse than simply skipping missing data (real-time voice/video, live position updates in games) — not simply "when speed matters," since that framing misses the deeper reason.

  2. "Explain head-of-line blocking in TCP, and why it's a real cost even when the underlying network is otherwise healthy." Strong answer: TCP guarantees strict in-order delivery to the application; if one packet is lost, later packets that already arrived successfully still can't be delivered until the lost one is retransmitted and received — a real latency cost imposed by the reliability guarantee itself, not by network health.

  3. "Why does DNS primarily use UDP instead of TCP?" Strong answer: DNS queries and responses are typically small, single request/response exchanges — TCP's connection setup overhead (Chapter 3's three-way handshake) isn't worth paying for such a small exchange, and if a UDP response is lost, simply retrying the whole (cheap) query is simpler than TCP's retransmission machinery; DNS falls back to TCP specifically when a response is too large for one UDP datagram.

  4. "How does QUIC address TCP's head-of-line blocking problem while still providing reliability?" Strong answer: QUIC is built on UDP (avoiding TCP's kernel-enforced connection-wide ordering) and implements its own reliability and per-stream ordering at a higher layer — so loss on one stream doesn't block delivery of data on other independent streams within the same connection, unlike TCP's single ordered byte stream.

Chapter summary / cheat sheet

  • TCP: reliable, ordered, connection-oriented — guarantees delivery and order at the cost of connection setup, acknowledgment overhead, and head-of-line blocking.
  • UDP: connectionless, no guarantees, minimal overhead — the application is responsible for handling loss/reordering itself, which is exactly the right trade-off for real-time media and other cases where a late packet is worse than a lost one.
  • The real decision criterion isn't "reliability vs. speed" — it's whether TCP's specific guarantee (strict in-order, complete delivery) helps or actively hurts the use case.
  • QUIC (HTTP/3's transport, Chapter 6) builds UDP-based, per-stream reliability specifically to avoid TCP's head-of-line blocking while still providing strong guarantees where they're needed.