本页目录
UDP 与 QUIC 传输层
UDP 本身只是一个"带端口号的 IP",QUIC 在它之上用 UDP 封装了一套完整的可靠传输:0-RTT 握手、stream multiplexing 无队头阻塞、连接迁移——TCP 花了 30 年学到的东西,QUIC 一次做对。
概述
UDP 是最简单的传输层协议——8 字节 header,无连接、无重传、无流控。其"简陋"恰恰是优势:DNS 查询(1 个包往返)、实时音视频(丢几帧不影响体验)、VPN(WireGuard)都选择 UDP。QUIC 在 UDP 之上实现了一个完整的现代传输层,将 TCP 的流控/拥塞控制和 TLS 1.3 加密合并到单一协议中,解决了 TCP 的队头阻塞和连接迁移问题。HTTP/3 即运行在 QUIC 上。
UDP
UDP Header (fixed 8 bytes):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Length: UDP header + payload 总长度 (最小 8 = header only, 最大 65535)
Checksum: IPv4 可选 (0=no checksum), IPv6 强制 — 覆盖 pseudo-header (src/dst IP +
protocol + length) + UDP header + payload
UDP 提供"端口 + 长度 + optional checksum"之外什么都不做——无连接、无重传、无流控、无拥塞控制。应用层如果需可靠性,得自己实现。QUIC 正是"应用层自己实现传输层"的案例。
QUIC 传输层
QUIC 不是"UDP + 可靠性包装"——它是在 UDP 之上实现的全新传输层,将 TCP 的流控/拥塞控制 和 TLS 的加密认证合并到了一个协议里。
QUIC Packet 结构
QUIC Long Header (Initial/Handshake/0-RTT packets):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1| Type | Version (32 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DCID Len | Destination Connection ID (0-20 bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SCID Len | Source Connection ID (0-20 bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
QUIC Short Header (1-RTT data packets):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|1|S|R|R|R| Destination Connection ID (...) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Header Form (bit 0): 1=Long, 0=Short
Fixed Bit (bit 1): 始终 1 — 用于 version negotiation 检测 (中间件不能设这个 bit)
Spin Bit (bit 2 in Short): 每个 RTT 翻转 1 次 — 观察者可以测量 RTT
Reserved Bits: 0, 中间件如果改这些 bits → GREASE 检测到 → connection close
QUIC 连接: Connection ID
TCP 连接 = 5-tuple (src_ip, dst_ip, src_port, dst_port)。IP 换了 → 连接断开。
QUIC 连接 = Destination Connection ID + Source Connection ID。端点可以随时换 Connection ID (通过 NEW_CONNECTION_ID frame) → IP/port 完全可变。
Connection Migration:
Stream Multiplexing
一个 QUIC 连接有多个 stream:
Stream ID (62-bit, variable-length encoding):
Bit 0: 0=client-initiated, 1=server-initiated
Bit 1: 0=bidi, 1=unidirectional
Bits 2-61: stream number (0, 1, 4, 8, 12, ... — client starts at 0, server at 1,
bidi-only: even client, odd server)
每个 stream 独立:
- 有独立的 flow control (MAX_STREAM_DATA / STREAM_DATA_BLOCKED)
- Stream 1 丢包 → 只阻塞 Stream 1 的数据, Stream 3 继续交付
- 解决 TCP 队头阻塞: TCP 中所有 stream 的字节在同一个 byte stream → 一个丢包阻塞所有
连接级 flow control: MAX_DATA / DATA_BLOCKED — 限制所有 stream 的总接收量
0-RTT
首次连接:
1-RTT: ClientHello → ServerHello → EncryptedExtensions → Finished → (data)
后续连接 (PSK mode):
Client: 缓存上次的 session ticket (含 PSK + transport parameters + server address)
0-RTT: ClientHello + PSK + early data — 在收到 ServerHello 之前就开始发数据
Server: 验证 PSK → 接受 early data → 直接处理
0-RTT 安全风险 — Replay:
攻击者重放 0-RTT 包 → server 重复处理 → 重复副作用 (如扣款两次)
QUIC 缓解:
- 应用层: 只允许幂等操作 (GET, HEAD, OPTIONS) 在 0-RTT 中
- 传输层: server 可以发送 REJECTED 帧 → client 必须重发 0-RTT 数据
- AEAD limits: 同一个 key 不能用于太多 0-RTT 包 → 限制重放窗口
QUIC Loss Detection
QUIC 的丢包检测比 TCP 更精确:
TCP: 3 duplicate ACKs → retransmit (但对 SACK, 区分 reordering vs loss)
QUIC:
- 每个包有独立的 packet number (单调递增, 即使是重发包也用新号)
- 接收方 ACK frame: 最大收到 + missing ranges → 精确知道哪些包丢了
- Loss detection:
1. Time threshold: 超过 kTimeThreshold (9/8) × latest RTT → 丢包
2. Packet threshold: 超过 kPacketThreshold (3) 个更大的包被 ACK 了 → 丢包
QUIC 应用
HTTP/3: QUIC 的最大用户 (RFC 9114)
DNS/DoQ: DNS over QUIC (RFC 9250) — 0-RTT query
SMB over QUIC: Windows Server 2022
RDP over QUIC: 远程桌面抗丢包
参考
- RFC: 768, 8999-9002, 9000 Appendix A (design rationale), 9250
- 实现: quiche (Cloudflare), lsquic (LiteSpeed), quinn (Rust), ngtcp2 (C)
- LWN: "QUIC and HTTP/3"
Keywords: UDP, QUIC, connection migration, 0-RTT, stream multiplexing, head-of-line blocking, spin bit, GREASE