TCP vs UDP: What's the Difference?

Almost every service you run talks over one of two transport protocols: TCP or UDP. They sit on top of IP, and they solve the same basic problem of getting bytes from one machine to another. The difference is what they promise about those bytes along the way. TCP promises a lot and works hard to keep those promises. UDP promises almost nothing and stays out of your way. Understanding that trade-off is the whole game, and it explains why your database uses one and your video call uses the other.

If you want the wider context around addressing, routing, and the layers below this, start with our networking fundamentals guide. This post zooms in on the transport layer.

The core trade-off #

TCP is connection-oriented. Before any data moves, the two sides agree to talk, and then TCP guarantees that every byte arrives, in order, exactly once. If a packet goes missing, TCP notices and resends it. If packets arrive out of order, TCP holds them and reassembles the stream before handing it up. It also manages flow control, so a fast sender does not overwhelm a slow receiver, and congestion control, so the network itself does not collapse under load. That is a lot of machinery, and it costs you overhead and latency.

UDP is connectionless. It takes your data, wraps it in a small header, and fires it at the destination. There is no handshake, no acknowledgment, no reordering, and no promise that anything arrives at all. What you gain is speed and simplicity. A UDP packet has an 8-byte header versus TCP's 20-plus bytes, and there is no round-trip setup before the first byte of real data.

Here is the shape of it side by side:

Property | TCP | UDP
Connection | Yes, handshake first | No
Delivery guarantee | Reliable, retransmits lost data | Best effort, none
Ordering | Guaranteed | Not guaranteed
Flow/congestion control | Yes | No
Header size | 20+ bytes | 8 bytes
Speed/overhead | Higher latency, more overhead | Low latency, low overhead

The three-way handshake #

TCP's connection setup is the famous three-way handshake. The client sends a SYN (synchronize) with an initial sequence number. The server replies with SYN-ACK, acknowledging the client and sending its own sequence number. The client sends a final ACK, and the connection is open. That is one full round trip before any application data moves, which matters when you are opening thousands of short connections.

Teardown is similar. Each side sends a FIN, and the other acknowledges it, so closing a connection cleanly takes its own round trips. This is why connection reuse and keep-alive settings matter so much for busy services: setup and teardown are pure overhead you want to amortize.

What reliable and ordered actually costs #

The guarantees are not free. Every segment TCP sends must be acknowledged, and if an ACK does not come back in time, TCP retransmits. That adds latency, and it means a single lost packet can stall everything behind it. This is head-of-line blocking: because the stream must be delivered in order, packet number 5 sits in the buffer waiting even after packets 6 through 10 have already arrived, all because packet 4 got dropped and has to be resent first.

For a file transfer or an API response, this is exactly what you want. You would rather wait than receive corrupt or scrambled data. For a live voice call, waiting on a retransmit is worse than just skipping the lost audio, which is the whole reason UDP exists.

What UDP gives up and gains #

UDP hands the hard problems back to you. There is no retransmission, so if you need reliability, your application builds it. There is no ordering, so if sequence matters, you number your own packets. There is no congestion control, so a badly behaved UDP application can flood a link.

In exchange you get the lowest possible latency and full control. For real-time media, dropping a stale packet and moving on beats waiting. For a single request-response like a DNS lookup, opening a TCP connection would cost more than the query itself. UDP lets you send one packet, get one back, and be done.

Real use cases #

TCP: HTTP/1.1 and HTTP/2, REST and gRPC APIs, database connections, SSH, and email (SMTP, IMAP). Anything where correctness and completeness beat raw speed.

UDP: DNS and DHCP for quick single-shot exchanges, VoIP and video conferencing, live video streaming, and online gaming where fresh data matters more than complete data. QUIC, the transport under HTTP/3, is also built on UDP.

QUIC and HTTP/3 moving the web to UDP #

The interesting twist is that the web itself is drifting toward UDP. QUIC, which carries HTTP/3, runs on top of UDP and then rebuilds reliability, ordering, and congestion control in userspace rather than in the kernel's TCP stack. Why bother? Because building on UDP lets QUIC fix TCP's head-of-line blocking by giving each stream its own ordering, fold the TLS handshake into the connection setup to cut round trips, and evolve without waiting for operating system kernels to update. It is not that UDP is more reliable. It is that starting from a blank slate let the designers make better trade-offs than a decades-old TCP implementation allows.

How to see them #

On a running host, ss shows both. The -t flag lists TCP sockets and -u lists UDP:

bash.bash

# TCP sockets that are listening or established
ss -tn

# UDP sockets
ss -un

# Watch actual packets on the wire for a given port
sudo tcpdump -n -i any port 53 # DNS, mostly UDP
sudo tcpdump -n -i any tcp port 443 # HTTPS over TCP

Watch a DNS lookup with tcpdump and you will see a single request and a single reply. Watch an HTTPS load and you will see the SYN, SYN-ACK, ACK handshake before any encrypted data flows.

Which should you use? #

Ask one question: does missing or reordered data break your application? If yes, use TCP and let the protocol do the hard work. Databases, APIs, file transfers, and remote shells all fall here. If stale data is worthless and you would rather drop it than wait, use UDP and accept that reliability is your job. Real-time media, telemetry, and discovery protocols fall here.

Most teams never make this choice directly, because it is baked into the libraries and services they already use. You pick it consciously mainly when you are building a custom protocol or tuning a real-time system.

If you land on TCP and need to squeeze more throughput out of it, our guide to Linux TCP tuning covers the kernel knobs worth turning.

The call we'd make #

Default to TCP. For the overwhelming majority of services, the reliability is worth the overhead, and modern connection reuse makes the handshake cost mostly disappear. Reach for UDP only when you have a concrete real-time or single-shot reason, and when you are ready to own the reliability logic yourself. And if you are building something new on the web, look hard at HTTP/3 and QUIC: it gives you UDP's flexibility with the reliability rebuilt in for you.