UDP Protocol
User Datagram Protocol is a connectionless, stateless transport protocol. No handshake, no ordering, no retransmission, no flow control. Each datagram is independent — fire and forget.
Why It Matters
UDP is the backbone of real-time protocols: DNS, gaming, VoIP, video streaming, and IoT. When retransmitting stale data is worse than dropping it (a voice packet from 200ms ago is useless), UDP wins. It’s also the foundation of modern protocols like QUIC (HTTP/3).
UDP Header (8 bytes total)
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source Port | Dest Port |
+--------+--------+--------+--------+
| Length | Checksum |
+--------+--------+--------+--------+
| Data ... |
+-----------------------------------+
Compare: TCP header is 20-60 bytes. UDP’s 8-byte header means minimal overhead per packet.
When to Use UDP
| Use Case | Why UDP? |
|---|---|
| DNS | Single query-response, no connection overhead |
| Gaming | Low latency critical, stale positions are useless |
| VoIP/Video | Real-time, tolerate drops, can’t wait for retransmit |
| IoT/Sensors | Tiny packets, constrained devices, broadcast/multicast |
| QUIC (HTTP/3) | Builds its own reliability on top of UDP, avoids TCP head-of-line blocking |
| DHCP | Client doesn’t have an IP yet, needs broadcast |
TCP vs UDP
| Aspect | TCP | UDP |
|---|---|---|
| Connection | 3-way handshake | None |
| Reliability | Guaranteed delivery + retransmit | Best-effort |
| Ordering | Sequence numbers maintain order | No ordering |
| Flow control | Sliding window | None |
| Congestion control | AIMD, slow start | None (app must handle) |
| Header size | 20-60 bytes | 8 bytes |
| Latency | Higher (handshake + retransmit waits) | Lower |
| Head-of-line blocking | Yes (one lost segment blocks stream) | No |
C Socket Example
Sender
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in dest = {
.sin_family = AF_INET,
.sin_port = htons(9999),
};
inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);
const char *msg = "hello";
sendto(sock, msg, 5, 0, (struct sockaddr *)&dest, sizeof(dest));
close(sock);
return 0;
}Receiver
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(9999),
.sin_addr.s_addr = INADDR_ANY,
};
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
char buf[1024];
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
ssize_t n = recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen);
buf[n] = '\0';
printf("got: %s from %s:%d\n", buf,
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
close(sock);
return 0;
}Multicast
UDP supports one-to-many with multicast groups (224.0.0.0/4):
struct ip_mreq mreq = {
.imr_multiaddr.s_addr = inet_addr("239.0.0.1"),
.imr_interface.s_addr = INADDR_ANY,
};
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
// Now recvfrom() receives multicast traffic on 239.0.0.1Used for service discovery (mDNS), streaming, and cluster coordination.
QUIC: Reliability on Top of UDP
QUIC (used by HTTP/3) builds TCP-like reliability over UDP:
- Multiplexed streams without head-of-line blocking
- Built-in TLS 1.3 (0-RTT connection setup)
- Connection migration (survives IP changes, e.g., WiFi→cellular)
This avoids TCP’s limitations while keeping the benefits of UDP’s simplicity at the OS level.
Related
- TCP Protocol — reliable alternative
- DNS Protocol — DNS queries use UDP by default
- IP and Routing — UDP runs directly over IP
- Socket Programming —
SOCK_DGRAMfor UDP sockets