TCP vs UDP

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: Two ways to send data over the internet. TCP is “registered mail with delivery confirmation” — reliable but slow to set up. UDP is “fire and forget” — fast and unreliable. Almost everything in webapps uses TCP; specific use cases (DNS, video, games) need UDP.


In plain English

When one computer sends data to another over the internet, it doesn’t just dump bytes onto the wire and hope. There’s a transport-layer protocol that defines HOW the bytes travel:

  • TCP (Transmission Control Protocol) — guarantees the bytes arrive, in order, without duplicates. Slower; uses an initial handshake; retransmits lost packets.
  • UDP (User Datagram Protocol) — sends individual packets (“datagrams”); no guarantee of delivery, order, or uniqueness. Faster; no handshake; no retransmission.

A useful analogy:

  • TCP = sending a registered letter. Tracked, confirmed, ordered. Receipt expected. Slow.
  • UDP = shouting across a noisy room. Fast. If they didn’t hear, oh well.

Which to use depends on what the application can tolerate:

  • A web page download must be perfect — every byte, in order. Can’t have missing characters in HTML. → TCP.
  • A live video stream can tolerate one lost frame. Pausing to retransmit one frame would freeze the video. → UDP.
  • A multiplayer game wants the latest position, not the position from 100ms ago that was lost. → UDP.
  • DNS queries are tiny + retried at the app layer. Connection overhead would be more cost than benefit. → UDP.

The web — HTTP, HTTPS, REST APIs, webhooks — almost universally rides on TCP. UDP shows up in specific places: DNS, real-time media, online games, some VPN protocols, and (newer) HTTP/3 via QUIC.

This entry is the transport-layer view. For HTTP-level concerns (status codes, headers, methods), see HTTP & HTTPS.


Why it matters

Three concrete reasons knowing the difference pays off:

  1. It explains “why is the first request slow?” TCP handshake (and TLS handshake on top) means a brand-new HTTP request to an unvisited domain takes 100-300ms just to ESTABLISH the connection. Subsequent requests reuse the connection.

  2. It explains HTTP/3. HTTP/3 uses QUIC, which is built on UDP — specifically to avoid TCP’s slow handshake and head-of-line blocking. Knowing why UDP is the foundation makes HTTP/3’s benefits make sense.

  3. It explains why some operations feel different. Streaming video over UDP looks smooth (occasionally with a quick glitch); over TCP would pause for retransmission. Knowing the trade-off helps you reason about choices.

The trade-off: most webapp developers never directly choose TCP vs UDP. The protocol is determined by the application layer (HTTP picks TCP; DNS picks UDP). But understanding the layer below clarifies a lot of behavior above.


TCP — the reliable workhorse

When two computers want to talk via TCP, they first establish a connection via a three-way handshake:

Client → Server:  SYN          ("I want to talk")
Server → Client:  SYN-ACK      ("OK, I can talk")
Client → Server:  ACK          ("Great, let's go")

This takes one full round trip. After it, both sides have agreed on initial sequence numbers, options, and are ready to exchange data.

TCP guarantees:

  1. Reliable delivery. Lost packets are detected (no ACK received within timeout) and retransmitted.
  2. In-order delivery. Packets that arrive out of order are buffered until the missing ones arrive.
  3. No duplicates. If a packet is received twice (e.g., due to a retransmission race), TCP deduplicates.
  4. Flow control. The receiver tells the sender “slow down, my buffer is full.”
  5. Congestion control. TCP backs off when the network is overloaded; ramps up when clear.

The cost: SETUP is slow (one round trip), packets that arrive out of order STALL the stream until missing ones come (head-of-line blocking), and the protocol has overhead per packet (headers, ACKs).

For HTTP and the web: TCP is the right trade-off. We CAN’T tolerate lost or out-of-order bytes; speed is improved by reusing connections (HTTP/2 multiplexing on one TCP connection).


UDP — the fire-and-forget

UDP has no handshake, no connection, no acknowledgments. A program just sends a packet to IP:port, and the OS puts it on the wire. The receiver either gets it or doesn’t. There’s no notification either way.

