How UDP Works: Datagrams, Checksums, and Why Loss Can Be Acceptable
TCP can deliver a reliable byte stream. That is useful when a file must arrive complete and in order. It is the wrong contract when a late packet is worse than a missing one. User Datagram Protocol (UDP) is the Internet's datagram transport: a small header, a payload, and almost no promises.
This article explains what UDP actually guarantees, how the header is laid out, how ports and checksums work, what happens when datagrams are dropped or reordered, and why DNS, games, voice, and HTTP/3 all sit on top of it. It is not a rewrite of TCP congestion control or of the HTTP request cycle. Those protocols use a transport. UDP is a transport.
What UDP Is Allowed to Promise
RFC 768 defines UDP as a procedure for application programs to send messages to other programs with a minimum of protocol mechanism. The wording is precise. UDP does not establish a connection. It does not retransmit. It does not order payloads. It does not pace senders. It does not tell the application that a peer is reachable, except insofar as a later reply happens to arrive.
The contract is: take this blob of bytes, attach a source port, a destination port, a length, and optionally a checksum, and hand it to IP. If IP delivers a datagram whose checksum is valid, the receiving stack demultiplexes it to a socket bound to that destination port. Anything else — loss, duplication, reordering, delay — is visible to the application as silence or as a surprising arrival.
That silence is not a bug. Real-time media would rather skip a frame than stall the timeline while a retransmission races a new sample. A DNS resolver would rather retry a query than keep a three-way handshake alive for a 100-byte lookup. UDP leaves those policy choices in user space.
Where UDP Sits
On the sending host the path is roughly:
application write
-> UDP socket (port, peer address)
-> UDP header construction
-> IPv4 or IPv6 packet
-> routing and NIC
On the receiving host the reverse happens. IP accepts a packet whose protocol number is 17 (UDP). The stack checks length, optionally verifies the checksum, and looks up a socket whose local port matches the destination port and whose remote filter matches the source. If no socket matches, many stacks send an ICMP Port Unreachable. If a socket matches, the payload is queued for recvfrom or an equivalent.
UDP is therefore not “IP with extra steps.” Ports give process-level demultiplexing. The checksum covers the UDP header, the payload, and a pseudo-header of IP addresses so a datagram delivered to the wrong host is less likely to be accepted. IP still owns addressing, fragmentation, and the hop-by-hop path.
The UDP Header
The header is eight bytes:
- Source port (16 bits). Zero is allowed when the sender does not expect a reply.
- Destination port (16 bits). This is how the receiver finds the application.
- Length (16 bits). Header plus payload, so the minimum value is 8.
- Checksum (16 bits). For IPv4 the checksum may be zero, meaning “not computed.” For IPv6 the checksum is mandatory.
There is no sequence number, no acknowledgment field, no window, no flags, and no options. A protocol that needs those fields must put them in the payload. QUIC does exactly that. So do RTP, WireGuard's handshake messages in a different sense, and countless game protocols.
The maximum theoretical payload is limited by the 16-bit length field: 65,535 minus 8 bytes of UDP header, further reduced by the IP header. In practice path MTU is much smaller. A 1,500-byte Ethernet frame leaves roughly 1,472 bytes for UDP over IPv4 without options, or less with IPv6. Applications that send larger datagrams force IP fragmentation. Fragmentation is fragile: a lost fragment loses the whole datagram, and many networks drop fragments. Sensible UDP designs keep datagrams under a conservative path MTU or discover the MTU first.
Ports and Sockets
A UDP socket is identified by a local address and local port, and often by a connected peer as well. Binding to port 53 is how a DNS server receives queries. Binding to an ephemeral port is how a client tags its replies.
Unlike TCP, UDP sockets can be left unconnected. sendto names the destination on every call. recvfrom returns both the payload and the sender's address. A single socket can therefore talk to many peers. That is why a DNS recursive resolver or a game server can handle thousands of clients without a connection table at the transport layer.
Connecting a UDP socket is still useful. After connect, the stack filters inbound datagrams to one peer and the application can use send and recv. Errors such as ICMP Port Unreachable can also be reported on that socket. Connection here is a local filter, not a handshake with the other side.
Port reuse and SO_REUSEPORT let multiple processes share a well-known port so a busy DNS or QUIC service can scale across cores. That is an operating-system feature layered on UDP, not part of RFC 768.
Checksums and Corruption
The UDP checksum is a one's complement sum over a pseudo-header (source IP, dest IP, protocol, UDP length), the UDP header, and the payload, padded to an even number of bytes. A mismatch means the stack discards the datagram. The application never sees a corrupt payload that failed the check.
The checksum is only 16 bits. It is not a cryptographic integrity check. It will miss some multi-bit errors. Protocols that need authenticity put their own MAC or AEAD over the payload — TLS inside QUIC, DTLS, WireGuard, and DNS cookies or DNSSEC at another layer. Treating the UDP checksum as “security” is a category error.
IPv4 still allows a zero checksum. Some high-speed tunnels and legacy stacks used that to skip the cost of summing. IPv6 forbids it because IPv6 itself has no header checksum. If you operate IPv6, every accepted UDP datagram was at least checksummed once.
Loss, Reordering, and Duplication
IP networks drop packets when queues overflow, links fail, or middleboxes police traffic. They also reorder packets when flows take different paths or when a retransmission of a fragment arrives after a later datagram. Duplicates occur when a link-layer retry meets a delayed original.
TCP hides those events behind sequence numbers and a receive buffer. UDP surfaces them. If you send datagrams 1, 2, and 3, the receiver might see 1, 3, then 1 again, and never see 2. An application that concatenates payloads without its own sequence space will mis-parse the stream. An application that keys state by “latest timestamp wins” will simply ignore the late copy.
This is why UDP tutorials that stop at “unreliable” undersell the design problem. Unreliability is a family of failure modes. You choose which ones to repair:
- Retransmit on timeout when the payload is small and completeness matters (DNS, most RPC-over-UDP).
- Forward error correction when delay budgets cannot afford a round trip (some live video).
- Ignore loss when the next sample supersedes the last (voice, position updates).
- Rebuild a reliable multiplexed transport in user space (QUIC).
ICMP, Unreachability, and Path Signals
UDP has no handshake, but the path can still talk back. ICMP Destination Unreachable, including Port Unreachable and Fragmentation Needed, is how a host learns that a port is closed or that a datagram was too large. Many stacks turn those messages into errors on a connected UDP socket.
Those signals are optional and spoofable. A silent peer might be down, firewalled, or simply slow. Application timeouts remain the real liveness check. Path MTU discovery for UDP is similarly advisory: if ICMP is filtered, the sender must fall back to a smaller size or stop transmitting.
NAT and the Binding Problem
Consumer networks hide many devices behind one public address. A NAT device creates a mapping when an inside host sends a UDP datagram: public address plus port maps to private address plus port for a limited time. Replies that match the mapping are forwarded. Datagrams that do not match are dropped.
TCP NATs can observe SYN and FIN. UDP NATs have only idle timers. Typical bindings last tens of seconds to a few minutes. A VoIP client that goes quiet will lose its mapping unless it sends keepalives. Two peers behind different NATs cannot talk until at least one mapping exists, which is why STUN, TURN, and ICE exist: they discover or relay a path that the NAT will accept.
None of that is specified in RFC 768. It is an artifact of address scarcity. It still dominates real UDP deployment. QUIC connection migration exists in part because a phone that changes from Wi-Fi to cellular changes its outer UDP 4-tuple, and the application wants the logical connection to survive that change.
A Minimal Example
The following Python snippet sends one datagram and waits for one reply. It is not a protocol. It shows the socket surface.
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
sock.sendto(b"PING", ("127.0.0.1", 9000))
try:
data, addr = sock.recvfrom(2048)
print(addr, data)
except socket.timeout:
print("no reply")
sock.close()
A matching server binds, reads one datagram, and writes a reply to addr. If the server is not running, the client times out. On some platforms a connected socket plus an ICMP Port Unreachable becomes Connection refused instead of a timeout. That difference is exactly the lack of a transport-level session: the operating system reports what the network happened to say.
What Runs on UDP in Practice
DNS queries are the classic small-RPC case. A resolver sends a question in one datagram, usually to port 53, and accepts an answer. If nothing returns, it retries or asks another server. Truncated answers fall back to TCP. The protocol chose UDP because the common case is one packet each way.
Real-time media uses RTP over UDP so a late video frame can be discarded without stalling audio. Games send input and world-state snapshots with their own sequence numbers. WireGuard and many VPNs encapsulate encrypted packets as UDP datagrams so they can traverse NAT more predictably than some other encapsulations.
HTTP/3 runs over QUIC, and QUIC runs over UDP. That is not because the web abandoned reliability. It is because TCP's single ordered byte stream makes one lost packet delay every later stream on the same connection. QUIC puts stream reliability in user space, keeps each stream independent, and folds the cryptographic handshake into connection setup. UDP is the substrate that middleboxes already forward.
TCP remains the right default for bulk transfer, databases, and most APIs over HTTP/1.1 and HTTP/2. WebSockets ride on HTTP and TCP, not on raw UDP. TLS as used on the ordinary web assumes a reliable stream; DTLS and QUIC are the datagram-oriented relatives.
When UDP Is the Wrong Tool
Use UDP when you can state a loss policy. If the policy is “every byte, in order, forever,” you will reinvent a worse TCP: sequence numbers, timers, congestion control, and then the painful discovery that your homemade controller bursts under load.
UDP also does not protect you from being a bad network citizen. A sender that ignores delay and loss can fill buffers and harm other flows. QUIC and many modern media stacks implement congestion control precisely because the transport below them will not.
Firewalls and enterprise networks sometimes treat unknown UDP more harshly than TCP 443. A protocol that must work on hostile networks often keeps a TCP or HTTPS fallback even when UDP is faster on an open path.
Misconceptions
"UDP is always faster than TCP." On a clean, well-tuned path, good TCP fills the pipe. UDP looks faster when the alternative is a handshake plus head-of-line blocking, or when the application is willing to drop data. Bandwidth is not free just because ACKs are absent.
"UDP packets are guaranteed to be small and atomic." A datagram is atomic at the UDP layer: you get all of it or none of it. IP may still fragment under the datagram. Atomicity is not the same as “fits in one Ethernet frame.”
"A successful sendto means the peer received the data." It means the local stack accepted the datagram for transmission. There is no end-to-end ACK in UDP itself.
"You should disable the checksum for speed." On IPv6 you cannot. On IPv4 you should not, unless you have another integrity check and a measured reason.
Takeaways
UDP is a port-multiplexed datagram service over IP. Its header is eight bytes and its state is whatever the application stores. Loss, reorder, and duplication are part of the interface, not exceptions to it. DNS, telephony, games, and QUIC all chose that interface because they needed to define reliability themselves — or to refuse it.
If you need a dependable stream and kernel-managed congestion control, use TCP. If you need independent messages and you are prepared to handle silence, use UDP. If you need multiplexed reliable streams without TCP's cross-stream stall, look at QUIC, which still begins life as UDP datagrams on the wire.