犀牛鸟 2026 研究笔记 estelledc.github.io

第 1 章:为什么需要 QUIC——当 TCP 成为互联网的瓶颈

阅读门槛说明:本导读从日常场景出发,不要求你事先了解 QUIC 协议本身。但由于 QUIC 建立在网络协议栈之上,后续章节(尤其 ch03 起)会假设你具备 TCP/IP 基础概念(三次握手、序列号、丢包重传)。如果这些对你完全陌生,建议先花 1-2 小时过一遍任何一份”TCP/IP 入门”材料。导读总体 difficulty 标注为 advanced,指的是 目标深度(最终要能读懂 Rust 源码、写调度器),而非 起点要求

一个你每天都在经历的场景

早高峰,你挤上地铁,打开手机继续看昨晚没看完的视频。地铁进站的时候信号还不错,用的是站台的 WiFi。列车一开动,手机自动切到 4G 信号。然后你就看到了那个让人抓狂的转圈圈——视频卡住了,进度条不动了,过了好几秒才恢复。

再想一个场景:你在宿舍用笔记本电脑打开一个网页,页面上有文字、图片、CSS 样式表、JavaScript 脚本。其中一张图片的数据包在网络传输中丢了,需要重传。按理说,文字和 CSS 已经到了,浏览器完全可以先渲染出来。但实际上,整个页面都在等那张丢失的图片——因为它们共享同一条 TCP 连接,一个数据包丢了,后面所有的数据都得排队等。

这两个问题,本质上都是 TCP 协议的设计局限。而 QUIC,就是为了解决这些问题而生的。


TCP:互联网的功臣,如今的瓶颈

TCP 是什么?一个日常类比

想象你要寄一个大型包裹给朋友。你把东西拆成 100 个小箱子,每个箱子贴上编号(1/100, 2/100…),然后交给快递公司。快递公司保证:

  1. 所有箱子都会到达(可靠传输)
  2. 按编号顺序交给你朋友(有序交付)
  3. 如果某个箱子丢了,会重新寄(重传机制)

这就是 TCP 的核心承诺。它在 1981 年被标准化(RFC 793),40 多年来一直是互联网传输层的基石。HTTP、HTTPS、SSH、FTP——几乎所有你日常使用的协议都建立在 TCP 之上。

TCP 的三大根本性限制

但 TCP 的设计是 40 年前的产物。当时的互联网是这样的:带宽以 Kbps 计算,网页只有纯文本,移动网络根本不存在。TCP 的设计假设是:连接两端的 IP 地址不会变,网络带宽很小不需要并行传输,安全性可以由上层协议(TLS)来处理。

这些假设在今天全都站不住脚了。

限制一:队头阻塞(Head-of-Line Blocking)

这是 TCP 最致命的问题。回到快递的类比:假设你朋友要求”必须按编号顺序开箱子”。箱子 1-50 已经到了,但箱子 51 在路上丢了。即使箱子 52-100 已经全部到达,你朋友也不能打开任何一个——他必须等箱子 51 被重新寄到。

在技术层面,TCP 是一个单一字节流。所有数据共享同一个序列号空间。当 Packet #51 丢失时,接收端的内核缓冲区里虽然已经有了 Packet #52-100 的数据,但它不会把这些数据交给应用层——因为 TCP 承诺有序交付。

HTTP/2 试图通过”多路复用”(Multiplexing)来解决这个问题:在一条 TCP 连接上同时传输多个 HTTP 请求/响应。但这只是应用层的优化——底层的 TCP 还是一条字节流。一个包丢了,所有流都被阻塞。这就是为什么 HTTP/2 在高丢包率网络下反而可能比 HTTP/1.1(每个请求一条连接)更慢。

TCP 队头阻塞示意图:

应用层看到的"多个流":
  Stream A: [数据块1] [数据块2] [数据块3]
  Stream B: [数据块1] [数据块2]
  Stream C: [数据块1] [数据块2] [数据块3]

TCP 层实际传输(单一字节流):
  [A1][B1][A2][C1][B2][A3][C2][C3]
       ↑
     这个包丢了
       ↓
  所有后续数据被阻塞,A2、C1、B2、A3、C2、C3 全部等待

