TCP vs UDP: Concepts and Use Cases
Somewhere around 1980, a small group of engineers had to make a decision that every single video call, download, and multiplayer match you've ever used still lives inside of today. IP, the addressing layer underneath everything, had just been given a very honest but very limited job: get this packet from host A toward host B, best effort, no promises. It doesn't guarantee the packet arrives. It doesn't guarantee it arrives once. It doesn't guarantee it arrives in the order it was sent. IP is, in a real sense, a postal system that occasionally loses a letter, sometimes delivers two copies of it, and every so often hands you page three before page one — and shrugs. Someone had to decide what to build on top of that shrug, and instead of one answer, the internet ended up with two: TCP and UDP. Neither one apologizes for what it isn't. Both were built, deliberately, to be exactly what they are — which turns out to be the whole reason understanding them is worth an entire chapter.
Two Honest Answers to the Same Hard Question
TCP and UDP both live at the transport layer, directly above IP, and they exist to answer one specific question that IP refuses to answer: given that packets can vanish, duplicate, or arrive out of order, what guarantees — if any — does an application actually get? TCP's answer is almost stubborn in its thoroughness: "all of them, and I'll do the bookkeeping myself." UDP's answer is the opposite kind of honest: "none at all — if you need guarantees, build them yourself." Neither answer is wrong. They were designed for different jobs, and a surprising amount of real networking skill is simply recognizing, before you write a line of code, which of the two a given problem actually calls for.
TCP: The Reliable Courier
Think of TCP as a certified-mail service crossed with an obsessively organized secretary. Every package is tracked, numbered, delivered in order, and signed for; if something goes missing along the way, it gets resent automatically, without you ever having to ask. That reliability isn't a single feature — it's a small stack of mechanisms working together: a handshake that confirms both sides are actually listening before any real data moves, sequence numbers and acknowledgments that let a lost packet be detected and retransmitted, flow control so a fast sender never floods a slow receiver's buffer, and congestion control so the sender backs off the moment the network path itself — not just the receiver — starts to strain.
That handshake is the famous three-step exchange: SYN, SYN-ACK, ACK.
[Sender] ---(SYN)-------> [Receiver]
[Sender] <--(SYN-ACK)---- [Receiver]
[Sender] ---(ACK)-------> [Receiver]
[Sender] ---(Data 1)----> [Receiver]
[Sender] ---(Data 2)----> [Receiver]
[Receiver] <--(ACK 1)---- [Sender]
[Receiver] <--(ACK 2)---- [Sender]
TCP's flow control and congestion control are almost entirely invisible from the outside, which is exactly the point — an application just sends and receives bytes, while the operating system quietly handles pacing, retransmission, and reordering underneath. That invisibility came at a cost the internet learned the hard way. In October 1986, the young NSFNET suffered what's remembered today as congestion collapse: senders kept blindly retransmitting into an already-saturated network, and effective throughput between some sites fell by a factor of roughly a thousand within days, because every retransmission just added more fuel to a fire it was supposed to be putting out. An engineer named Van Jacobson diagnosed the problem and, in 1988, published the additive-increase, multiplicative-decrease algorithm that is still, in spirit, the ancestor of every congestion-control scheme TCP uses today — a sender that gently speeds up while things are calm, and slams the brakes hard the instant it senses trouble.
TCP shows up wherever correctness matters more than speed: web browsing, email, file transfers, database connections, SSH sessions — anywhere a missing or reordered byte would be a genuine problem rather than a minor inconvenience.
UDP: The Speedy Sprinter
If TCP is certified mail, UDP is a postcard: fast, cheap, no tracking number, no guarantee it arrives at all, and no apology if it doesn't. There's no handshake, no negotiated state, just "send this datagram and move on." Each write becomes exactly one packet on the wire, delivered whole or not delivered at all — nothing is split, nothing is merged, and there's no built-in mechanism for noticing loss, let alone fixing it. UDP also supports broadcast and multicast, sending one datagram to many recipients at once, something connection-oriented TCP simply cannot do.
[Sender] ---(Data 1)---> [Receiver]
[Sender] ---(Data 2)---> [Receiver]
(No handshake, no ACKs, no guarantees!)
UDP was formalized in RFC 768, written by Jon Postel in 1980 — one of the shortest, plainest RFCs ever published, barely a few pages long, because there was almost nothing left to specify once you strip reliability away: a source port, a destination port, a length, a checksum, then your data. That brevity isn't a limitation; it's the entire design philosophy stated in three pages. UDP is not "TCP but broken" — it is TCP with every reliability mechanism deliberately stripped out, so the application above it can decide exactly what it actually needs and build only that much.
Consider a live video call. A frame that arrives late, describing a moment three seconds gone, is worse than useless — displaying it would make the call feel more broken, not less. Worse, if that lost frame were riding over TCP, every newer frame behind it in the stream would stall in a queue, waiting for the old one to be retransmitted and confirmed before any of them could be delivered. That stall has a name, head-of-line blocking, and it's one of the biggest reasons latency-sensitive systems reach for UDP instead of TCP: better to drop one stale frame and move on than to freeze the whole stream waiting for it.
TCP buys reliability with latency and lets the kernel manage that trade-off for you; UDP buys speed by handing that same decision back to your application.
Head-to-Head
| Dimension | TCP | UDP |
|---|---|---|
| Connection model | Connection-oriented (handshake, teardown) | Connectionless |
| Reliability | Guaranteed delivery, retransmits lost data | Best-effort, no retransmission |
| Ordering | Guaranteed in-order delivery | No ordering guarantee |
| Message boundaries | None — byte stream | Preserved — one send = one datagram |
| Flow control | Yes (receiver-driven window) | None |
| Congestion control | Yes (sender adapts to network load) | None (app must implement if needed) |
| Header overhead | 20+ bytes | 8 bytes |
| Multicast/broadcast | Not supported | Supported |
| Typical latency profile | Higher, especially under packet loss | Lower, consistent |
| Typical use cases | Web, email, file transfer, databases, SSH | Streaming, gaming, VoIP, DNS, NTP |
Where Each One Actually Shows Up
Picture five ordinary moments and notice how differently each one values speed against certainty. Online banking runs on TCP without question — every byte of a login or a transfer has to arrive intact, in order, exactly once, and paying for that certainty with a little overhead is obviously worth it. Video conferencing tools like Zoom or Google Meet usually carry their audio and video over UDP (via RTP), because a frame from half a second ago is worthless the moment a newer one exists; tellingly, when a strict corporate firewall blocks UDP outright, these same apps fall back to relaying media over TCP through a TURN server, and call quality visibly degrades under any packet loss — a very concrete demonstration of head-of-line blocking happening to a real human on a real call. A DNS lookup is normally one small UDP datagram, fast and disposable, but if an answer is too large to fit in one datagram, the server flags it as truncated and the resolver quietly retries the same query over TCP, because now completeness actually matters more than speed. Competitive online games send player-position updates dozens of times a second over UDP, precisely because a lost update is already obsolete by the time the next one, a few milliseconds later, arrives to replace it. And a package manager like apt or npm install needs TCP without exception, because a single flipped byte in a downloaded archive can make the whole thing fail to open.
Frequently Asked Questions
Why does TCP's handshake need three steps instead of just two? Because both sides have to prove they can send and receive, and both need to agree on a starting sequence number for their own half of the conversation — a two-message exchange would only ever prove that one direction works. There is a subtler reason too: the three-way exchange, combined with randomized initial sequence numbers, makes it extremely unlikely that a stale packet from a long-closed connection gets mistaken for part of a brand-new one.
If I send two messages over TCP, will they always arrive as two separate reads? No, and this genuinely surprises people who are new to sockets. TCP is a byte stream, not a sequence of messages, so two separate sends can arrive as one combined chunk, or one send can arrive split across several reads — the network and operating system are free to coalesce or split the data however they like. If an application actually needs distinct messages, it has to build that framing itself, typically with a length prefix or a delimiter, which is exactly the opposite of UDP, where each send becomes exactly one datagram, arriving whole in a single read or not at all.
Isn't UDP just TCP with the reliability accidentally left out? No, and that framing gets the design backwards. UDP is TCP with every reliability mechanism deliberately stripped out, so the application above it can decide exactly what it actually needs and build only that much — RFC 768 is barely a few pages long precisely because there was almost nothing left to specify once reliability was removed, and that brevity is the entire design philosophy, not an oversight.
Why would a video call ever prefer to lose data rather than wait for it to be resent? Because a frame describing a moment three seconds gone is worse than useless once a newer frame already exists to replace it, and if that lost frame were riding over TCP, every newer frame behind it would stall in a queue waiting for the old one to be retransmitted and confirmed — a stall called head-of-line blocking. Better to drop one stale frame and move on than to freeze an entire live stream waiting on data nobody needs anymore.
If "the web runs on TCP" was true for decades, why does HTTP/3 run over UDP instead? Because HTTP/2's habit of multiplexing many requests over one TCP connection meant a single lost TCP segment stalled every multiplexed stream at once, even requests that had nothing to do with the lost data — head-of-line blocking again, just at a different layer. QUIC, which HTTP/3 builds on, re-implements reliability and ordering itself but per individual stream, on top of UDP, so a lost packet only ever blocks the one stream it actually belonged to.
Carrying This Forward
Neither protocol is "the good one." TCP is the right tool the instant a wrong or missing byte would actually cost something — money, a corrupted file, a broken command. UDP is the right tool the instant staleness costs more than loss — a video frame, a game-position update, a DNS query you'd rather just retry than wait on. Once you start noticing which category a piece of software falls into, you'll find yourself predicting, correctly, which protocol is running underneath things you've used for years without ever thinking about it: why a dropped call sounds choppy instead of just pausing, why a stalled download resumes exactly where it left off, why your character in an online game sometimes seems to teleport a few feet instead of sliding smoothly. That instinct — the one you're building right now, in prose, with no code in sight — is exactly what you'll put to direct use once the Go Fundamentals part of this book hands you net.Dial and net.ListenPacket and lets you feel the difference between a stream and a datagram with your own hands.
Fun Facts and Memes
- TCP's three-way handshake is sometimes compared to a secret handshake before a club meeting — nobody gets in until both sides have proven they know the ritual.
- UDP is jokingly called the "Unreliable Data Protocol," but that's precisely why it's the right choice whenever speed matters more than any single lost packet.
- If TCP were a person, it would be a careful librarian, checking every book back in by hand; UDP would be a courier already sprinting to the next delivery before you've finished signing for the last one.