强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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.2TCP+TLS 1.3QUIC
握手延迟3 RTT2 RTT1 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

特性TCPQUIC
序列号字节序列号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
CDNCloudflare, 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