UDP guarantees:

  • The packet is delivered to the receiver UNLESS the network drops it
  • Each packet is independent of others
  • Source/destination ports are stamped on each packet

It does NOT guarantee:

  • Delivery (packets can be lost)
  • Order (packets can arrive in different order than sent)
  • Uniqueness (rare, but a packet can be duplicated by routing weirdness)
  • Flow / congestion control (the application must handle it)

The trade: extreme speed and low overhead. No connection setup; no retransmission delays; no buffering for ordering.

When the application can tolerate occasional loss + wants speed, UDP wins. When the application needs guarantees + can accept overhead, TCP wins.


Side-by-side

AspectTCPUDP
ConnectionEstablished via handshakeConnectionless
ReliabilityGuaranteed deliveryNone
OrderingGuaranteed in-orderNone
Duplicate detectionYesNo
Flow controlBuilt-inNone (app’s responsibility)
Congestion controlBuilt-inNone (app’s responsibility)
Overhead per packetHigher (headers, ACKs)Lower
Latency on first packet~1 RTT for handshakeNone
Latency after setupLow; can have head-of-line blockingLow; no blocking
Best forLoss-intolerant data, ordering criticalLoss-tolerant data, speed critical
ExamplesHTTP/1.x, HTTP/2, SSH, FTP, SMTP, IMAPDNS, DHCP, video streaming, VoIP, online games, QUIC

What rides on TCP (almost everything in webapps)

  • HTTP/1.0, HTTP/1.1, HTTP/2 — the web
  • HTTPS — same, encrypted via TLS (TLS itself rides on TCP)
  • WebSockets — persistent two-way over a single TCP connection
  • SMTP, IMAP, POP3 — email
  • SSH — remote terminal
  • FTP — file transfer (legacy)
  • TLS — used by HTTPS and others
  • MQTT — IoT messaging (also has UDP variant for low-power)
  • PostgreSQL wire protocol — database connections
  • Redis (default) — also has a Unix socket option

If you’re sending data where every byte matters and order matters, you’re on TCP. That’s most things.


What rides on UDP (specific niches)

  • DNS queries (port 53) — usually a single small packet there + back; retransmission at the app layer; speed > guarantee
  • DHCP (ports 67/68) — initial network configuration
  • NTP (port 123) — time synchronization
  • VoIP (SIP, RTP) — voice over the internet; loss tolerable, latency intolerable
  • Streaming media (some) — Netflix uses HTTP/TCP; live video conferencing often UDP
  • Online games — sub-second position updates; old data is useless if you’ve already moved
  • WebRTC — peer-to-peer browser communication; uses STUN/TURN over UDP
  • QUIC (HTTP/3) — the new generation of HTTP runs on UDP, with QUIC providing reliability + ordering at the application layer instead of in the OS

UDP is small percentage by volume but critical for specific applications.


QUIC and HTTP/3 — UDP done right

For 30 years, TCP was THE choice for reliable internet protocols. In the 2010s, Google noticed TCP had specific weaknesses for modern web traffic:

  • Slow handshake (1 RTT before any data, plus 1-2 more for TLS)
  • Head-of-line blocking (a lost packet stalls everything on the connection)
  • Updates to TCP are slow because OSes ship with their TCP implementation; can’t innovate fast

So Google built QUIC — a new transport protocol that:

  • Runs on UDP (skipping TCP’s limitations)
  • Provides reliability, ordering, congestion control at the application layer
  • Combines transport + crypto handshake into a single round trip (or even 0-RTT for repeat visits)
  • Avoids head-of-line blocking — separate streams aren’t blocked by each other
  • Updates with the application, not the OS

QUIC became the foundation of HTTP/3 (RFC 9114, 2022). In 2026, HTTP/3 is widely deployed: Vercel, Cloudflare, Google, most major CDNs serve it. Browsers negotiate it transparently.

The bottom line: UDP isn’t only for “lossy is OK” anymore. Layered on UDP, you can build a transport that’s MORE reliable than TCP for specific patterns.


TCP / UDP debugging tools

A few practical commands:

# Test if a TCP port is open
nc -zv example.com 443
 
