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

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:

  1. Create a UDP socket
  2. Bind it to a specific port (e.g. port 53 for DNS)
  3. 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

TechniqueHow it works
Application ACKsReceiver sends back a custom "got it" message; sender retransmits if no ACK
Sequence numbersApp adds its own sequence numbers to detect order and loss
Selective retransmissionOnly retransmit specific lost datagrams (like QUIC does)
Forward Error Correction (FEC)Send redundant data so receiver can reconstruct lost packets without retransmission
TimestampsAdd timestamps to detect staleness; discard late arrivals
Heartbeats/keepalivesPeriodic 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.

AspectTCP through NATUDP through NAT
State trackingNAT tracks the connection (SYN → ESTABLISHED → FIN)NAT creates a mapping when it sees outbound UDP; no connection state
TimeoutTypically 1–2 hours (connection has clear start/end)Typically 30 seconds to 2 minutes (no end signal)
KeepaliveTCP keepalive packets refresh the mappingApp must send periodic heartbeats or the mapping expires
Inbound initiationBlocked unless port forwardedBlocked 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

  1. UDP does not establish a connection — it sends independent datagrams with no handshake, no state, no guarantees.
  2. The sending process: application data → add 8-byte header (ports, length, checksum) → hand to IP → done.
  3. The receiver checks the checksum and delivers to the application bound to the destination port (or sends ICMP unreachable if nothing is listening).
  4. Applications build their own reliability on top of UDP when needed (QUIC being the most sophisticated example).
  5. UDP supports multicast and broadcast — TCP cannot.
  6. NAT timeouts for UDP are shorter than TCP; apps must send heartbeats to keep mappings alive.
  7. 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:

  1. Walk through what happens when a DNS query is sent via UDP — from application to network and back.
  2. Why is there no listen() or accept() in UDP server code?
  3. What happens if a UDP datagram arrives and nobody is listening on that port?
  4. How does QUIC add reliability on top of UDP while keeping UDP's advantages?
  5. What is UDP hole punching and why is it needed?

Navigation: ← 1.3.c — What is UDP · 1.3.e — TCP vs UDP →