编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 计算机原理

  • 网络协议

    • README
    • 通过看新闻熟悉网络
    • 通过购物熟悉加密
    • 从0到1部书电商网站
    • 请求网络的通用流程
    • 网络编程模型的概念
    • 传输协议TCP和UDP
    • Socket的发展和设计
    • 传输数据的设计思想
    • 网络域名解析的流程
    • HTTP服务设计流程
    • HTTP协议设计思想
    • HTTPS协议设计策略
    • HTTP连接和跳转
    • HTTP代理和缓存设计
    • 如何去排查网络故障
    • WebSocket实时通信
    • HTTP3与QUIC协议
      • 01.工作案例引入
        • 1.1 一个视频App的TCP之痛
        • 1.2 TCP痛点背后的协议知识图谱
      • 02.TCP为什么成为瓶颈
        • 2.1 TCP队头阻塞的根源
        • 2.2 连接建立的RTT代价
        • 2.3 TCP是不可升级的
        • 2.4 TCP痛点总结
      • 03.QUIC的设计哲学
        • 3.1 为什么选择UDP
        • 3.2 在用户态实现传输层
        • 3.3 QUIC vs TCP核心差异
        • 3.4 协议栈对比
      • 04.QUIC的连接建立
        • 4.1 0-RTT与1-RTT握手
        • 4.2 Connection ID标识连接
        • 4.3 连接迁移原理
        • 4.4 与TLS的深度融合
      • 05.QUIC的多路复用
        • 5.1 无队头阻塞的多路复用
        • 5.2 对比HTTP/2 over TCP
        • 5.3 流量控制
        • 5.4 优先级设计
      • 06.QUIC的可靠传输与拥塞控制
        • 6.1 丢包与重传
        • 6.2 可插拔拥塞控制
        • 6.3 QUIC的纠错机制
        • 6.4 与TCP BBR的对比
      • 07.HTTP/3
        • 7.1 HTTP/3与QUIC的关系
        • 7.2 HTTP/3的改进
        • 7.3 部署现状与挑战
        • 7.4 升级路径
      • 08.综合案例:移动端网络连接的四次进化
        • 8.1 案例背景与目标
        • 8.2 第一代:HTTP/1.1 over TCP
        • 8.3 第二代:HTTP/2 over TCP
        • 8.4 第三代:HTTP/2 + TCP优化
        • 8.5 第四代:HTTP/3 over QUIC
        • 8.6 四种方案横向对比
        • 8.7 案例升华:为什么HTTP/3是Web的未来
        • 8.8 全文知识图谱回顾
      • 09.思考题与作业
        • 9.1 基础思考题
        • 9.2 进阶思考题
        • 9.3 动手作业
  • 操作系统

  • 数据库原理

  • 计算机
  • 网络协议
杨充
2021-06-02
目录

HTTP3与QUIC协议

# 17.HTTP/3与QUIC协议

# 目录介绍

  • 01.工作案例引入
    • 1.1 一个视频App的TCP之痛
    • 1.2 TCP痛点背后的协议知识图谱
  • 02.TCP为什么成为瓶颈
    • 2.1 TCP队头阻塞的根源
    • 2.2 连接建立的RTT代价
    • 2.3 TCP是不可升级的
    • 2.4 TCP痛点总结
  • 03.QUIC的设计哲学
    • 3.1 为什么选择UDP
    • 3.2 在用户态实现传输层
    • 3.3 QUIC vs TCP核心差异
    • 3.4 协议栈对比
  • 04.QUIC的连接建立
    • 4.1 0-RTT与1-RTT握手
    • 4.2 Connection ID标识连接
    • 4.3 连接迁移原理
    • 4.4 与TLS的深度融合
  • 05.QUIC的多路复用
    • 5.1 无队头阻塞的多路复用
    • 5.2 对比HTTP/2 over TCP
    • 5.3 流量控制
    • 5.4 优先级设计
  • 06.QUIC的可靠传输与拥塞控制
    • 6.1 丢包与重传
    • 6.2 可插拔拥塞控制
    • 6.3 QUIC的纠错机制
    • 6.4 与TCP BBR的对比
  • 07.HTTP/3
    • 7.1 HTTP/3与QUIC的关系
    • 7.2 HTTP/3的改进
    • 7.3 部署现状与挑战
    • 7.4 升级路径
  • 08.综合案例:移动端网络连接的四次进化
    • 8.1 案例背景与目标
    • 8.2 第一代:HTTP/1.1 over TCP
    • 8.3 第二代:HTTP/2 over TCP
    • 8.4 第三代:HTTP/2 + TCP优化
    • 8.5 第四代:HTTP/3 over QUIC
    • 8.6 四种方案横向对比
    • 8.7 案例升华:为什么HTTP/3是Web的未来
    • 8.8 全文知识图谱回顾
  • 09.思考题与作业
    • 9.1 基础思考题
    • 9.2 进阶思考题
    • 9.3 动手作业

# 01.工作案例引入

# 1.1 一个视频App的TCP之痛

场景:小吴是一名移动端工程师,负责公司短视频 App 的播放体验优化。产品抱怨"用户反馈视频加载慢,WiFi 切 4G 时经常卡住要重刷"。小吴开始排查。