限制二:连接建立慢(握手延迟)

TCP 建立连接需要三次握手(3-way handshake):

客户端                     服务端
  |------- SYN -------->|     第 1 次
  |<----- SYN+ACK ------|     第 2 次
  |------- ACK -------->|     第 3 次
  |                      |
  | (现在才能开始传数据) |

这要花 1 个 RTT(Round-Trip Time,往返时间)。如果你在北京访问一个美国的服务器,RTT 大约是 200ms,光建立 TCP 连接就要 200ms。

但这还没完。现代互联网几乎所有通信都要加密,需要在 TCP 之上再做 TLS 握手:

TCP 三次握手:1 RTT
TLS 1.2 握手:2 RTT(通常情况)
TLS 1.3 握手:1 RTT
─────────────────────────
总计:TCP + TLS 1.3 = 2 RTT
      TCP + TLS 1.2 = 3 RTT

2-3 个 RTT,对于北京到美国的连接就是 400-600ms。用户要等半秒多才能看到第一个字节。在移动网络上,RTT 经常超过 100ms,这个延迟就更加明显了。

限制三:无法连接迁移

TCP 连接由四元组(source IP, source port, destination IP, destination port)唯一标识。当你的手机从 WiFi 切到 4G:

这就是地铁里视频卡住的根本原因。TCP 没有”这个连接可以跟着我走”的概念——它把连接绑定在了网络地址上。

为什么不能直接”修”TCP?

你可能会想:既然 TCP 有这些问题,升级一下不就好了?

问题在于:TCP 已经”固化”在了整个互联网基础设施中。

路由器、防火墙、NAT 设备、运营商的中间件——这些设备都理解 TCP 报文格式,并且会对 TCP 进行各种”优化”(或者说干预)。如果你修改 TCP 的报文格式,这些中间设备可能会丢弃你的数据包,因为它们不认识新的格式。

这种现象叫做协议骨化(Protocol Ossification)。TCP 的报文头是明文的,中间设备可以看到并操作它。40 年间,全球数十亿网络设备已经形成了对 TCP 格式的依赖。想要升级 TCP,你需要升级全球所有的路由器和防火墙——这显然是不可能的。

Google 的工程师们想到了一个聪明的办法:绕过 TCP,在 UDP 上重新建造


QUIC:从零开始的传输层协议

为什么选择 UDP 作为基础?

UDP(User Datagram Protocol)是互联网另一个基础传输协议。它非常简单——基本上就是”发出去就不管了”,不保证可靠、不保证有序、不做拥塞控制。

这正好是 QUIC 需要的。UDP 的好处是:

  1. 所有网络设备都认识 UDP——不会被防火墙拦截(大部分情况下)
  2. UDP 头部极其简单——只有 8 字节,没有什么可以被中间设备”干预”的
  3. UDP 不做任何假设——QUIC 可以在 UDP 之上自由实现自己的可靠性、有序性和拥塞控制

类比:TCP 就像坐公交车,路线是固定的(协议规则),你不能让司机改道。UDP 就像打车,你告诉司机目的地,怎么走你来决定。QUIC 选择了打车,然后自己当导航——它在 UDP 之上实现了比 TCP 更好的可靠传输。

QUIC 的四大核心承诺

承诺一:0-RTT 连接建立

QUIC 把传输层握手和加密握手合为一体。首次连接只需 1 RTT:

客户端                         服务端
  |--- Initial (含 TLS ClientHello) --->|  
  |<-- Initial (含 TLS ServerHello) ----|  1 RTT 后就能发数据
  |--- Handshake + 应用数据 ----------->|

更厉害的是,如果你之前连接过这个服务器,QUIC 支持 0-RTT

客户端                         服务端
  |--- Initial + 0-RTT 应用数据 ------->|  第一个包就带数据!
  |<-- Initial + Handshake -------------|

第一个数据包就能携带应用数据!在用户的体验上,这意味着”瞬间”打开页面。

承诺二:无队头阻塞的多路复用

QUIC 原生支持多个独立的(Stream)。每个流有自己的序列号空间,互不影响:

