A video call freezes mid-sentence. A game character teleports across the screen. A file download crawls along at a fraction of your connection speed. All three problems cross the same cable, and all three are shaped by a decision made years before you opened the app: whether it speaks TCP or UDP.
Both protocols sit at the transport layer, just above IP. IP moves packets between machines on a best-effort basis — it makes no promises about arrival, order, or duplication. TCP and UDP are two different answers to the question "what do we do about that?"
Two contracts for the same wire
TCP is a contract. You get an ordered, reliable stream of bytes: if you send "hello" then "world", the other end receives "hello" then "world" — or an error. Retransmissions, reordering, and duplicate suppression happen inside the protocol, invisible to your application.
UDP is a postcard. You hand a datagram to the network stack, it gets an address and a port, and off it goes. No acknowledgement, no retransmission, no ordering. If it arrives, great. If it doesn't, that's the application's problem.
Both use ports to identify which process should receive the data. That's where the similarity ends.
What TCP does with every packet
Before a single byte of your data moves, TCP performs a three-way handshake: SYN, SYN-ACK, ACK. That's one full round trip, and on a connection to a server 80 milliseconds away, you've already spent 80ms before the request leaves your machine. Add TLS and you spend another round trip or two.
Once the connection is up, TCP tracks everything:
- Sequence numbers on every byte, so the receiver can reassemble data in order.
- ACKs going back, so the sender knows what landed and what didn't.
- Retransmission timers, so lost segments are sent again.
- Flow control, so a slow receiver doesn't get flooded by a fast sender.
- Congestion control, so the connection backs off when the network is struggling.
The header alone is 20 bytes minimum, before options. All of that machinery is why a large file transfer works at all: bytes get squeezed through lossy links and arrive intact.
The cost of reliability
Ordering has a dark side called head-of-line blocking. If segment 5 is lost and segments 6 through 20 arrive fine, TCP holds everything until 5 is retransmitted. Your application sees nothing until the gap is filled. On a clean network that costs milliseconds. On a flaky Wi-Fi connection it's the reason a page hangs and then dumps content all at once.
Reliability is also RTT-bound. A connection can only send as fast as acknowledgements come back, so long-distance and lossy paths hurt TCP far more than they hurt UDP.
UDP: minimal, fast, and honest about it
A UDP header is 8 bytes: source port, destination port, length, checksum. That's the whole protocol. There's no handshake, so the first packet you send is the first packet of data. There's no connection state on either side.
That sounds like a downgrade, and for a file transfer it is. For live audio, it's a feature. If a packet of voice carrying 20 milliseconds of speech goes missing, retransmitting it arrives too late to be useful — you'd rather lose it and keep moving. UDP lets the application make that call instead of the kernel making it for them.
When an application does want reliability over UDP, it builds only the parts it needs. QUIC, the transport behind HTTP/3, does exactly this: reliability per stream, so a lost packet stalls one stream rather than the whole connection.
Where each protocol actually shows up
TCP carries: HTTP/1.1 and HTTP/2 web traffic, SMTP and IMAP email, SSH sessions, FTP and SFTP file transfers, database connections, and most on-demand video streaming, which is delivered as HTTP segments over TCP or QUIC.
UDP carries: DNS queries, DHCP, NTP time sync, SNMP, TFTP, syslog, VoIP, WebRTC video calls, live game traffic, and QUIC/HTTP-3 on port 443.
That last entry surprises people. HTTP/3 runs over UDP, even though it's carrying web pages that need to arrive intact. The protocol stack moved reliability into user space so it could be smarter about ordering. If you've ever blocked UDP 443 on a firewall and watched sites slow down, that's why — the browser falls back to TCP-based HTTP/2.
How to choose for your own application
Work through these questions in order:
- Does losing one message break things? If yes, you want TCP or a reliability layer over UDP.
- Is this a stream or a series of independent messages? Streams suit TCP; discrete messages suit UDP.
- What's your latency budget? If a retransmitted packet would be useless by the time it arrives, UDP wins.
- Will it work through NAT and firewalls? TCP outbound over 443 almost always works. UDP sometimes doesn't, so plan a fallback.
- Do you need congestion control? If you use UDP and send hard, you'll cause loss for everyone sharing the link. QUIC and WebRTC implement their own; your custom protocol needs to as well.
Quick checks when a connection misbehaves
You can diagnose most of this with tools already on your machine. Run ping and traceroute (or mtr, which combines them) to see where latency and loss begin. A single hotspot of loss mid-path usually points to congestion rather than your own hardware.
On Linux, netstat -s prints TCP retransmission counters, and ss -ti shows per-connection details including retransmits and RTT estimates. A healthy connection retransmits a vanishingly small share of segments; double-digit percentages mean something is wrong.
For traffic-level detail, tcpdump or Wireshark will show you retransmissions, duplicate ACKs, and zero-window conditions directly. If you're comparing transports, iperf3 can run the same test over TCP (-c host) and UDP (-c host -u), which makes the latency and loss difference between them concrete rather than theoretical.
One practical trap with UDP: datagrams larger than the path MTU get fragmented or dropped, and unlike TCP there's no negotiation to prevent it. That's why DNS keeps responses small and falls back to TCP for large answers. If you're seeing random drops on a large-datagram UDP protocol, check MTU before blaming the network.
None of this needs to be abstract. Watch a download and a video call on the same connection: one tolerates delay to preserve every byte, the other discards bytes to preserve time. That's the whole distinction, and once you see it, the protocol choice behind almost any application becomes predictable.
Photo: panumas nikhomkhai / Pexels