痛点 ① —— "为什么从 WiFi 走到电梯口,视频就断了?":小吴发现,当用户从客厅 WiFi 走到电梯口(WiFi 信号变弱,手机自动切到 4G),正在播放的视频必定卡住——需要等几秒甚至直接报错。因为 TCP 连接是用四元组 {源IP, 源端口, 目标IP, 目标端口} 标识的,IP 一变,TCP 连接就断了。需要重新 DNS 解析、TCP 握手、TLS 握手——整个过程至少 2~3 秒。

痛点 ② —— "一个丢包,整个页面所有请求都卡住":播放页有 6 个并发的 HTTP/2 请求(视频分片、弹幕、推荐列表、用户信息、播放历史、广告)。测试发现,视频分片请求丢了一个包,其他 5 个请求的数据明明已经到达了,却全部卡住等待重传。这就是 TCP 队头阻塞——HTTP/2 的多路复用在 TCP 层被"一锅端"。

痛点 ③ —— "每次打开 App,首页加载要 3 个 RTT 才能看到内容":冷启动访问首页:DNS 解析(1 RTT)→ TCP 握手(1 RTT)→ TLS 握手(2 RTT)→ HTTP 请求(1 RTT)。总共 5 个 RTT,在移动网络下(RTT ≈ 100ms)就是 500ms 的纯网络延迟,还不算服务端处理时间。

疑惑链条:

  • "TCP 连接为什么不能跟着 IP 迁移?" → TCP 用四元组标识连接 → IP 变了连接就废了 → QUIC 用 Connection ID 标识 → 连接可以"漂移"
  • "HTTP/2 的多路复用不是解决队头阻塞了吗?" → 它只解决了 HTTP 层的队头阻塞 → TCP 层的队头阻塞仍然存在 → QUIC 把"流"搬到了 UDP 之上,每个流独立
  • "TLS 握手为什么要 2 个 RTT?" → TLS 1.2 需要客户端和服务端各一轮交换 → TLS 1.3 减到 1 RTT → QUIC 把 TLS 握手和传输层握手合并,0-RTT 即可恢复连接
  • "为什么要基于 UDP?UDP 不是不可靠吗?" → QUIC 在 UDP 之上重新实现了可靠传输 → 相当于在用户态写了一个"更好的 TCP" → 绕过内核的 TCP 协议栈,可以灵活升级

小吴这一串问题,本质都是在问:TCP 的设计有什么先天缺陷?QUIC 怎么解决这些缺陷?UDP 之上如何实现可靠传输?为什么 HTTP/3 要抛弃 TCP?——这正是"HTTP/3 与 QUIC 协议"要回答的。

# 1.2 TCP痛点背后的协议知识图谱

HTTP版本与底层协议的进化:

HTTP/1.1  ─── TCP ─── IP
  队头阻塞:HTTP层 + TCP层(双重)
  握手:TCP(1RTT) + TLS(2RTT) = 3RTT

HTTP/2    ─── TCP ─── IP
  队头阻塞:TCP层(应用层已解决)
  握手:TCP(1RTT) + TLS(2RTT) = 3RTT
  痛点②:一个丢包阻塞所有流 ← TCP层队头阻塞

HTTP/3    ─── QUIC ─── UDP ─── IP
  队头阻塞:无(流独立)
  握手:QUIC(1RTT, 0-RTT恢复) ← 痛点③解决
  连接迁移:支持 ← 痛点①解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

三类痛点与后续章节的映射关系:

痛点 症状 根因 QUIC的解决方案 对应章节
① WiFi切4G断连 TCP用四元组标识 Connection ID 04.连接建立
② 一个丢包全卡 TCP层队头阻塞 流独立 05.多路复用
③ 握手5个RTT TCP+TLS分两次握手 合并为1握手 04.0-RTT

本章的主线就是沿着这三个痛点,一层一层拆解 TCP 的局限、QUIC 的设计、HTTP/3 的进化。读完之后,你不仅能理解为什么互联网正在从 TCP 迁移到 QUIC,还能明白这个迁移如何从根本上改善移动端和弱网环境的体验。

# 02.TCP为什么成为瓶颈

# 2.1 TCP队头阻塞的根源

疑惑:HTTP/2 的多路复用不是已经解决了队头阻塞吗?

答疑:HTTP/2 解决了应用层的队头阻塞,但 TCP 在传输层队头阻塞依然存在。

HTTP/2 over TCP 的队头阻塞:

  Stream1 数据 | Stream2 数据 | Stream3 数据 | Stream1 数据
  ──────────────────────────────────────────────────→ 一个TCP字节流
  
  如果带Stream2数据的TCP包丢了,接收方的TCP协议栈必须等待重传
  即使Stream1和Stream3的数据已经到达,TCP也不会向上层递交
  因为TCP的语义是"按序到达"
  
  结果:Stream2丢一个包 → Stream1/2/3全部卡住 ← 这就是TCP队头阻塞
1
2
3
4
5
6
7
8
9
10
为什么TCP的按序递交是必须的?

TCP的设计假设:数据是有序的字节流
→ 如果允许乱序递交 → 上层应用收到"第3段"但缺少"第2段"
→ 上层无法还原完整消息
→ TCP的选择:宁可阻塞所有后续数据,也要保证顺序