QUIC 多流传输:

  Stream 0 (CSS):  [帧1] [帧2] ✓ 到达即交付
  Stream 2 (JS):   [帧1] [帧2] [帧3] ✓ 到达即交付
  Stream 4 (图片): [帧1] [✗丢失] [帧3]  ← 只有这个流等重传
  Stream 6 (HTML): [帧1] [帧2] ✓ 到达即交付

图片流的丢包不会影响 CSS、JS、HTML 的交付。浏览器可以先渲染文字和样式,图片晚一点再补上来。这就是 QUIC 解决队头阻塞的方式——在传输层就把”流”作为一等公民。

承诺三:连接迁移

QUIC 引入了 Connection ID(连接 ID)的概念。连接不再由 IP+端口标识,而是由一个随机生成的 ID 标识:

WiFi 网络下:
  源 IP: 192.168.1.100, 端口: 12345
  Connection ID: 0x1a2b3c4d
  → 服务端认识这个连接

切换到 4G:
  源 IP: 10.0.0.50, 端口: 54321  ← IP 和端口都变了!
  Connection ID: 0x1a2b3c4d      ← 但 Connection ID 没变
  → 服务端说:"哦,还是你!"继续传数据

地铁里视频不再卡住——因为连接跟着你走,不跟着 IP 走。

承诺四:全程加密

QUIC 的报文头部大部分都是加密的。不像 TCP 的头部是明文的(中间设备可以看到序列号、窗口大小等),QUIC 只暴露极少量信息。这意味着:

  1. 中间设备无法干预 QUIC 的内部机制——防止协议骨化
  2. 窃听者看不到你在传输什么——天生隐私保护
  3. 未来升级协议不需要全球设备配合——保持可演进性

QUIC 的前世今生:从 Google 实验到国际标准

2012-2015:Google QUIC(gQUIC)

2012 年,Google 的 Jim Roskind 提出了 QUIC 的概念。最初它叫”Quick UDP Internet Connections”,是 Chrome 浏览器的一个实验性功能。Google 在自家的服务器和 Chrome 之间部署了 gQUIC,测试数据令人兴奋:

但 gQUIC 是 Google 的私有协议,使用了自己的加密方案(不是 TLS),格式也不公开。

2016-2021:IETF 标准化

2016 年,IETF(互联网工程任务组)开始标准化 QUIC。这是一个巨大的工程——把 Google 的私有实验变成所有人都能实现的公开标准。关键变化包括:

2021 年 5 月:RFC 9000 发布

经过 5 年的讨论和 34 个草案版本,QUIC v1 正式成为互联网标准:

RFC 内容
RFC 9000 QUIC 传输协议核心
RFC 9001 QUIC 中的 TLS 使用
RFC 9002 QUIC 丢包检测与拥塞控制
RFC 9369 QUIC v2(密码学升级)

2024-2025:多路径 QUIC

多路径 QUIC(Multipath QUIC)的标准化工作正在进行中(draft-ietf-quic-multipath)。这是 TQUIC 项目的核心方向——让一个 QUIC 连接同时使用 WiFi 和 4G,实现带宽聚合和无缝切换。


今天的 QUIC 已经在哪里?

你可能没意识到,但你每天都在用 QUIC:

在浏览器里,你可以打开 chrome://net-internals/#quic 看看当前有多少连接在使用 QUIC。


六大开源实现:为什么不是只有一个?

标准有了,为什么会有 6 个(甚至更多)不同的实现?

因为设计空间太大了。RFC 9000 定义了协议的规则(”你必须这样做”),但在实现层面留下了大量选择:

选择 可选方案
编程语言 Rust / C / C++ / Go / Python…
架构模式 sans-io / event-driven / kernel-integrated
拥塞控制 CUBIC / BBR / BBR2 / BBR3 / COPA
TLS 库 BoringSSL / OpenSSL / rustls / GnuTLS
I/O 模型 epoll / io_uring / XDP / 用户态
多路径 支持 / 不支持

这就像”造一辆车”的标准告诉你必须有四个轮子、方向盘、刹车,但没规定发动机用汽油还是电动、车身用钢材还是碳纤维。不同的实现根据自己的使用场景和设计理念做出不同的选择。

六个实现的一句话定位