# Open a TCP connection (interactive)
telnet example.com 80
 
# Send a UDP packet
echo "hello" | nc -u example.com 12345
 
# See active connections + listening ports
netstat -tunlp        # Linux: TCP + UDP + listening + processes
ss -tulpn             # Linux modern equivalent
netstat -an           # cross-platform; less detail

For protocol-level analysis:

  • Wireshark — visual packet sniffing; shows TCP / UDP / individual packets with headers
  • tcpdump — CLI packet capture; same data, command-line interface
  • Chrome DevTools → Network — TCP/HTTP timing breakdown per request

A concrete example: visiting a site (the protocol layers)

When you visit https://example.com:

Layer 7 (Application):   HTTP request: "GET /"
Layer 6 (Presentation):  TLS encryption wraps the HTTP request
Layer 4 (Transport):     TCP packets carry the encrypted bytes
Layer 3 (Network):       IP packets carry TCP packets across routers
Layer 2 (Link):          Ethernet/WiFi frames carry IP packets across the wire
Layer 1 (Physical):      Actual electrons/photons on copper/fiber/radio

TCP sits at layer 4, between the IP layer (routing across networks) and the application (HTTP). It establishes the connection, transmits bytes reliably, closes the connection.

You don’t see TCP in your code. You write HTTP. The OS’s TCP stack does the work. But if HTTP feels slow on the first request and fast on subsequent ones, TCP is why — the connection is reused.

For HTTP/3 (over QUIC/UDP), the picture changes:

Layer 7:  HTTP/3 request
Layer 4:  QUIC (carries TLS + reliability + ordering)
          UDP packets carry QUIC frames
Layer 3:  IP
Layer 2:  Ethernet/WiFi
Layer 1:  Physical

QUIC replaces both TCP and TLS in one combined protocol. Faster initial handshake; no head-of-line blocking.


