UDP in Practice | Low Latency, DNS, Games, Streaming & QUIC

UDP in Practice | Low Latency, DNS, Games, Streaming & QUIC

이 글의 핵심

UDP sends datagrams without a connection for minimal delay; reliability and ordering are application (or QUIC) concerns.

Introduction

UDP (User Datagram Protocol) is a lightweight transport that sends datagrams without establishing a connection. TCP provides reliability, ordering, and congestion control; UDP only sendsloss, duplication, and reordering are handled by the application or upper layers (QUIC, RTP, …). That makes UDP common for DNS, realtime games, VoIP/video, and monitoring where latency or multicast matters.

This article covers headers, ports, checksums, how retransmission and MTU work in practice, and how HTTP/3 / QUIC uses UDP.

After reading this post

  • Describe the UDP header and IPv4 vs IPv6 checksum rules
  • Implement basic datagram clients in C++, Python, and JavaScript with timeouts
  • Explain why games and streaming pick UDP from latency and throughput angles
  • Relate QUIC and WebRTC at a stack diagram level

Table of contents

  1. Protocol overview
  2. How it works
  3. Hands-on programming
  4. Performance characteristics
  5. Real-world use cases
  6. Optimization tips
  7. Common problems
  8. Wrap-up

Protocol overview

History and background

RFC 768 (1980) standardizes UDP—IP multiplexing via ports and optional checksumming with minimal philosophy: the internet is assumed unstable, and realtime needs a thin layer.

OSI placement

UDP is layer 4. IP reaches the host; UDP ports demux to sockets/processes. No connection state—each datagram is independent.

Core properties

PropertyDescription
ConnectionlessNo handshake (unless the app adds one).
UnreliableDrops and reordering are not repaired by UDP.
Datagram boundariesReceive size often matches send (OS buffering may split recv).
Multicast-friendlyEasier one-to-many patterns than TCP.
Low overhead8-byte header vs TCP.

How it works

Header (IPv4)

FieldSizeRole
Source port16 bitsSender port (optional).
Destination port16 bitsReceiver port.
Length16 bitsUDP header + payload length.
Checksum16 bitsOptional (0) on IPv4; mandatory on IPv6 UDP.

Checksum

IPv4 allows checksum disabled (0); IPv6 UDP requires checksums. Uses pseudo-header + UDP + payload. For stronger guarantees, add encryption and MAC at the app or TLS layer.

Ports

0–65535 demuxes processes on a host; well-known ports (e.g. DNS 53) are IANA-registered.

QUIC relationship (2026)

HTTP/3 uses QUIC over UDP. QUIC adds encryption, congestion control, streams, and connection migration—“simple UDP” vs “TCP-like reliability” in one modern stack.

flowchart TB
  subgraph app [Applications]
    H3[HTTP/3]
    RTP[RTP / realtime media]
    DNS[DNS client]
  end
  subgraph mid [Example stack]
    QUIC[QUIC + TLS]
    UDPR[UDP socket]
  end
  subgraph l3 [Network]
    IP[IP]
  end
  H3 --> QUIC
  QUIC --> UDPR
  RTP --> UDPR
  DNS --> UDPR
  UDPR --> IP

Hands-on programming

C++ (UDP client sketch)

// g++ -std=c++17 -O2 udp_client.cpp -o udp_client
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
  const char* host = argc > 1 ? argv[1] : "127.0.0.1";
  const uint16_t port = argc > 2 ? static_cast<uint16_t>(std::stoi(argv[2])) : 5353;

  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) { perror("socket"); return 1; }

  sockaddr_in peer{};
  peer.sin_family = AF_INET;
  peer.sin_port = htons(port);
  if (inet_pton(AF_INET, host, &peer.sin_addr) != 1) return 1;

  const std::string msg = "ping";
  if (sendto(fd, msg.data(), msg.size(), 0,
             reinterpret_cast<sockaddr*>(&peer), sizeof(peer)) < 0) {
    perror("sendto");
    close(fd);
    return 1;
  }

  char buf[2048];
  socklen_t plen = sizeof(peer);
  ssize_t n = recvfrom(fd, buf, sizeof(buf), 0,
                        reinterpret_cast<sockaddr*>(&peer), &plen);
  if (n < 0) { perror("recvfrom"); close(fd); return 1; }
  std::cout.write(buf, n);
  std::cout << '\n';
  close(fd);
  return 0;
}