但这个假设对于HTTP/2的多路复用就不适用了:
→ Stream1和Stream3是独立的,Stream2的丢包不应该阻塞它们
→ 但TCP不知道"流"的概念,TCP只知道字节序列号
→ TCP层看不到HTTP/2的流边界
1
2
3
4
5
6
7
8
9
10
11

回到小吴的痛点②:视频App的一个视频分片丢包,导致弹幕、推荐列表等完全独立的请求全部卡住。这就是 TCP 队头阻塞的典型表现。

# 2.2 连接建立的RTT代价

HTTP/2 over TCP 的完整连接建立(冷启动):

  客户端                                 服务端
  ──── SYN ───────────────────────→       ) TCP握手(1RTT)
  ←─── SYN-ACK ────────────────────       )
  ──── ACK ───────────────────────→       )
  ──── ClientHello ───────────────→       ) TLS 1.3握手(1RTT)
  ←─── ServerHello + {Finished} ────       )
  ──── {Finished} ─────────────────→       )
  ──── HTTP/2 Settings ────────────→       ) HTTP/2就绪
  ──── HTTP Request ───────────────→       ) 1RTT

  最少 3 RTT(TCP+TLS+HTTP请求)才能收到数据
  移动网络 RTT≈100ms → 至少 300ms 纯网络延迟
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.3 TCP是不可升级的

疑惑:为什么不直接改进 TCP,而要重起炉灶用 UDP?

答疑:TCP 协议栈实现在操作系统内核中,升级极其困难。

TCP协议栈的部署困境:

  互联网上有数十亿台设备:
    ├── 服务器(各种Linux版本)
    ├── 个人电脑(Windows/macOS/Linux)
    ├── 手机(Android/iOS)
    ├── 路由器/交换机
    ├── 防火墙/NAT设备
    └── 各种IoT设备

  要升级TCP协议栈 → 需要所有这些设备的操作系统更新
  → 部分设备可能永远不会更新(旧Android手机、嵌入式设备)
  → TCP新特性(如TCP Fast Open)推广了十几年,支持率仍然不高

QUIC在用户态实现 → 只需更新应用程序或库
  → Chrome浏览器更新 → 全球40%的用户立即获得QUIC支持
  → 服务端升级Nginx/Caddy → 立即支持
  → Google在2013年部署QUIC,2016年已在Chrome中大规模使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这就是 QUIC(Quick UDP Internet Connections)这个名称的由来——不是"快速的 UDP",而是"快速部署的互联网连接协议",通过用户态实现绕过"升级内核"这个最大的障碍。

# 2.4 TCP痛点总结

痛点 根因 HTTP/2能否解决 QUIC如何解决
队头阻塞 TCP按序递交 ❌ 不能 ✅ 流独立
握手RTT多 TCP+TLS分两次 ❌ 不能 ✅ 合并为1次
连接迁移 四元组绑定IP ❌ 不能 ✅ Connection ID
协议僵化 内核实现 ❌ 不能 ✅ 用户态升级

# 03.QUIC的设计哲学

# 3.1 为什么选择UDP

为什么基于UDP而不是从零开始一个新的IP协议?

方案A:创建新IP协议(如IP Protocol 142)
  ❌ 需要所有中间设备(路由器、防火墙、NAT)支持
  ❌ 已有设备不会升级
  ❌ 部署周期十年以上

方案B:基于UDP
  ✅ UDP在所有网络设备上已经广泛支持
  ✅ 只需升级终端(客户端和服务端)
  ✅ 中间设备把QUIC包当成普通UDP包处理
  ✅ 部署周期数月到数年
1
2
3
4
5
6
7
8
9
10
11
12

# 3.2 在用户态实现传输层

QUIC 最大的创新不是技术上的,而是架构上的——把可靠传输从内核搬到用户态。

传统TCP:
  应用层(用户态)
  ─────────────── 系统调用边界 ───────────────
  TCP协议栈(内核态) ← 不可升级或升级极慢

QUIC:
  HTTP/3(用户态)
  QUIC协议(用户态) ← 可以随应用快速升级
  UDP(内核态) ← 薄薄一层,只负责端口和校验
1
2
3
4
5
6
7
8
9
用户态实现的好处:

1. 快速迭代:Chrome 6周发布一个版本 → QUIC新特性6周内触达用户
2. 可插拔:拥塞控制算法可以热替换(BBR/CUBIC/自定义)
3. 更好的调试:可以记录详细的内部状态日志
4. 跨平台:不需要为每个操作系统写不同的实现
1
2
3
4
5
6

# 3.3 QUIC vs TCP核心差异

维度 TCP QUIC
传输层 TCP UDP + QUIC
实现位置 内核态 用户态
连接标识 四元组(IP+端口) Connection ID(64位随机数)
握手延迟 TCP握手 + TLS握手(2~3 RTT) 合并握手(1 RTT, 0-RTT恢复)
队头阻塞 有(字节流按序递交) 无(流独立)
连接迁移 不支持(IP变连接断) 支持(基于Connection ID)
拥塞控制 内核实现,难以变更 可插拔,用户态可选算法
升级 需升级内核 更新应用程序即可
加密 可选(TLS在上层) 内置(QUIC始终加密)

