Free lesson · Computer Networking
UDP Server
The Simplest Transport Protocol
After spending an entire module on TCP - its handshakes, reliability mechanisms, flow control, congestion control, and state machines - UDP is a breath of fresh air. UDP (User Datagram Protocol) does almost nothing. It takes data from the application, slaps on a minimal header with source and destination port numbers, and hands it to the Network Layer for delivery. No connection setup. No acknowledgments. No retransmission. No ordering. No flow control. No congestion control.
That list of "no" is not a deficiency. It is the entire point.
UDP is defined in RFC 768, published in 1980 - one of the shortest RFCs ever written, because there is so little to specify. The protocol has survived unchanged for over 45 years precisely because its simplicity is its value. Every feature TCP adds comes with overhead: latency (handshake before data), bandwidth (acknowledgments, headers), CPU (retransmission timers, congestion window calculations), and memory (per-connection state). UDP eliminates all of this, giving applications raw speed and full control over how they handle (or do not handle) reliability.
The UDP Header - 8 Bytes of Everything
The UDP header contains exactly four fields, totaling 8 bytes:
| Field | Size | Purpose |
|---|---|---|
| Source Port | 16 bits | Identifies the sending application |
| Destination Port | 16 bits | Identifies the receiving application |
| Length | 16 bits | Total datagram size (header + payload) in bytes |
| Checksum | 16 bits | Error detection (optional in IPv4, mandatory in IPv6) |
That is it. Compare this to TCP's minimum 20-byte header with sequence numbers, acknowledgment numbers, window sizes, flags, and options. UDP's header is less than half the size and contains no state-tracking fields because UDP maintains no state.
The Length field specifies the total size of the UDP datagram including the header, giving a theoretical maximum datagram size of 65,535 bytes (2^16 - 1). In practice, UDP datagrams are usually kept well under the path MTU (typically 1,472 bytes = 1,500 MTU - 20 IP header - 8 UDP header) to avoid IP fragmentation, which introduces the same reassembly problems discussed in Module 04.
The Checksum covers the UDP header, the payload, and a pseudo-header derived from the IP header (source IP, destination IP, protocol, UDP length). In IPv4, the checksum is optional - a value of 0 means "no checksum computed." In IPv6, the checksum is mandatory because IPv6 removed the IP-layer checksum, making the transport-layer checksum the only protection against bit errors. In practice, virtually all modern UDP implementations compute the checksum regardless of IP version.
graph LR
subgraph TCPHeader["TCP Header (20+ bytes)"]
direction TB
T1["Source Port"]
T2["Dest Port"]
T3["Sequence Number"]
T4["Ack Number"]
T5["Flags (SYN/ACK/FIN/RST)"]
T6["Window Size"]
T7["Checksum"]
T8["Options (variable)"]
end
subgraph UDPHeader["UDP Header (8 bytes)"]
direction TB
U1["Source Port"]
U2["Dest Port"]
U3["Length"]
U4["Checksum"]
end
style TCPHeader fill:#ef4444,stroke:#dc2626,color:#fff
style UDPHeader fill:#22c55e,stroke:#16a34a,color:#fff
This diagram compares the TCP and UDP headers side by side. The TCP header contains at least 8 fields in 20 or more bytes: source port, destination port, sequence number, acknowledgment number, flags, window size, checksum, and variable-length options. The UDP header contains only 4 fields in exactly 8 bytes: source port, destination port, length, and checksum. UDP's simplicity is immediately visible - it has no fields for sequence tracking, acknowledgment, flow control, or connection management.
What UDP Does Not Do
Understanding UDP requires understanding what it deliberately omits:
No connection establishment. UDP has no handshake. The sender transmits a datagram immediately - no SYN, no SYN-ACK, no ACK. There is no connection state on either side. This eliminates the one-RTT latency penalty that TCP's handshake imposes on every new connection. For protocols that send a single request and receive a single response (like DNS), this matters enormously - a DNS query over UDP completes in one RTT, while the same query over TCP requires two RTTs (one for the handshake, one for the query-response).
No reliability. UDP does not guarantee delivery. If a datagram is lost in transit (dropped by a congested router, corrupted by a noisy link), UDP does not detect the loss, does not retransmit, and does not notify the sender. The datagram simply disappears. The sender has no way of knowing whether the datagram arrived unless the application implements its own acknowledgment mechanism.
No ordering. UDP datagrams may arrive in a different order than they were sent. If the sender sends datagrams 1, 2, 3, the receiver might receive them as 2, 3, 1 - or as 2, 3 (with 1 lost). UDP provides no sequence numbers and makes no attempt to reorder. Applications that need ordering must implement their own sequence numbering.
No flow control. UDP does not prevent the sender from overwhelming the receiver. If the sender transmits faster than the receiver can process, datagrams are dropped at the receiver's buffer. There is no window mechanism, no backpressure signal, no way for the receiver to say "slow down." The sender and receiver must agree on rates through application-level mechanisms or simply accept that overflows will cause data loss.
No congestion control. UDP does not respond to network congestion. If the network between sender and receiver is congested, UDP continues transmitting at whatever rate the application dictates. This can make congestion worse - unlike TCP, which reduces its sending rate when it detects congestion, UDP blasts through regardless. This is why uncontrolled UDP traffic can be harmful to the network: it does not back off, potentially starving TCP flows that do respond to congestion signals.
Why "Unreliable" Is a Feature
The word "unreliable" sounds negative, but in networking it is a technical term meaning "no delivery guarantee." And for many applications, this is exactly what they want:
Latency is more important than completeness. In a video call, a frame that arrives 200ms late (because TCP retransmitted it) is useless - the conversation has moved on. It is better to skip the frame and display the next one. UDP allows the application to make this trade-off: accept the loss, move on, maintain real-time responsiveness.
Application-level reliability is more efficient. Some applications need reliability but not the exact flavor TCP provides. A game server might want to retransmit critical state updates (player health, score changes) but not position updates (which are superseded by newer updates every 16ms). TCP's all-or-nothing reliability retransmits everything, including data the application no longer cares about. UDP lets the application implement selective reliability - retransmit only what matters.
No head-of-line blocking. Because UDP has no ordering constraint, a lost datagram does not block subsequent datagrams from being delivered. Each datagram is independent. This is the property that QUIC builds on - QUIC uses UDP as its substrate specifically because UDP's lack of ordering prevents the cross-stream HOL blocking that TCP imposes.
Lower overhead for small messages. A DNS query is typically 30-50 bytes. Sending it over TCP requires a 3-way handshake (1 RTT, ~180 bytes of headers), the query (another RTT), and a 4-way close. Over UDP, the same query is a single datagram with an 8-byte header - one RTT total, minimal overhead. For protocols that exchange small, independent messages, UDP's efficiency advantage is substantial.
Common UDP Applications
| Application | Why UDP? |
|---|---|
| DNS (port 53) | Small queries, independent, idempotent. One RTT vs three for TCP. Fallback to TCP for large responses. |
| DHCP (ports 67/68) | Client has no IP address yet - cannot establish a TCP connection. Must broadcast. |
| NTP (port 123) | Time synchronization needs low-latency, not guaranteed delivery. A late time update is counterproductive. |
| SNMP (port 161) | Network monitoring queries. Simple, small, tolerant of occasional loss. |
| Video/Voice (RTP) | Real-time media. Late frames are useless. Covered in detail in Topic 4. |
| Online Gaming | Position updates every 16ms. Latest state matters, not every state. |
| QUIC (port 443) | HTTP/3's transport. Uses UDP to avoid TCP's HOL blocking and ossification. |
| Syslog (port 514) | Log forwarding. Tolerant of occasional loss. Speed over guarantee. |
graph TD
subgraph UDPApps["UDP Application Categories"]
direction TB
RT["Real-Time Media<br/>Video calls, VoIP,<br/>live streaming<br/>(RTP/SRTP)"]
QRY["Query-Response<br/>DNS, DHCP, NTP,<br/>SNMP<br/>(small, independent)"]
GAME["Gaming<br/>Position updates,<br/>state sync<br/>(latest-state-wins)"]
TUNNEL["Tunneling<br/>QUIC, VPN (WireGuard),<br/>VXLAN<br/>(protocol substrate)"]
end
style RT fill:#4f46e5,stroke:#4338ca,color:#fff
style QRY fill:#7c3aed,stroke:#6d28d9,color:#fff
style GAME fill:#8b5cf6,stroke:#7c3aed,color:#fff
style TUNNEL fill:#a78bfa,stroke:#8b5cf6,color:#fff
This diagram shows four categories of UDP applications. Real-time media includes video calls, VoIP, and live streaming using RTP. Query-response protocols include DNS, DHCP, NTP, and SNMP, which exchange small independent messages. Gaming uses UDP for position updates and state synchronization where the latest state matters more than every state. Tunneling protocols like QUIC, WireGuard VPN, and VXLAN use UDP as a substrate for building more complex protocols on top.
UDP Socket Programming
UDP socket programming is simpler than TCP because there is no connection to establish or maintain:
# UDP Server
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # SOCK_DGRAM = UDP
server.bind(('0.0.0.0', 9999))
while True:
data, client_address = server.recvfrom(1024) # Receive datagram
print(f"Received from {client_address}: {data.decode()}")
server.sendto(b"ACK", client_address) # Send response
# UDP Client
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"Hello!", ('127.0.0.1', 9999))
response, _ = client.recvfrom(1024)
print(f"Response: {response.decode()}")
Notice the differences from TCP: SOCK_DGRAM instead of SOCK_STREAM, recvfrom/sendto instead of recv/send (because there is no connection - each datagram needs an explicit address), and no connect/accept/listen calls. A single UDP socket can communicate with any number of remote endpoints simultaneously - there is no per-connection socket like TCP's accepted sockets.
UDP Performance Characteristics
UDP's simplicity translates directly into performance advantages that matter at scale:
Zero connection setup cost. A TCP connection requires 1 RTT for the handshake before any data can flow. For services that handle millions of short-lived interactions (DNS resolvers processing billions of queries per day), eliminating the handshake reduces latency by 50% and halves the number of packets per interaction.
Lower kernel overhead. The Linux kernel's TCP stack maintains extensive per-connection state: sequence numbers, acknowledgment tracking, retransmission timers, congestion window, RTT estimates, SACK state, and more. UDP maintains no per-connection state - the kernel simply passes datagrams between the application and the network stack. This makes UDP significantly lighter on CPU and memory for high-packet-rate workloads.
Better multicore scalability. Because UDP has no per-connection state, there is no contention between CPU cores on shared connection tables. Combined with SO_REUSEPORT (which allows multiple sockets to receive from the same port with kernel-level load balancing), UDP servers can scale linearly across cores. This is one reason that high-performance DNS servers like Knot DNS and game servers use UDP.
Predictable latency. TCP's retransmission and congestion control mechanisms introduce variable delays. A TCP segment that triggers a retransmission adds at least one RTO (200ms+ minimum) to the transaction. UDP's latency is purely a function of network propagation time - there are no protocol-induced delays. For applications that need bounded latency guarantees (financial trading, industrial control systems), this predictability is essential.
UDP-Lite
UDP-Lite (RFC 3828) is a variant of UDP that allows partially corrupted datagrams to be delivered to the application instead of being silently dropped. Standard UDP's checksum covers the entire datagram - any bit error in any byte causes the entire datagram to be discarded. For codecs that can tolerate minor corruption (video and audio codecs that gracefully degrade with small errors), discarding the entire frame because of one corrupted byte is wasteful. UDP-Lite lets the application specify how many bytes must be checksum-verified, allowing the rest to be delivered even if corrupted. This is useful for real-time media where a slightly glitchy frame is better than a missing frame.
UDP and NAT / Firewalls
UDP's connectionless nature creates challenges for NAT and firewalls:
NAT timeout. TCP connections have explicit state (ESTABLISHED, etc.) that NAT devices can track. UDP has no state - NAT devices create a mapping when they see the first outgoing datagram and remove it after an idle timeout (typically 30-120 seconds). If the application does not send data frequently enough, the NAT mapping expires and incoming datagrams are dropped. This is why UDP-based protocols often implement keepalive mechanisms - periodic packets sent solely to prevent NAT timeout.
Firewall statelessness. Stateful firewalls track TCP connections and allow return traffic automatically. For UDP, the firewall must make assumptions: if an internal device sent a UDP datagram to an external address, allow responses from that address for a limited time. This heuristic-based approach is less secure than TCP's explicit connection tracking and is why some firewalls block UDP by default.
UDP amplification attacks. Because UDP has no handshake, an attacker can send a small UDP request with a spoofed source IP to a server that responds with a much larger reply. The amplified response floods the victim (the spoofed IP). DNS, NTP, and memcached have all been exploited for amplification attacks, generating traffic volumes exceeding 1 Tbps. This is the primary security concern with UDP-based services.
Interview: UDP knowledge demonstrates understanding beyond the TCP default:
"Why would you ever use an unreliable protocol?" - Because for some applications, a late delivery is worse than no delivery. Real-time media, gaming, and high-frequency data streams all prefer dropping stale data over delaying fresh data. "Unreliable" means the application controls the reliability trade-off, not the transport layer.
"What is the overhead difference between TCP and UDP?" - TCP requires a 3-way handshake (1 RTT) before data, a 20+ byte header per segment, per-connection memory for state tracking, and CPU for retransmission and congestion control. UDP has no handshake (0 RTT), an 8-byte header, no connection state, and no retransmission logic. For a single DNS query, TCP adds ~2 RTTs and ~200 bytes of overhead; UDP adds 0 extra RTTs and 8 bytes.
"Can you build reliable communication on top of UDP?" - Yes. QUIC does exactly this - it builds reliability, ordering, flow control, and congestion control on top of UDP, but with per-stream independence that TCP cannot provide. The trade-off is that the application (or library) must implement these mechanisms rather than relying on the kernel.
What Comes Next
The next topic provides a structured decision framework for choosing between TCP and UDP - the single most interview-relevant transport-layer question. You will learn how to articulate the trade-offs and make a deliberate protocol choice in System Design interviews.
Exercise: Starter: Write a simple UDP echo server and client in Python (use the code above as a starting point). Send a message and verify the response. Then try sending a message to a non-existent server - what happens? (Unlike TCP, there is no "Connection refused" error for the sender. The datagram simply disappears, unless the destination sends an ICMP Port Unreachable.)
Intermediate: Use
tcpdumpto capture UDP traffic. Runsudo tcpdump -i any udp port 53and then perform a DNS lookup (dig google.com). Examine the captured packets - you should see a single query datagram and a single response datagram. Compare this to a TCP DNS lookup (dig +tcp google.com) and count the additional packets for the TCP handshake and teardown.Advanced: Research UDP amplification attacks in detail. How does a DNS amplification attack work? What is the amplification factor (ratio of response size to request size) for DNS, NTP (monlist command), and memcached? How do BCP38/BCP84 (source address validation) and response rate limiting mitigate these attacks? Why has the internet community struggled to fully deploy source address validation despite knowing about the problem for decades?