上一篇:《IM 长连接与消息存储:从能用到可靠》
那篇讲的是"消息为什么可信"——ACK、幂等、Outbox、本地库。
这篇换个角度:同样是长连接,传输层本身还能不能更快、更抗弱网?
一、我的第一反应:这又是哪个第三方 SDK?
前阵子跟同事聊长连接,他说了一句:
"我们用的是 QUIC。"
我脑子里的第一反应是:又是一个要 Pod 引入的第三方轮子吧?
结果他给我看了两眼代码——没有 Podfile,没有 Package.swift 依赖,只有两行:
let options = NWProtocolQUIC.Options()
let parameters = NWParameters(quic: options)
Network 是 iOS 系统自带的框架,NWProtocolQUIC 是苹果对 QUIC 的原生实现,随系统一起装进手机里(iOS 15+)。
也就是说:
QUIC 不是库,是一套协议标准(IETF RFC 9000 系列)。
就像 "HTTP" 不是某个库一样,QUIC 是"协议",而 NWProtocolQUIC、quic-go、Chromium 的 QUICHE 是各家对它的实现。
这一下把我的认知打散了重来。于是我去补了一遍,下面是我理解的 QUIC。
二、先用一句话说清 QUIC 是什么
QUIC = 把 TCP 的可靠传输 + TLS 1.3 的加密 + 多路复用,重做在 UDP 之上。
分层看更直观:
┌────────────────────────────────────────┐
│ 应用层 你自己的业务协议(protobuf 等) │
├────────────────────────────────────────┤
│ QUIC(可靠 + 加密 + 多路复用) │
├────────────────────────────────────────┤
│ UDP(只负责把包丢出去) │
├────────────────────────────────────────┤
│ IP │
└────────────────────────────────────────┘
UDP 只做一件事:把数据报发出去、带上端口、能穿过 NAT。它不保证顺序、不保证到达、不重传、不控速。
QUIC 在它上面补齐了这些东西:
| 能力 | UDP 原生 | QUIC 补上 |
|---|---|---|
| 可靠性(丢包重传、有序交付) | ❌ | ✅ |
| 加密 | ❌ | ✅ TLS 1.3 内置 |
| 多路复用(多 stream) | ❌ | ✅ |
| 拥塞控制 | ❌ | ✅ 可插拔 |
| 流量控制 | ❌ | ✅ 每个 stream 独立 |
| 连接迁移 | ❌ | ✅ Connection ID |
所以最准确的说法是:QUIC 是"用 UDP 当车"的可靠传输协议,不是"UDP 加了个壳",而是把 TCP 那套东西重新实现了一遍。
三、为什么非要套在 UDP 上?
这是我觉得最反直觉的一点。既然要重造 TCP,为什么不直接定义一个新传输层协议?
原因很现实:互联网上的 NAT、防火墙、运营商设备,只认 TCP 和 UDP。
- 新协议号 → 大量中间设备直接丢弃,包根本到不了服务端
- 套进 UDP → 现有网络都能转发,顶多被限速或 QoS
QUIC 的代价也就来自这里:它唯一怕的东西,就是"UDP 被封"。有些公司网络、校园网、部分运营商会对 UDP 限速甚至直接封禁,表现就是"QUIC 一直连不上"。所以成熟的 QUIC 服务通常会准备"TCP + TLS 降级通道",浏览器用 HTTP/3 时也是这么回退的。
四、QUIC 的 6 个核心技术点
4.1 握手合并:从 3 个 RTT 到 1 个,甚至 0 个
传统 HTTPS 要串行做三件事:
TCP 三次握手(1 RTT)
→ TLS 握手(1~2 RTT)
→ 才开始发 HTTP 请求
QUIC 把"传输握手"和"加密握手"合并成一次:
UDP(不用三次握手)
→ QUIC 握手(内含 TLS 1.3)
→ 直接发数据
首次连接:1 RTT
会话恢复:0 RTT(用之前缓存的密钥直接发)
对 IM 这种"打开 App 就要拉离线消息"的场景,省掉的这一两个 RTT 是能感觉到的。
0-RTT 有个坑:它存在重放攻击风险,所以只适合幂等请求(比如"拉取列表"),不适合"转账""发红包"这类操作。
4.2 多路复用:真正解决队头阻塞
TCP 是一条"单车道":包 A 丢了,后面的 B、C 全部要等 A 重传。
TCP(单流):
A 丢 → [A 重传中] B 排队 C 排队 D 排队 …
→ 用户看到"卡住,然后一口气全来"
QUIC(多 stream):
stream1: A 丢 → 只影响 stream1
stream2: B、C 照常收发
注意:QUIC 内部每条 stream 仍是有序的,但 stream 之间互不阻塞。所以"无队头阻塞"指的是跨流,不是"流内乱序"。
4.3 连接迁移:切网不再必断
TCP 连接靠"四元组"(源 IP、源端口、目的 IP、目的端口)标识,手机从 WiFi 走到 4G,IP 一变,连接就废了。
QUIC 用 Connection ID 标识连接,跟 IP 无关:
WiFi 下:Client(192.168.1.5) ─┐
├─ 同一个 Connection ID,连接不断
4G 下: Client(10.0.0.8) ─┘
理论上切网可以"无缝";实际能不能不断,还取决于服务端是否支持、以及中间设备是否放了行。
4.4 加密不是可选项:TLS 1.3 写死在协议里
这是 QUIC 和 WebSocket 最大的理念差别之一:
- WebSocket:
ws://是明文的,wss://才加密,加不加你自己选 - QUIC:协议规定必须用 TLS 1.3,没有"不加密的 QUIC"
而且它不是简单"套一层 TLS",而是把 TLS 1.3 的握手拆开、嵌进 QUIC 自己的包里:
传统 TLS 1.3:TCP 上跑 TLS 记录层,再包应用数据
QUIC: 只借 TLS 1.3 的握手来协商密钥
握手完成后,密钥直接保护 QUIC 包(AEAD + 包头保护)
没有 TLS 记录层
所以 QUIC 的 Initial 包本身就是加密的,ClientHello 是塞在 QUIC 包里传的。
4.5 拥塞控制可插拔
TCP 的拥塞控制算法通常焊死在系统内核里,想换很麻烦。QUIC 在用户态实现,算法可以换(CUBIC、BBR 等),也方便按业务调参、做实验。
对普通开发者,这条感知不强;但对"弱网国家/地区"的产品,这是实打实的优化空间。
4.6 有"流",但没有"消息"
这点特别容易误解。QUIC 提供了 stream(字节流),但它不知道什么叫"一条消息"。
QUIC 只保证:stream 里的字节按序到达
不保证: 这些字节怎么切成"一条业务消息"
所以应用层仍然要自己做消息边界,最常见的就是长度前缀:
[4字节长度][XXX包头][payload][4字节长度][XXX包头][payload]...
(上面 XXX包头 就是各家自定义协议的字段,比如魔数、命令字、序号——具体长什么样由业务自己定。)
五、和 WebSocket 到底差在哪
一句话:WebSocket 是应用层协议,QUIC 是传输层协议,两者不在一个楼层。
| 维度 | WebSocket | QUIC |
|---|---|---|
| 所在层 | 应用层(RFC 6455) | 传输层(RFC 9000) |
| 底层 | TCP | UDP |
| 握手 | 借 HTTP Upgrade → 101 |
UDP 上直接做 QUIC + TLS 1.3 握手 |
| 加密 | wss 才有 | 强制 TLS 1.3 |
| 消息边界 | 帧自带边界 | 无,应用层自己切 |
| 队头阻塞 | 有(TCP 单流) | 跨流无 |
| 切网 | 必断重连 | 可迁移 |
| 代理/防火墙 | 80/443,基本都放行 | UDP + 自定义端口,容易被限 |
| 抓包调试 | DevTools / Charles 全支持 | TLS 1.3 密文,排障成本高 |
顺便纠正一个常见误解:
QUIC ≠ HTTP/3。
HTTP/3 是跑在 QUIC 上的应用层协议;QUIC 本身只是个"运输工具"。
你可以用 QUIC 跑 HTTP/3,也可以用它跑自己的私有协议(靠 ALPN 区分)。
六、什么时候"不该"用 QUIC
不是所有场景都适合,踩过才知道:
| 场景 | 建议 |
|---|---|
| 用户网络环境复杂(企业代理、校园网封 UDP) | 准备 TCP/TLS 降级 |
| 需要浏览器直接访问 | 走标准 HTTP/3,别自定义 ALPN |
| 团队没有抓包/排障能力 | 慎用,出问题很难查 |
| 只是低频请求(一天几次) | 直接 HTTP 更省事 |
七、写在最后
QUIC 给我的最大触动不是"快",而是它把传输层的可靠性、加密、多路复用打包成了一个默认选项——你不用再纠结"要不要上 TLS""要不要拆多连接",协议层面已经替你做完了。
但它也把复杂度转移给了应用层:消息边界、请求配对、心跳、重连、鉴权,一个都不能少。这部分恰好是上一篇 《IM 长连接与消息存储:从能用到可靠》 讲的内容。
下一篇:在 iOS 上真正把 QUIC 用起来——连接怎么建、心跳怎么发、鉴权门闩怎么设计、断线重连怎么不丢消息,以及我在这个过程中踩过的坑。
如果这篇帮你搞清了 "QUIC 到底是什么",点个赞让我知道;有不同理解也欢迎在评论区聊。