# 3.4 协议栈对比

HTTP/2 over TLS over TCP:         HTTP/3 over QUIC:

  ┌──────────────────────┐        ┌──────────────────────┐
  │        HTTP/2         │        │        HTTP/3         │
  ├──────────────────────┤        ├──────────────────────┤
  │         TLS           │        │   QUIC(用户态实现)    │
  ├──────────────────────┤        │ 合并了TLS+传输功能    │
  │         TCP           │        ├──────────────────────┤
  ├──────────────────────┤        │   UDP(仅端口+校验     │
  │          IP           │        │   不做可靠传输)       │
  └──────────────────────┘        ├──────────────────────┤
                                   │          IP           │
                                   └──────────────────────┘

关键变化:
  TCP+TLS两层 → QUIC一层(合并握手+始终加密)
  内核态 → 用户态(可快速迭代升级)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 04.QUIC的连接建立

# 4.1 0-RTT与1-RTT握手

QUIC 的核心优化之一:将传输层握手和 TLS 握手合并为一次。

QUIC 1-RTT握手(首次访问,没有缓存):

  客户端                                 服务端
  ──── ClientHello + KeyShare ────→       ) 
      (包含TLS版本、密码套件、           )  1 RTT
       客户端ECDHE公钥)                  )
  ←─── ServerHello + {Finished} ────       )
      (选择的密码套件、服务端ECDHE公钥、    )
       服务端Finished已加密)               )
  
  此时客户端已经可以推导出会话密钥
  ──── {Finished} + Data ──────────→       ) 0 RTT
      (客户端Finished + 应用数据)          )
  
  总耗时:1 RTT即可发送应用数据!


QUIC 0-RTT握手(之前连接过,有缓存):

  客户端(拿上次的会话票据)
  ──── ClientHello + Early Data ───→       ) 0 RTT!
      (恢复密钥 + 应用数据)                 )
  
  ←─── ServerHello + Data ──────────       )
      (接受恢复 或 拒绝重新协商)            )
  
  总耗时:0 RTT!打开App瞬间就能发数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
各协议握手RTT对比(冷启动):

HTTP/1.1 + TLS 1.2:  TCP(1) + TLS(2) + HTTP(1) = 3~4 RTT
HTTP/2 + TLS 1.3:    TCP(1) + TLS(1) + HTTP(1) = 2~3 RTT
HTTP/3 (QUIC):       QUIC(1) + HTTP(1)          = 1~2 RTT
HTTP/3 (0-RTT):      QUIC(0) + HTTP(1)          = 0~1 RTT

移动网络 RTT≈100ms:
  HTTP/1.1+TLS1.2:  300~400ms → HTTP/3(0-RTT): 0~100ms
1
2
3
4
5
6
7
8
9

# 4.2 Connection ID标识连接

TCP用四元组标识连接:
  {客户端IP, 客户端端口, 服务端IP, 服务端端口}
  → IP变了 → 连接断了 → 必须重新握手

QUIC用Connection ID标识连接:
  Connection ID = 随机64位数字
  → IP变了 → Connection ID不变 → 连接不中断!
1
2
3
4
5
6
7

回到小吴的痛点①:

用户从WiFi走到电梯(IP变化):

TCP:
  旧连接{192.168.1.5:52341, 10.0.0.1:443} → 失效
  新连接{10.5.3.2:19283, 10.0.0.1:443} → 重新握手
  过程:TCP握手(100ms) + TLS握手(200ms) = 300ms 断连

QUIC:
  旧路径{192.168.1.5:52341, CID=0xABCD} → 探测到切换
  新路径{10.5.3.2:19283, CID=0xABCD} → 继续使用
  过程:无需重连,无缝切换,0ms 断连
1
2
3
4
5
6
7
8
9
10
11

# 4.3 连接迁移原理

QUIC连接迁移的具体机制:

1. 客户端检测到网络切换(WiFi→4G)
2. 客户端用新IP+新端口,但**同样的Connection ID**发送数据
3. 服务端收到后,发现Connection ID已存在
   → 验证这个"新路径"是否来自合法的源(通过PATH_CHALLENGE帧)
4. 验证通过 → 连接迁移完成
5. 后续数据通过新路径传输

安全防护:PATH_CHALLENGE/PATH_RESPONSE帧
  → 防止攻击者伪造IP劫持连接
  → 只有拥有合法密钥的客户端才能完成验证
1
2
3
4
5
6
7
8
9
10
11
12

# 4.4 与TLS的深度融合

QUIC 从设计之初就内置了加密(基于 TLS 1.3),而不是像 HTTP/2 那样"TCP 之上可选 TLS"。

QUIC始终加密:
  ✓ 握手消息:加密
  ✓ 应用数据:加密
  ✓ ACK确认包:加密(TCP中ACK是明文)
  ✓ 连接关闭:加密(TCP中RST是明文)

好处:
  → 中间设备无法基于明文做"智能优化"(这些优化往往有害)
  → 更难以被中间人攻击
  → 协议信令不被篡改
1
2
3
4
5
6
7
8
9
10

