Episode 1 — Fundamentals / 1.3 — Internet Protocols
1.3.d — How UDP Establishes Communication
In one sentence: UDP doesn't establish a connection at all — it simply wraps your data in a header with source/destination ports and a checksum, hands it to IP, and hopes for the best. That radical simplicity is the entire point.
Navigation: ← 1.3.c — What is UDP · 1.3.e — TCP vs UDP →
Table of Contents
- 1. The Core Idea — No Connection
- 2. How UDP Sends Data — Step by Step
- 3. UDP vs TCP — The Connection Difference
- 4. How the Receiver Handles UDP
- 5. UDP "Conversation" Patterns
- 6. Building Reliability on Top of UDP
- 7. UDP in Action — Real Examples
- 8. UDP and NAT
- 9. Key Takeaways
- 10. Explain-It Challenge
1. The Core Idea — No Connection
TCP establishes a connection with a handshake before any data flows. UDP skips all of that.
TCP approach: UDP approach:
───────────── ──────────────
1. "Hey, are you there?" (SYN) 1. "Here's your data." (send)
2. "Yes, I'm here!" (SYN-ACK) 2. Done.
3. "Great, let's go!" (ACK)
4. "Here's your data." (send)
5. "Got it." (ACK)
6. ...
TCP: 3 packets before data UDP: 0 packets before data
UDP has no concept of a connection. Each datagram is completely independent. The sender doesn't even know if the receiver exists, is running, or received the data.
The postcard analogy
┌─────────────────────────────────────────────────────────────────┐
│ THE POSTCARD ANALOGY │
│ │
│ TCP = Certified mail with tracking │
│ ───────────────────────────────── │
│ • You confirm the recipient's address first │
│ • Each letter has a tracking number │
│ • You get a delivery confirmation │
│ • If lost, the post office resends it │
│ • Letters arrive in order │
│ │
│ UDP = Dropping a postcard in a mailbox │
│ ───────────────────────────────────── │
│ • You write the address and drop it in │
│ • No tracking number │
│ • No delivery confirmation │
│ • If lost, you'll never know │
│ • If you send 5, they might arrive in any order │
│ • But it's MUCH faster and cheaper │
└─────────────────────────────────────────────────────────────────┘
2. How UDP Sends Data — Step by Step
Sender side
APPLICATION
│
│ "Send this message to 93.184.216.34:53"
│ (e.g. a DNS query)
│
▼
┌──────────────────────────────────────────────┐
│ UDP LAYER │
│ │
│ 1. Pick a source port (ephemeral, e.g. 54321)│
│ 2. Set destination port (53 for DNS) │
│ 3. Calculate length (header + data) │
│ 4. Compute checksum (header + data + pseudo) │
│ 5. Wrap it all into a UDP datagram │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ IP LAYER │
│ │
│ 1. Add source IP and destination IP │
│ 2. Set protocol field to 17 (UDP) │
│ 3. Create IP packet │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ DATA LINK / PHYSICAL │
│ │
│ Frame → bits on wire/radio │
└──────────────────────────────────────────────┘
THE DATAGRAM IS NOW IN THE NETWORK.
UDP'S JOB IS DONE. IT WILL NOT CHECK IF IT ARRIVED.
Receiver side
BITS ARRIVE ON WIRE/RADIO
│
▼
┌──────────────────────────────────────────────┐
│ DATA LINK / PHYSICAL │
│ Receive frame → extract IP packet │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ IP LAYER │
│ Check destination IP matches this host │
│ Protocol = 17 → hand to UDP │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ UDP LAYER │
│ │
│ 1. Verify checksum (discard if corrupt) │
│ 2. Look up destination port (53) │
│ 3. Is an application listening on port 53? │
│ YES → deliver datagram to application │
│ NO → send ICMP "port unreachable" back │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ APPLICATION (DNS server) │
│ Receives the complete datagram │
│ Processes query │
│ Sends response back via UDP │
└──────────────────────────────────────────────┘
What UDP does NOT do at any point
- No SYN, no SYN-ACK, no ACK — no handshake
- No sequence numbers — no ordering
- No acknowledgment — no retransmission
- No window size — no flow control
- No congestion algorithm — no congestion control
3. UDP vs TCP — The Connection Difference
TCP TIMELINE UDP TIMELINE
──────────── ────────────
t=0 SYN ──────────► t=0 DATA ──────────►
t=50 ◄────────── SYN-ACK t=50 ◄────────── RESPONSE
t=100 ACK ──────────► t=50 DONE!
t=100 DATA ──────────►
t=150 ◄────────── ACK
t=150 ◄────────── RESPONSE
t=200 ACK ──────────►
...
t=250 FIN ──────────►
t=300 ◄────────── ACK
t=300 ◄────────── FIN
t=350 ACK ──────────►
TCP: 7+ packets for one exchange UDP: 2 packets for one exchange
TCP: ~350ms total UDP: ~50ms total
4. How the Receiver Handles UDP
Socket binding
For a server to receive UDP, it must:
- Create a UDP socket
- Bind it to a specific port (e.g. port 53 for DNS)
- Call
recvfrom()which blocks until a datagram arrives
Unlike TCP, there is no listen() or accept() — no connection queue. The socket just receives individual datagrams from anyone who sends to that port.
What happens if nobody is listening
If a UDP datagram arrives at a port where no application is bound:
- The OS generates an ICMP Port Unreachable message back to the sender
- The datagram is silently dropped
- The sender's application may or may not care (depends on implementation)
What happens if the receive buffer is full
If datagrams arrive faster than the application reads them, the OS buffer fills up. Additional datagrams are silently dropped. No notification is sent to the sender. This is by design.
5. UDP "Conversation" Patterns
Even though UDP has no formal connection, applications build patterns on top of it.
Pattern 1: Request-Response (DNS)
CLIENT DNS SERVER
│ │
│ "What's the IP for google.com?" │
│ (one UDP datagram) ────────────────►│
│ │
│ ◄──────────────────── (one reply) │
│ "93.184.216.34" │
│ │
│ If no reply in 2 seconds, │
│ APPLICATION retries (not UDP) │
The application (DNS resolver) handles retries, not UDP. DNS typically waits 2–5 seconds, retries 2–3 times, then fails.
Pattern 2: Continuous Stream (Game/VoIP)
GAME CLIENT GAME SERVER
│ │
│ Position (10,20) ─────────────────────►│ (20ms ago)
│ Position (11,20) ─────────────────────►│ (now)
│ Position (12,21) ─────────────────────►│ (20ms later)
│ Position (13,21) ─────────────────────►│ (40ms later)
│ │
│ ◄──────────────────── Game state update │ (continuous)
│ ◄──────────────────── Game state update │
│ │
│ No handshake. No ACKs. If a packet │
│ is lost, next one has newer data anyway │
Pattern 3: Multicast/Broadcast (mDNS, service discovery)
DEVICE LOCAL NETWORK
│ │
│ "Anyone here named printer.local?" │
│ (multicast to 224.0.0.251) ────────►│ ──► ALL devices hear it
│ │
│ ◄──── "That's me! 192.168.1.42" ───│ (printer responds)
UDP naturally supports multicast (one datagram → many receivers). TCP cannot do this — it's point-to-point only.
6. Building Reliability on Top of UDP
When applications need some reliability but don't want TCP's overhead, they build their own mechanisms on top of UDP.
Common techniques
| Technique | How it works |
|---|---|
| Application ACKs | Receiver sends back a custom "got it" message; sender retransmits if no ACK |
| Sequence numbers | App adds its own sequence numbers to detect order and loss |
| Selective retransmission | Only retransmit specific lost datagrams (like QUIC does) |
| Forward Error Correction (FEC) | Send redundant data so receiver can reconstruct lost packets without retransmission |
| Timestamps | Add timestamps to detect staleness; discard late arrivals |
| Heartbeats/keepalives | Periodic pings to detect if the other side is still alive |
QUIC — The best example
QUIC builds a full reliable transport on top of UDP:
┌──────────────────────────────────────────────────┐
│ QUIC (application layer, runs in userspace) │
│ │
│ ┌─────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Streams │ │ Reliability │ │ Congestion │ │
│ │ (many │ │ (per-stream │ │ control │ │
│ │ parallel│ │ ACKs) │ │ (BBR, etc.) │ │
│ └─────────┘ └─────────────┘ └──────────────┘ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ TLS 1.3 │ │ Connection │ │
│ │ (built in) │ │ migration │ │
│ └─────────────────┘ └──────────────────┘ │
├──────────────────────────────────────────────────┤
│ UDP (just provides ports + multiplexing) │
├──────────────────────────────────────────────────┤
│ IP (routing) │
└──────────────────────────────────────────────────┘
QUIC uses UDP as a dumb pipe and implements everything else itself. This gives protocol designers freedom to innovate without waiting for OS kernel updates (TCP is implemented in the kernel; QUIC runs in userspace).
7. UDP in Action — Real Examples
DNS query walkthrough
Your browser needs the IP for "github.com"
1. DNS resolver creates a query (type A, name "github.com")
2. Wraps it in a UDP datagram:
Source Port: 49152 (ephemeral)
Dest Port: 53 (DNS)
Data: DNS query bytes (~40 bytes)
3. Sends to configured DNS server (e.g. 8.8.8.8)
4. NO HANDSHAKE — just sends
5. DNS server receives on port 53
6. Processes query
7. Sends UDP response back:
Source Port: 53
Dest Port: 49152
Data: DNS response with IP (~80 bytes)
8. If no response in 2 seconds → application retries
9. After 3 retries → fail, try backup DNS server
Total: 2 packets, ~1 RTT, done
Same thing via TCP would need 9+ packets (handshake + query + response + close)
Why DNS switches to TCP
When a DNS response is larger than 512 bytes (or ~1232 bytes with EDNS), it falls back to TCP. The server signals this by setting the TC (Truncated) flag in the UDP response, and the client retries over TCP. This is common with DNSSEC responses.
8. UDP and NAT
NAT (Network Address Translation) handles UDP differently from TCP.
| Aspect | TCP through NAT | UDP through NAT |
|---|---|---|
| State tracking | NAT tracks the connection (SYN → ESTABLISHED → FIN) | NAT creates a mapping when it sees outbound UDP; no connection state |
| Timeout | Typically 1–2 hours (connection has clear start/end) | Typically 30 seconds to 2 minutes (no end signal) |
| Keepalive | TCP keepalive packets refresh the mapping | App must send periodic heartbeats or the mapping expires |
| Inbound initiation | Blocked unless port forwarded | Blocked unless port forwarded or hole-punched |
UDP hole punching
A technique used by peer-to-peer applications (VoIP, gaming) to traverse NAT:
PEER A (behind NAT A) SERVER PEER B (behind NAT B)
│ │ │
│ "I want to talk to B" │ │
│ ──────────────────────► │ │
│ │ "A wants to talk" │
│ │ ──────────────► │
│ │ │
│ "B is at 203.0.113.5:8888"│ │
│ ◄────────────────────── │ │
│ │ │
│ Send UDP to 203.0.113.5:8888 ───────────────► │
│ ◄──────────────────── Send UDP to A's public IP│
│ │
│ NATs now have mappings for each other │
│ Direct peer-to-peer UDP traffic flows! │
9. Key Takeaways
- UDP does not establish a connection — it sends independent datagrams with no handshake, no state, no guarantees.
- The sending process: application data → add 8-byte header (ports, length, checksum) → hand to IP → done.
- The receiver checks the checksum and delivers to the application bound to the destination port (or sends ICMP unreachable if nothing is listening).
- Applications build their own reliability on top of UDP when needed (QUIC being the most sophisticated example).
- UDP supports multicast and broadcast — TCP cannot.
- NAT timeouts for UDP are shorter than TCP; apps must send heartbeats to keep mappings alive.
- UDP's "lack of features" is intentional — it's a minimal, fast foundation that gives applications full control.
10. Explain-It Challenge
Without looking back, explain in your own words:
- Walk through what happens when a DNS query is sent via UDP — from application to network and back.
- Why is there no
listen()oraccept()in UDP server code? - What happens if a UDP datagram arrives and nobody is listening on that port?
- How does QUIC add reliability on top of UDP while keeping UDP's advantages?
- What is UDP hole punching and why is it needed?
Navigation: ← 1.3.c — What is UDP · 1.3.e — TCP vs UDP →