Skip to main content

TCP vs UDP

TCP and UDP are the two dominant transport layer protocols, each designed for fundamentally different trade-offs. TCP is connection-oriented – a 3-way handshake establishes state before data flows, acknowledgments confirm receipt, and lost segments are retransmitted. UDP is connectionless – each datagram is independent, there is no state, and no mechanism exists to recover lost packets. The right choice depends entirely on whether your application can tolerate loss. Byte stream vs datagrams – application framing responsibility: TCP is a byte stream. The transport layer provides no message boundaries – if you send 1000 bytes and your peer calls recv(), it may get 100 bytes or 1000 bytes or anything in between depending on buffering and network conditions. Applications using TCP MUST implement their own message framing (length-prefix, newline delimiter, fixed-size blocks). HTTP/1.1 uses CRLF line endings. HTTP/2 uses length-prefixed binary frames. Redis RESP uses \r\n termination. UDP sends and receives discrete datagrams. One send() = one recv() on the other end (if it arrives). The datagram boundary is preserved by the network. Applications receive complete messages or none at all. Where reliability responsibility lives: With TCP: the kernel retransmits lost segments, reorders out-of-order segments, and enforces flow and congestion control. Application code assumes a reliable ordered stream. With UDP: if you need reliability, you implement it yourself. QUIC (HTTP/3) implements per-stream reliability over UDP: lost packets on stream A do not block stream B, eliminating TCP's head-of-line blocking while keeping per-stream delivery guarantees. NAT behavior differences: TCP's 3-way handshake creates explicit NAT state (connection tracking). NAT devices track the 5-tuple (src IP, src port, dst IP, dst port, protocol) and maintain state for the connection lifetime. UDP has no handshake – each datagram is independent. NAT devices create UDP "pseudo-connections" based on the 5-tuple but with a short timeout (typically 30 seconds for NAT mappings vs minutes for TCP). This is why VoIP and online games using UDP occasionally need STUN/TURN to maintain NAT hole-punching state. QUIC's Connection IDs are designed to survive NAT rebinding when the IP changes. TCP reliability ≠ application-level correctness: TCP guarantees byte-level delivery – it ensures every byte you sent is received in order. It does NOT guarantee application-level correctness. If a database accepts your UPDATE query over TCP but crashes before committing, the bytes were delivered but the operation failed. TCP's reliability is at the transport level, not the application level. Application-level correctness requires transactions, idempotency keys, and acknowledgment from the business logic layer.

TCP guarantees delivery, ordering, and error correction at the cost of latency. UDP sends datagrams with no guarantees, no handshake, and minimal overhead. Use TCP when data integrity matters (web, email, databases). Use UDP when speed matters more than reliability (DNS, video, gaming, VoIP).

FeatureTCPUDP
Connection modelConnection-oriented (3-way handshake)Connectionless – no setup or teardown
Delivery guaranteeGuaranteed – retransmits lost segmentsBest-effort – packets may be silently dropped
OrderingOrdered – sequence numbers ensure in-order deliveryUnordered – datagrams arrive in any order
Error correctionChecksum + retransmission of corrupted segmentsChecksum only – corrupted datagrams silently dropped
Flow controlSliding window – receiver controls send rateNone – sender transmits unconstrained
Congestion controlCUBIC / BBR / Reno – adapts to network loadNone – application responsible
Header size20–60 bytes8 bytes fixed
Latency overheadHigher – handshake adds 1 RTT before dataLower – first packet carries payload immediately
Message framingNone – byte stream, application must add its own framing (length-prefix, delimiter)Preserved – one send = one recv (if delivered). Datagram boundary maintained by the network.
NAT behaviorStateful – 3-way handshake creates NAT connection tracking entry. Long timeout (~minutes).Stateless – NAT creates pseudo-connection per 5-tuple with short timeout (~30s). Requires STUN/TURN for NAT traversal.
Application over UDPN/AQUIC adds per-stream reliability + congestion control on top of UDP. Reliability is NOT exclusive to TCP.
StateStateful – both endpoints track sequence/window stateStateless – no per-connection state on either side
Broadcast / MulticastNot supported – point-to-point onlySupported – UDP can target broadcast/multicast groups

When to use TCP

Use TCP when data integrity is non-negotiable: web traffic (HTTP/1.1, HTTP/2), email (SMTP, IMAP), file transfers (SFTP, rsync), remote shell (SSH), and all database connections (MySQL, PostgreSQL, MongoDB, Redis). Any application where a missing byte corrupts the result needs TCP.

When to use UDP

Use UDP when speed and low overhead matter more than reliability: DNS queries (single round-trip), video streaming and WebRTC, online gaming (stale state is worthless), VoIP (slight loss is acceptable), DHCP/SNMP/NTP, and QUIC/HTTP/3 (which implements its own reliability on top of UDP).

Common Mistakes

  • Using TCP for DNS queries – each DNS lookup over TCP adds a full handshake RTT. UDP is correct unless the response exceeds the EDNS(0) buffer size or is truncated.
  • Using UDP for financial or transactional data without implementing application-layer reliability – silent packet loss corrupts state.
  • Assuming UDP is faster in all cases – on a reliable LAN, TCP's overhead is negligible. UDP's benefit is on high-latency or lossy paths.
  • Not implementing congestion control in UDP applications – a UDP sender that ignores network load can cause congestion collapse for other flows.
  • Confusing UDP with being 'unreliable by design' – QUIC (HTTP/3) proves UDP can be the foundation of a reliable, ordered, encrypted, multiplexed transport.
  • Assuming TCP byte delivery equals application-level correctness – TCP guarantees the bytes arrive, not that the application processed them correctly. A crashed database server received your bytes; they were not committed. Idempotency and transactions live above the transport layer.
  • Forgetting that TCP is a byte stream – recv() can return partial data. Applications must implement framing (length-prefix or delimiter). Failure to do this causes subtle bugs that only appear under load when TCP coalesces small sends.
  • Not accounting for UDP NAT timeout – UDP NAT bindings typically expire in 30 seconds of inactivity. Long-lived UDP applications (VoIP, tunnels, online games) must send keepalives or use STUN to maintain NAT hole-punch state.

FAQ

Is QUIC (HTTP/3) TCP or UDP?

QUIC runs over UDP, but implements its own reliability, ordering, and congestion control per stream. This gives QUIC the best of both: no TCP head-of-line blocking (a lost packet on stream 1 doesn't block stream 2) and full reliability.

Can UDP packets be reordered?

Yes. IP routing does not guarantee order. UDP datagrams to the same destination can take different paths and arrive out of order. Real-time applications like video decoders handle this with jitter buffers.

Why do games use UDP instead of TCP?

In real-time games, a position update from 200ms ago is worthless – it's better to skip it than retransmit it. TCP's retransmission would cause a perceptible freeze waiting for the old packet. Games send state updates continuously and discard stale ones.