# 05.QUIC的多路复用

# 5.1 无队头阻塞的多路复用

QUIC 的多路复用和 HTTP/2 的看起来相似,但底层实现有本质不同:

HTTP/2 over TCP:
  流A数据 | 流B数据 | 流A数据 | 流C数据
  ───────────────────────────────────→ 一个TCP字节流
  如果"流B数据"的包丢了 → 整个TCP字节流阻塞 → A和C都卡住

HTTP/3 over QUIC:
  流A ──────→(独立的QUIC流,自己的序列号)
  流B ──────→(独立的QUIC流,自己的序列号)
  流C ──────→(独立的QUIC流,自己的序列号)
  
  流B丢包 → 只影响流B → 流A和C继续正常传输
1
2
3
4
5
6
7
8
9
10
11
QUIC如何实现流独立?

1. QUIC在UDP之上为每个流维护独立的序号空间
   流A有seqA(0,1,2...),流B有seqB(0,1,2...)
   不再像TCP那样全局一个序号空间

2. 每个流的数据打包在独立的QUIC帧中
   接收方根据帧头中的Stream ID将数据分发到对应的流

3. 流B丢包 → QUIC只等待流B的重传
   流A和流C的数据帧已经到达 → 立即交给上层

4. 关键:UDP不做"按序递交"
   UDP收到什么就向上交什么 → QUIC自己决定哪些递交给哪个流
1
2
3
4
5
6
7
8
9
10
11
12
13
14

回到小吴的痛点②:QUIC 下,视频分片丢包只影响那个分片流,弹幕、推荐列表等独立流完全不受影响。

# 5.2 对比HTTP/2 over TCP

实验:HTTP/2(TCP) vs HTTP/3(QUIC) 在1%丢包率下的表现

场景:20个并发的请求,每个500KB

HTTP/2(TCP):
  1%丢包 → 任何包丢失 → 整个连接暂停
  20个请求互相影响
  总耗时:平均 3.5 秒
  原因:TCP队头阻塞 + 丢包触发拥塞控制降窗

HTTP/3(QUIC):
  1%丢包 → 只有丢失的那个流的包暂停
  20个请求独立传输
  总耗时:平均 1.2 秒
  原因:无队头阻塞 + 每个流独立重传
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5.3 流量控制

QUIC 有两级流量控制:

  • 连接级:限制整个连接的总缓冲区大小
  • 流级:限制单个流的总缓冲区大小

这样设计的好处:某个流接收慢不会阻塞其他流(HTTP/2 over TCP 中,TCP 接收窗口满了,所有流都停)。

# 5.4 优先级设计

QUIC 支持流优先级,HTTP/3 继承了 HTTP/2 的优先级树设计。浏览器可以告诉服务端"CSS 优先,JS 其次,图片最后"。

# 06.QUIC的可靠传输与拥塞控制

# 6.1 丢包与重传

QUIC 在 UDP 之上重新实现了一套可靠传输机制:

QUIC的重传机制相对于TCP的改进:

1. 独立序号空间
   TCP:一个包丢,后续所有包都要等待
   QUIC:每个流独立,A流丢包不影响B流

2. 更精确的RTT估算
   TCP:重传包的RTT计算有歧义(不知道ACK是针对原始包还是重传包)
   QUIC:每个包有唯一的Packet Number,明确区分原始和重传

3. 更多的ACK信息
   QUIC的ACK帧可以携带多达256个ACK范围
   → 服务端清楚地知道哪些包收到了、哪些没收到
   → 更精准的重传决策
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 6.2 可插拔拥塞控制

QUIC 在用户态实现,拥塞控制算法可以随时更换,无需重启。

TCP的拥塞控制:
  → 内核实现 → 更换算法需要加载内核模块或重编译内核
  → 常见的:CUBIC(默认)、BBR(需要4.9+内核)

QUIC的拥塞控制:
  → 用户态实现 → 更换算法只需修改配置文件
  → 支持的:NewReno、CUBIC、BBR、BBRv2、自定义
  
  甚至可以基于机器学习做自适应拥塞控制!
1
2
3
4
5
6
7
8
9

# 6.3 QUIC的纠错机制

QUIC 支持前向纠错(FEC):在发送数据时附带冗余信息,接收方可以通过冗余信息恢复丢失的数据包,而不需要重传。这在高丢包率的弱网环境下效果显著。

# 6.4 与TCP BBR的对比

维度 TCP CUBIC TCP BBR QUIC
队头阻塞 有 有 无
握手RTT 1+2=3 1+2=3 1(0-RTT)
连接迁移 不支持 不支持 支持
拥塞算法 固定 可换(内核) 可插拔(用户态)
应用层可见 不透明 不透明 可观测

# 07.HTTP/3

# 7.1 HTTP/3与QUIC的关系

HTTP/3 是 HTTP 协议在 QUIC 上的映射。核心精神继承自 HTTP/2(多路复用、二进制帧、头部压缩),但传输层替换为 QUIC。

HTTP/3 = HTTP语义 + QUIC传输

HTTP语义不变:
  ✓ GET/POST/PUT/DELETE 方法
  ✓ 状态码 200/404/500
  ✓ 头部 Cookie/Content-Type/Cache-Control
  ✓ QPACK 头部压缩(改进的HPACK,解决队头阻塞)