Python 3

#!/usr/bin/env python3
import socket
import sys

def main() -> None:
    host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 5353

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(3.0)
    try:
        sock.sendto(b"ping", (host, port))
        data, addr = sock.recvfrom(4096)
        print(f"from {addr}: {data!r}")
    except socket.timeout:
        print("timeout: no response", file=sys.stderr)
        raise SystemExit(1)
    finally:
        sock.close()

if __name__ == "__main__":
    main()

Node.js (dgram)

// node udp_client.mjs
import dgram from "node:dgram";

const host = process.argv[2] ?? "127.0.0.1";
const port = Number(process.argv[3] ?? 5353);

const socket = dgram.createSocket("udp4");

const onMessage = (msg, rinfo) => {
  console.log(`from ${rinfo.address}:${rinfo.port}: ${msg.toString()}`);
  socket.close();
};

socket.on("message", onMessage);
socket.on("error", (err) => {
  console.error(err.message);
  socket.close();
  process.exitCode = 1;
});

socket.send(Buffer.from("ping"), port, host, (err) => {
  if (err) {
    console.error(err.message);
    process.exitCode = 1;
    socket.close();
  }
});

setTimeout(() => {
  socket.close();
  console.error("timeout");
  process.exitCode = 1;
}, 3000).unref();

App-level reliability

Without TCP’s ACKs, request–response over UDP needs timeouts, retransmits with IDs, dedup, and backoff—or use QUIC/RTP that embed those concerns.


Performance characteristics

Latency

No handshake—you can send the first datagram immediately (firewalls/NAT aside). Important when time-to-first-packet dominates games and voice.

Throughput

UDP has no built-in windowed congestion control—you can flood, but you should not. Bulk transfer needs app congestion control or QUIC.

Overhead

8-byte UDP header—smaller than TCP until you add app/crypto headers.

Benchmarks

Loopback may hit hundreds of thousands of pps; on the internet, loss and jitter dominate games and voice more than raw Mbps.


Real-world use cases

DNS

Most queries use UDP; large responses may fall back to TCP.

Games

Latest state often beats retransmitting old stateUDP + app synchronization is common.

Realtime streaming

RTP typically runs on UDP; loss is handled with encoding, FEC, NACK, … WebRTC uses UDP—see the WebRTC guide.


Optimization tips

Packet size (MTU)

Design IP + UDP + payload under path MTU. Avoid IP fragmentation—loss costs a whole fragment chain.

Retransmit and ordering

  • Retransmit: measure RTT, handle jitter buffers, drop duplicates.
  • Ordering: sequence numbers; decide whether late packets are dropped or buffered.

Fairness

Uncontrolled UDP floods can harm neighbors and trigger ISP throttling. Rate limits and congestion control are operational requirements, not niceties.


Common problems

Loss

Common on Wi‑Fi, mobile, congested links—use FEC, lower bitrate, IFrame cadence, NACK/retransmit when delay allows.

Reordering

Multipath routing can reorder—use sequence numbers and reorder buffers, or stateless designs.

NAT and firewalls

UDP is stateless—NAT may expire mappings; keepalives, STUN/TURN (WebRTC), and QUIC keepalive address this.


Wrap-up

Summary

  • UDP favors low latency, simplicity, and multicast; reliability lives in the app or upper protocols.
  • DNS, games, and realtime media are classic UDP domains; HTTP/3/QUIC adds modern reliability on top.
  • MTU, timeouts, and retransmit policy are mandatory design topics.

When to choose UDP

  • Short requests, realtime, broadcast-style workloads—or QUIC/RTP. When reliability and ordering come first, start with the TCP guide; for browser P2P realtime, see WebRTC.