Common gotchas

  • HTTP “works” on UDP via HTTP/3. “TCP for web traffic” is the historical norm but not a law. Modern web increasingly uses HTTP/3 over UDP.

  • TCP’s initial handshake adds ~50-200ms. On a brand-new connection, this is unavoidable. Connection reuse, HTTP/2 multiplexing, and HTTP/3 mitigate.

  • Lost packets in TCP cause head-of-line blocking. If packet #5 of 10 is lost, packets 6-10 wait in a buffer until 5 arrives. The receiving app doesn’t see ANY of 5-10 until 5 is received.

  • Head-of-line blocking is why HTTP/3 matters. With many concurrent HTTP/2 streams on one TCP connection, ONE lost packet stalls ALL streams. HTTP/3’s QUIC separates streams; a lost packet on stream A doesn’t block stream B.

  • UDP is “best effort.” There’s no error. If you UDP-send a packet to a dead server, your program gets no notification. Application-level timeouts are mandatory.

  • DNS over TCP exists too (port 53 TCP). Used when responses are too big for UDP (over ~512 bytes). DNSSEC and zone transfers use TCP DNS.

  • DNS over HTTPS (DoH) and DNS over TLS (DoT) are modern variants that put DNS queries inside HTTPS (TCP) or TLS (TCP). Privacy + integrity; loses some of UDP DNS’s speed advantage.

  • Some firewalls block UDP. Aggressive corporate firewalls allow only TCP on 80/443. UDP-based services (HTTP/3 via QUIC, VoIP) may fall back to TCP equivalents.

  • VPNs over UDP vs TCP differ. OpenVPN can run over either. UDP is faster (no double-retransmission overhead); TCP works in restrictive firewalls but suffers from “TCP-over-TCP meltdown” (two layers of retransmission can spiral).

  • NAT and UDP interact awkwardly. NAT requires tracking active connections; UDP has no formal connection. NAT tables time out UDP “connections” after seconds or minutes of inactivity. WebRTC and peer-to-peer apps deal with this constantly.

  • Game protocols often layer reliability ON TOP of UDP. “Send player position via UDP, but if HEALTH changes, send via reliable channel.” Custom protocols mix.

  • The “P” in TCP/UDP is “Protocol,” not “Packet.” TCP = Transmission Control Protocol. UDP = User Datagram Protocol. The unit of UDP is a “datagram”; of TCP, conceptually a “stream of bytes” (though split into segments under the hood).

  • TCP delivers a BYTE STREAM, not messages. Two send() calls of 50 bytes each might arrive as one 100-byte read on the other side, or as 50+50, or even as 70+30. Application protocols define their own framing (e.g., HTTP uses headers + Content-Length or chunked encoding).

  • UDP delivers DATAGRAMS — message-shaped. One send() = one packet on the wire (subject to fragmentation). Each recv() returns one packet. Natural for message-based protocols.

  • MTU (Maximum Transmission Unit) typically 1500 bytes on Ethernet. Anything bigger gets fragmented at the IP layer. UDP doesn’t reassemble — receiving app gets fragments. TCP handles it transparently.

  • TCP “keep-alive” is OPTIONAL. Sends periodic empty packets to verify the connection is alive. Useful for long-lived connections (websockets); disabled by default in many configs.

  • TCP TIME_WAIT state holds a port for 30-60s after close. During this, the same IP:port pair can’t be reused. Annoying when restarting dev servers; usually transparent.

  • UDP doesn’t have “close.” There’s nothing to close. A UDP socket is just a binding; releasing it is instant.

  • Both TCP and UDP use the same port number space. Port 53 TCP and port 53 UDP are DIFFERENT endpoints; both used by DNS for different scenarios.

  • The OS hides most of this from apps. Your Node.js or Python code says “connect to host:port” and the OS picks TCP (if Socket()) or UDP (if dgram.createSocket()). The handshake, retransmissions, ACKs all happen below your code.

  • TCP fast open (TFO) lets clients send data IN the SYN packet, saving a round trip. Implemented in most OSes; rarely used because of compatibility issues.

  • The 0-RTT optimization in TLS 1.3 / QUIC lets a client send data on the FIRST packet for repeat connections (using a previously-established secret). Saves another round trip. Has replay-attack considerations.

  • Long-lived connections aren’t always better. A TCP connection that sits idle for an hour may have been silently torn down by some intermediate NAT or firewall. Re-establishing might be needed.

  • CDNs terminate TCP at the edge. When a user hits Cloudflare, the TCP/TLS handshake happens between user and Cloudflare. Cloudflare’s connection to your origin is a SEPARATE TCP connection (often pre-warmed and reused). This is why edge proxying speeds things up.

  • Connection: close HTTP header explicitly tears down the TCP connection after one request. Wasteful; rarely used now that keep-alive is default in HTTP/1.1.

  • Connection: keep-alive HTTP header explicitly requests connection reuse. Default in HTTP/1.1; explicitly required in HTTP/1.0.

  • Reading “TCP/IP” together is common but misleading. TCP runs ON TOP of IP. They’re separate layers. You can run UDP/IP, ICMP/IP, etc.

  • For Bible Quest-style work, you only see TCP/UDP indirectly. HTTP requests, database connections, real-time updates — all using TCP under the hood. You optimize at the application layer (HTTP/2, caching, ISR) and let the transport layer do its thing.

  • Network diagrams sometimes show TCP separately from “HTTP.” They’re at different layers. HTTP is application; TCP is transport. They co-exist on the same packets.

  • AI-generated code rarely cares about TCP/UDP. Backend code is written at the HTTP/TCP layer abstraction (libraries handle this); UI code is even more abstracted. This entry is for UNDERSTANDING, not daily work.


When you’d directly choose UDP

For most webapp developers: never. The application protocol (HTTP, WebSocket, DNS) picks the transport.

Specific scenarios where you’d write UDP code:

  • Custom game server — your own real-time protocol; UDP for speed
  • IoT / sensor data — tiny, frequent, loss-tolerable updates
  • DNS resolvers — implementing DNS itself
  • Streaming media servers — RTP / SRT / WebRTC
  • VPN clients — implementing OpenVPN, WireGuard

In Node.js: dgram.createSocket('udp4') for UDP. In browsers: WebRTC’s DataChannel is essentially UDP-like.


See also


Sources