QUIC提供的新能力:
  ✓ 0-RTT握手
  ✓ 无队头阻塞的多路复用
  ✓ 连接迁移
  ✓ 始终加密
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.2 HTTP/3的改进

QPACK 头部压缩:HTTP/2 的 HPACK 有队头阻塞问题(动态表更新是串行的),QPACK 将静态表和动态表分离,动态表更新通过单向流传输,不会阻塞请求流。

# 7.3 部署现状与挑战

HTTP/3 现状(2025年):

支持HTTP/3的浏览器:  Chrome 87+、Firefox 88+、Safari 14+
支持HTTP/3的服务端:  Nginx(quic分支)、Caddy、LiteSpeed
支持HTTP/3的CDN:     Cloudflare、Google Cloud CDN、AWS CloudFront
全球HTTP/3流量占比:  ~35%(Google服务 > 75%)

主要挑战:
  → UDP被某些企业防火墙/NAT封锁(需要fallback到HTTP/2)
  → QUIC消耗CPU比TCP高(用户态协议栈 vs 内核态)
  → nginx官方HTTP/3支持仍在完善中
  → 调试工具不够成熟(Wireshark支持但不如TCP方便)
1
2
3
4
5
6
7
8
9
10
11
12

# 7.4 升级路径

HTTP/3的渐进式部署:

1. 服务端支持 Alt-Svc 头部
   Alt-Svc: h3=":443"  → 告诉客户端"我也支持HTTP/3"

2. 客户端首次访问用 HTTP/2 → 收到 Alt-Svc → 记住

3. 下次访问 → 直接尝试 HTTP/3 → 失败则回退 HTTP/2

4. 部署CDN支持HTTP/3 → 全球用户自动获得QUIC加速

完全向后兼容:HTTP/3失败时自动降级为HTTP/2
用户无需任何操作即可受益
1
2
3
4
5
6
7
8
9
10
11
12
13

# 08.综合案例:移动端网络连接的四次进化

本章用小吴的视频 App 作为贯穿案例——从最原始的 HTTP/1.1 到 HTTP/3 QUIC,体验四次网络连接进化带来的体验跃升。

# 8.1 案例背景与目标

短视频 App:首页视频流 + 弹幕 + 评论 + 推荐,需要快速加载,支持弱网。对比四种方案在 RTT=100ms、丢包率 1% 的移动网络下的表现。

版本 协议 核心问题
V1 HTTP/1.1 over TCP 6个TCP连接,串行请求
V2 HTTP/2 over TCP TCP队头阻塞 + 3RTT握手
V3 HTTP/2 + TCP优化 有了提升但仍受TCP限制
V4 HTTP/3 over QUIC 0-RTT + 无队头阻塞 + 连接迁移

# 8.2 第一代:HTTP/1.1 over TCP

V1(HTTP/1.1)加载首页:

  需要加载:视频分片 + 弹幕 + 评论 + 推荐列表 + 用户信息 + 广告
  HTTP/1.1:浏览器限制每个域名6个并发连接

  6个连接:
    连接1: TCP握手(100ms) + TLS(200ms) + 请求(100ms) → 400ms
    连接2~6: 并行 → 400ms
    如果还有第7个请求 → 排队等待

  总耗时:~400ms(冷启动),~100ms(热连接Keep-Alive)
  
  冷启动耗时分解:
    DNS解析:    1 RTT = 100ms
    TCP握手:    1 RTT = 100ms
    TLS握手:    2 RTT = 200ms (TLS 1.2)
    HTTP请求:   1 RTT = 100ms
    总计:       5 RTT = 500ms  ← 痛点③
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 8.3 第二代:HTTP/2 over TCP

V2(HTTP/2 over TCP)加载首页:

  只需1个TCP连接承载所有请求
  6个请求并发发送

  连接建立:
    TCP握手(100ms) + TLS 1.3(100ms) = 200ms(冷启动)

  优势:减少了连接数,连接复用,首部压缩节省带宽
  
  但问题来了(痛点②):
    视频分片丢了一个包(移动网络1%丢包率很常见)
    → TCP必须等待重传
    → 弹幕、评论、推荐列表、用户信息的数据已全部到达
    → 但TCP不递交 → 全部卡住!
    
  总耗时:200ms握手 + 如果有丢包额外增加300~500ms

  瓶颈:TCP队头阻塞 —— 无法在TCP层之上解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 8.4 第三代:HTTP/2 + TCP优化

V3(HTTP/2 + TCP优化)加载首页:

  TCP优化:
    → TCP Fast Open(跳过1个RTT)
    → BBR拥塞控制(取代CUBIC,对抗丢包更友好)
    → TCP_NODELAY(禁用Nagle算法,减少延迟)

  冷启动:
    TCP Fast Open: 0 RTT(首次仍需1 RTT)
    TLS 1.3:       1 RTT
    HTTP请求:      1 RTT
    总计:          2 RTT = 200ms

  痛点①(WiFi切4G)仍然存在:
    → TCP连接基于IP四元组 → IP变了连接就断
    → 需要重新DNS + TCP + TLS → 至少2秒
    → 正在播放的视频必定卡住

  痛点②(丢包队头阻塞)仍然存在:
    → TCP层面无论如何优化都无法消除
    → 因为这是TCP按序递交的协议本质
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 8.5 第四代:HTTP/3 over QUIC