项目 Stars 语言 一句话定位
quiche ~11.4K Rust Cloudflare 的生产级实现,sans-io 设计,CDN 规模验证
msquic ~9.7K C 微软的内核级实现,Windows/Linux 双平台,极致性能
quinn ~5.2K Rust 最简洁的 Rust 实现,学习 QUIC 源码的最佳入口
s2n-quic ~1.3K Rust AWS 的安全优先实现,每行代码追溯到 RFC
TQUIC ~1.4K Rust 腾讯的多路径实现,唯一原生支持 Multipath QUIC
ngtcp2 ~1.3K C curl 的 QUIC 后端,TLS 无关设计,万能适配器

为什么你应该关注 QUIC?

对于犀牛鸟参赛者

TQUIC 是犀牛鸟 2026 的项目之一。它的核心方向是多路径 QUIC——一个协议层面正在标准化、实现层面还很不成熟的领域。这意味着:

  1. 贡献机会多:多路径调度算法、路径管理、性能优化都有大量可做的事
  2. 技术壁垒适中:代码是 Rust,规模适中(~300 文件),有清晰的模块边界
  3. 影响力大:你的代码可能会用在微信和 QQ 的真实场景中

对于想进入网络领域的同学

QUIC 是过去 10 年传输层最大的变革。理解 QUIC 意味着:

对于想学 Rust 的同学

6 个实现中有 4 个用 Rust 写成(quiche, quinn, s2n-quic, TQUIC)。QUIC 实现是学习 Rust 系统编程的绝佳素材——它涉及异步、trait 设计、零拷贝、FFI、性能优化等几乎所有 Rust 的核心话题。


本章小结

问题 答案
TCP 最大的问题是什么? 队头阻塞、连接建立慢、无法迁移
为什么不直接升级 TCP? 协议骨化——全球中间设备依赖 TCP 格式
QUIC 建在什么上面? UDP——所有设备都认识,且足够简单
QUIC 的核心承诺? 0-RTT、无 HOL 阻塞、连接迁移、全程加密
为什么有 6 个实现? 设计空间大,不同场景有不同最优解
TQUIC 的独特价值? 唯一原生支持多路径 QUIC 的开源实现

读完本章你能做什么

  1. 能向不懂技术的朋友解释”为什么手机切网络时视频会卡”
  2. 能说出 TCP 的三个根本性限制
  3. 能解释 QUIC 为什么选择建在 UDP 之上
  4. 能列出 QUIC 的四大核心优势
  5. 能说出六个开源 QUIC 实现的名字和各自一句话定位

常见误区

误区一:QUIC 就是 “UDP + TLS”

正确理解:QUIC 在 UDP 之上重新实现了可靠传输(类似 TCP 的功能)+ 多路复用 + 连接管理 + 拥塞控制。TLS 1.3 只是其中的加密部分。把 QUIC 说成”UDP + TLS”,就像把汽车说成”轮子 + 发动机”——缺了太多东西。

误区二:QUIC 比 TCP 快是因为 UDP 比 TCP 快

正确理解:UDP 本身并不”快”——它只是不做任何处理就把数据丢出去。QUIC 快的原因是更好的连接建立(0-RTT)、消除了队头阻塞、以及更先进的拥塞控制算法。QUIC 在 UDP 之上做的可靠性保证并不比 TCP 少。

误区三:QUIC 会完全取代 TCP

正确理解:短期内不会。TCP 仍然适合很多场景(内网通信、长连接数据库、低延迟要求不高的场景)。而且很多防火墙仍然会阻拦 UDP 流量,QUIC 需要回退到 TCP 的能力。两者会长期共存。

章节自测

  1. 你在手机上刷短视频,坐地铁经过隧道时 WiFi 切到 4G。用 TCP 连接会发生什么?QUIC 的连接迁移如何避免这个问题?
  2. HTTP/2 已经支持多路复用了,为什么还有”队头阻塞”问题?QUIC 是怎么从根本上解决的?(提示:思考 TCP 层 vs 应用层的关系)
  3. 如果你要给一个企业内网系统加 QUIC 支持,最可能遇到的阻力是什么?你会如何说服运维团队?

导读目录:README.md 下一章:第 2 章 · 阅读路线图与前置知识