TCP/UDP 网络协议教程 / 13-QUIC 协议
13 - QUIC 协议
13.1 QUIC 概述
QUIC(Quick UDP Internet Connections)是 Google 开发的基于 UDP 的传输协议,现已成为 HTTP/3 的标准。
协议栈对比:
传统 TCP 栈: QUIC 栈:
┌──────────┐ ┌──────────┐
│ HTTP/2 │ │ HTTP/3 │
├──────────┤ ├──────────┤
│ TLS │ │ QUIC │ ← 包含 TLS
├──────────┤ │ (基于UDP)│
│ TCP │ ├──────────┤
├──────────┤ │ UDP │
│ IP │ ├──────────┤
└──────────┘ │ IP │
└──────────┘
13.2 TCP 的核心问题
问题 1:队头阻塞 (Head-of-Line Blocking)
TCP 连接上的多个 HTTP 流:
HTTP/2 Stream 1: [Frame1] [Frame2] [Frame3]
HTTP/2 Stream 2: [Frame1] [Frame2] [Frame3]
HTTP/2 Stream 3: [Frame1] [Frame2] [Frame3]
如果 Stream 1 的 Frame2 丢失:
→ TCP 层不交付后续数据给应用层
→ 即使 Stream 2 和 3 的数据已经到达
→ 所有流都被阻塞
这就是 TCP 层队头阻塞!
问题 2:连接建立延迟
TCP + TLS 1.3 握手:
Client ──SYN──→ Server 1 RTT
Client ←──SYN+ACK── Server
Client ──ACK+ClientHello──→ Server 2 RTT
Client ←──ServerHello+Finished── Server
Client ──Finished──→ Server 3 RTT
Client ──HTTP Request──→ Server 4 RTT
总计:3-4 RTT 才能发送第一个请求
问题 3:连接无法迁移
场景:手机从 WiFi 切换到 4G
WiFi IP: 192.168.1.100:5000
4G IP: 10.0.0.100:6000
TCP 连接四元组变化 → 连接断开 → 重新建立
13.3 QUIC 核心特性
多路复用无队头阻塞
QUIC 的流是独立的:
Stream 1: [Frame1] [Frame2(丢)] [Frame3] ← 只有 Stream 1 受影响
Stream 2: [Frame1] [Frame2] [Frame3] ← 正常交付
Stream 3: [Frame1] [Frame2] [Frame3] ← 正常交付
每个流有独立的序列号空间和流控
"""概念:QUIC 流独立性"""
class QUICStream:
def __init__(self, stream_id):
self.stream_id = stream_id
self.data = bytearray()
self.offset = 0 # 每个流独立的偏移
def write(self, data: bytes):
self.data += data
self.offset += len(data)
# 多个独立流
stream1 = QUICStream(0) # 请求流
stream2 = QUICStream(4) # 响应流
stream3 = QUICStream(8) # 推送流
# 互不影响
stream1.write(b"GET /index.html")
stream2.write(b"200 OK ...")
stream3.write(b"push resource")
0-RTT 连接
首次连接 (1-RTT):
Client ──Initial(含 ClientHello)──→ Server
Client ←──Initial(ServerHello) + CRYPTO── Server
Client ──CRYPTO(完成握手) + 数据──→ Server
缓存服务器参数后重连 (0-RTT):
Client ──Initial + 0-RTT 数据──→ Server ← 第一个包就带数据!
Client ←──1-RTT(ServerHello) + 响应── Server
零个 RTT!极大减少延迟
0-RTT 风险:
• 重放攻击:攻击者可以重放 0-RTT 数据
• 限制:只对幂等操作使用(GET 请求等)
连接迁移
QUIC 使用 Connection ID 标识连接:
WiFi 时:(192.168.1.100:5000, Server:443, CID=0x1a2b3c4d)
4G 时: (10.0.0.100:6000, Server:443, CID=0x1a2b3c4d)
↑
连接不变!无缝切换!
Connection ID 在加密保护下,网络中间人无法追踪
13.4 QUIC vs TCP 全面对比
| 特性 | TCP+TLS 1.2 | TCP+TLS 1.3 | QUIC |
|---|---|---|---|
| 握手延迟 | 3 RTT | 2 RTT | 1 RTT / 0 RTT |
| 队头阻塞 | 有 | 有 | 无 |
| 多路复用 | 无 | 无 | 原生支持 |
| 连接迁移 | 不支持 | 不支持 | 支持 |
| 加密范围 | 仅数据 | 仅数据 | 数据+头部 |
| 拥塞控制 | 内核固定 | 内核固定 | 用户态可插拔 |
| 协议演进 | 困难 | 困难 | 容易 |
| UDP 依赖 | 否 | 否 | 是 |
13.5 HTTP/3 与 QUIC
HTTP/3 流模型:
Stream 0: 控制流
Stream 2, 6, 10...: 请求/响应流(客户端发起)
Stream 3, 7, 11...: 推送流(服务器发起)
每个请求/响应使用独立的 QUIC 流
"""HTTP/3 请求概念"""
class HTTP3Request:
def __init__(self, method, path, headers):
self.method = method
self.path = path
self.headers = headers
def encode(self):
# QPACK 头部压缩
return {
':method': self.method,
':path': self.path,
**self.headers
}
# 性能对比
# HTTP/1.1: 6 个连接 × 串行请求
# HTTP/2: 1 个连接 × 多路复用(但有 TCP 队头阻塞)
# HTTP/3: 1 个连接 × 独立流(无队头阻塞)
13.6 QUIC 安全性
QUIC 加密范围:
┌──────────────────────────────────────┐
│ QUIC Packet │
├─────────────┬────────────────────────┤
│ Header │ Payload │
│ (部分加密) │ (全部加密) │
│ │ │
│ ConnectionID│ Stream Frames │
│ PacketNum │ ACK Frames │
│ (加密) │ ... │
└─────────────┴────────────────────────┘
好处:
• 隐私保护:中间人无法看到 URL
• 防劫持:无法伪造 TCP 头部
• 协议僵化:中间设备无法干预
13.7 QUIC 可靠性
"""QUIC 可靠性机制(概念)"""
class QUICCongestionController:
"""QUIC 拥塞控制(用户态实现)"""
def __init__(self, algorithm="cubic"):
self.cwnd = 10
self.ssthresh = float('inf')
self.algorithm = algorithm
def on_ack(self, acked_bytes, rtt):
if self.algorithm == "cubic":
self._cubic_update(acked_bytes)
elif self.algorithm == "bbr":
self._bbr_update(acked_bytes, rtt)
def on_loss(self):
self.ssthresh = self.cwnd * 0.7
self.cwnd = self.ssthresh
# QUIC 拥塞控制可在用户空间灵活更换
# TCP 的拥塞控制在内核中,更新需要升级内核
QUIC ACK vs TCP ACK
| 特性 | TCP | QUIC |
|---|---|---|
| 序列号 | 字节序列号 | Packet Number |
| ACK 语义 | 累积确认 | 显式范围确认 |
| 重传序列号 | 新序列号 | 新 Packet Number |
| ACK Delay | 无明确机制 | 显式报告 |
13.8 QUIC 生态
# 使用 curl 测试 HTTP/3
curl --http3 https://www.google.com
# 查看 QUIC 连接
curl --http3 -v https://www.google.com 2>&1 | grep -i quic
| 组件 | 实现 |
|---|---|
| 浏览器 | Chrome, Firefox, Edge, Safari |
| 服务器 | Nginx, LiteSpeed, Caddy |
| CDN | Cloudflare, Google, Akamai |
| 库 | quiche (Rust), quic-go (Go), aioquic (Python) |
13.9 注意事项
⚠️ UDP 被阻止:某些企业/ISP 阻止 UDP,QUIC 需要回退到 TCP
⚠️ CPU 开销:QUIC 在用户空间加密,开销比内核 TCP 大
⚠️ 调试困难:加密使得抓包分析更困难
⚠️ NAT 超时:UDP NAT 映射超时通常比 TCP 短
13.10 扩展阅读
下一章:14 - 网络调试工具 - tcpdump、Wireshark、nc、ss