V4(HTTP/3 over QUIC)加载首页:

  热启动场景(之前访问过):
    QUIC 0-RTT握手: 0ms
    HTTP请求+响应: 100ms
    总计:          100ms ← 比V1快5倍,比V2快2倍!

  丢包场景(痛点②):
    视频分片丢包 → 等待重传(仅影响视频流)
    弹幕 → 正常加载(独立流)
    评论 → 正常加载(独立流)
    推荐列表 → 正常加载(独立流)

  WiFi切4G场景(痛点①):
    检测到IP变化 → 自动迁移连接
    Connection ID不变 → 0ms切换
    用户无感:视频不卡,弹幕不断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
QUIC给移动App的体验提升:

场景1:早上通勤,地铁里刷视频
  TCP:隧道里信号差 → 丢包 → 队头阻塞 → 卡顿
  QUIC:丢包只影响当前流 → 预加载继续 → 几乎无感

场景2:回到家,WiFi自动连接
  TCP:4G→WiFi切换 → 连接断开 → 重连 → 2秒空白
  QUIC:自动迁移 → 无缝切换 → 视频不中断

场景3:App从后台恢复
  TCP:连接可能已超时断开 → 重新握手 → 1秒延迟
  QUIC:0-RTT恢复 → 立即发送请求 → 毫秒级响应
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.6 四种方案横向对比

维度 V1 HTTP/1.1+TCP V2 HTTP/2+TCP V3 HTTP/2+TCP优化 V4 HTTP/3+QUIC
冷启动握手 5 RTT (500ms) 3 RTT (300ms) 2 RTT (200ms) 1 RTT (100ms)
热启动 2 RTT 2 RTT 1 RTT 0 RTT (0ms)
队头阻塞 HTTP层+TCP层 TCP层 TCP层 无
1%丢包表现 严重卡顿 所有流卡顿 所有流卡顿 仅1个流受影响
WiFi切4G 连接断开(2s+) 连接断开(2s+) 连接断开(2s+) 无缝迁移(0ms)
连接数(6请求) 最多6个 1个 1个 1个(QUIC连接)
加密 可选TLS 可选TLS TLS 1.3 始终加密
协议升级 内核更新 内核更新 内核更新 软件更新
对应痛点 ③ ②③ ①②③ 全部解决
体验进化图(移动网络 RTT=100ms,丢包率=1%):

首页加载耗时(ms)
│
│  500 ████████  V1 HTTP/1.1(冷启动5RTT)
│  300 █████     V2 HTTP/2(冷启动3RTT)
│  200 ████      V3 HTTP/2+优化(2RTT)
│  100 ██        V4 HTTP/3 QUIC(1RTT)
│    0 ▌         V4 热启动(0RTT!)
└──────────────────────────────→

视频中断体验:
  V1-V3 WiFi切4G:  必须重刷(2秒中断)
  V4 WiFi切4G:      无缝(0秒中断)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 8.7 案例升华:为什么HTTP/3是Web的未来

QUIC 的三个革命性设计:

1. 用户态实现(协议民主化)
   不再受限于"等操作系统升级"
   Google通过Chrome更新就能让数亿用户使用新协议
   这是历史上协议部署最快的案例之一

2. 连接=身份(Connection ID)
   连接不再绑定于IP
   → 移动互联网时代的刚需(WiFi↔4G↔5G切换是常态)
   → IoT设备的刚需(设备可能频繁切换网络)

3. 流独立(消除队头阻塞)
   TCP的按序递交假设在Web多路复用时代已经过时
   QUIC用"流"的概念替代"字节流",更符合现代Web的使用模式

HTTP/3 不只是"又一个HTTP版本"——它是HTTP首次抛弃TCP,
标志着互联网传输层进入"用户态可编程"时代。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
未来展望:

WebTransport(基于QUIC):
  → WebSocket的继任者
  → 支持部分可靠性(可选:这个包丢了不重传)
  → 支持多流、无序传输
  → 更适合游戏引擎、实时视频

MASQUE(基于QUIC的代理协议):
  → 让HTTP代理也享受QUIC的高性能

HTTP/3的生态壁垒:
  → 一旦CDN和大型网站都支持HTTP/3
  → 用户端加速普及(Chrome/Safari/Firefox全部支持)
  → 正向循环:支持越多 → 流量越大 → 更多人支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.8 全文知识图谱回顾

                    小吴的TCP之痛
                    │
    ┌───────┬───────┼───────┬───────┐
    │       │       │       │       │
   ①断连   ②队头   ③握手   升级难   明文
    │       │       │       │       │
    ▼       ▼       ▼       ▼       ▼
 IP绑定  按序递交  两次握手  内核实现  TCP暴露
 CID     流独立   合并握手  用户态   始终加密
 [4.2]   [5.1]   [4.1]   [3.2]   [4.4]
    │       │       │       │       │
    └───────┴───────┼───────┴───────┘
                    │
        ┌───────────┴───────────┐
        │                       │
  多路复用 [5章]          拥塞控制 [6章]
  流独立+两级流控        可插拔+BBR+FEC
        │                       │
        └───────────┬───────────┘
                    │
  V1→V2→V3→V4  移动端网络连接的四次进化
   [第8章]      500ms→0ms
                    │
                    ▼
  用户态实现 / 连接=身份 / 流独立
    QUIC的三大革命性设计 [8.7节]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

最终的方法论沉淀——理解 HTTP/3 和 QUIC,都应该掌握三个核心问题:

  1. TCP 缺了什么?(队头阻塞、连接绑定IP、握手RTT多、内核升级慢 → 四个缺陷)
  2. QUIC 做了什么?(UDP之上重建可靠传输、Connection ID、0-RTT、用户态可插拔 → 四个对策)
  3. 对业务有什么用?(移动端弱网体验、WiFi切4G无感、视频/游戏延迟改善 → 三个场景)

把这三个问题问到位,你就从"知道 HTTP/3"进化到了"理解为什么互联网需要 QUIC"。

# 09.思考题与作业

# 9.1 基础思考题

  1. TCP队头阻塞的本质:用自己的话解释"TCP队头阻塞"和"HTTP/2队头阻塞"有什么区别。为什么 HTTP/2 的多路复用不能解决 TCP 队头阻塞?

  2. Connection ID vs 四元组:QUIC 用 Connection ID 替代 TCP 的四元组来标识连接。这带来了连接迁移能力。但 Connection ID 是随机数,为什么不直接用它替代 TCP 的"源端口+目标端口"?

  3. 0-RTT的安全代价:QUIC 的 0-RTT 握手允许客户端在首次握手就发送数据。这带来了什么安全风险?为什么 TLS 1.3 也有 0-RTT,但它的风险比 QUIC 小?

  4. 为什么是 UDP 而不是新 IP 协议:QUIC 基于 UDP。为什么不直接创建一个新的 IP 协议号(如 IP Protocol 142),让 QUIC 直接跑在 IP 之上?这样不就可以去掉 UDP 的 8 字节头部了吗?

# 9.2 进阶思考题

  1. 痛点②的量化分析:HTTP/2 有 6 个并发流,丢包率为 1%,每个流传输 500KB 数据。如果 TCP 的拥塞窗口在丢包时减半,丢一个包后的重传超时是 200ms。计算 1% 丢包率下,HTTP/2(TCP) 和 HTTP/3(QUIC) 的平均总传输时间差。QUIC 能节省多少?

  2. QUIC的CPU代价:QUIC 在用户态实现可靠传输,比内核 TCP 需要更多 CPU。在 10Gbps 带宽下,QUIC 的 CPU 消耗比 TCP 高 20~30%。在什么场景下愿意用 CPU 换 QUIC 的灵活性?在什么场景下宁可忍受 TCP 的限制也要省 CPU?

  3. 中间设备的挑战:很多企业防火墙不允许 UDP 443 以外的流量(QUIC 默认用 UDP 443)。如果 UDP 被封锁,QUIC 如何 fallback 到 TCP?这种 fallback 对性能有什么影响?如何设计探测机制来判断 UDP 是否被封锁?

  4. WebTransport vs WebSocket:WebTransport 基于 QUIC,提供了比 WebSocket 更多的能力(无序传输、部分可靠性、多流)。在什么场景下应该用 WebTransport 而不是 WebSocket?WebTransport 的"部分可靠性"在游戏同步中如何应用?

# 9.3 动手作业

作业一(必做):用 Chrome DevTools 观察 HTTP/3。

  • 打开 Chrome,访问 https://www.google.com。
  • F12 → Network → 右键表头 → 勾选 Protocol 列。
  • 观察哪些请求使用 h3(HTTP/3),哪些用 h2(HTTP/2)。
  • 再访问 https://www.youtube.com,观察视频请求是否使用 QUIC。
  • 用 chrome://net-internals/#quic 查看当前的 QUIC 连接详情(Connection ID、版本、拥塞算法)。

作业二(选做):用 Caddy 部署 HTTP/3 服务。

  • 用 Docker 启动 Caddy(原生支持 HTTP/3)。
  • 用 Chrome 访问你的站点,确认 Protocol 列显示 h3。
  • 用 tcpdump 抓包(注意是 UDP 443,不是 TCP 443):
    sudo tcpdump -i lo0 udp port 443 -w quic.pcap
    
    1
  • 用 Wireshark 打开 quic.pcap,观察 QUIC 包结构:长包头(握手)和短包头(数据传输)。

作业三(架构思考):评估你的项目的 HTTP/3 迁移收益。

  • 分析你负责的 App 或 Web 应用的网络请求特征(连接数、请求频率、用户网络环境)。
  • 评估迁移到 HTTP/3 的预期收益:① 首屏加载提升多少 ② 弱网体验改善多少 ③ WiFi切4G断连问题解决。
  • 列出迁移步骤:CDN 开启 HTTP/3 → 服务端支持 HTTP/3 → 客户端升级网络库。
上次更新: 2026/06/09, 16:25:38
WebSocket实时通信
README

← WebSocket实时通信 README→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式