网络3️⃣QUIC
TCP 缺陷
① 升级困难
升级 TCP 的工作很困难
- TCP 是内核态实现的,应用程序只能使用不能修改。
- 如果要想升级 TCP 协议,那么只能升级内核。
- 涉及底层软件和运行库的更新,服务程序就需要回归测试是否兼容新的内核版本。
- 因此服务器的内核升级也比较保守和缓慢。
② 建立连接的延迟
TCP 建立连接存在延迟
- HTTP 基于 TCP 实现,通信之前需要进行 TCP 三握。
- HTTPS 加入了 SSL/TLS,通信之前需要进行 TCP 三握 + TLS 四握(更增加了延迟)。
- TCP Fast Open 解决了 TCP 三握的时延,但需要服务端和客户端的操作系统同时支持,而市面上有很多老式操作系统不支持。
- TLS 位于应用层,TCP 位于内核:无法同时握手,且 TLS 无法对 TCP 首部加密(存在安全问题)
③ 队头阻塞
TCP 存在队头阻塞问题
- TCP 是字节流协议,必须保证收到的字节数据是完整且有序的。
- 如果某个序列号的 TCP 段丢失,所有后续序列号的 TCP 段无法被应用层读取,必须等待重传成功。
④ 网络迁移
网络迁移需要重新建立 TCP 连接
-
TCP 通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接
-
如果网络发生变化(如移动网络到 WiFi),会导致 IP 地址发生改变,必须断开并重新建立 TCP 和 TLS 连接。
基于 UDP 实现的
QUIC
,可以解决 TCP 的上述缺陷。快速 UDP 互联网连接(Quick Udp Internet Connection)
1、实现可靠传输
报文结构
-
UDP 数据报 = UDP 首部 + UDP 载荷
-
QUIC packet 报文 = QUIC packet 首部 + QUIC packet 载荷
-
QUIC frame = QUIC frame 首部 + QUIC frame 载荷
-
HTTP/3 frame = HTTP/3 frame 首部 + HTTP/3 载
首部结构
在 HTTP/3 中,UDP 首部与 HTTP 首部之间有 2 层首部。
-
Packed Header
-
QUIC Frame Header
1.1、Packet Header
QUIC 包首部
类型:首次连接时和日常传输数据时,使用不同的首部。
-
Long Packet Header:用于首次建立连接。
-
Short Packet Header:用于日常传输数据。
Connection ID
QUIC 握手的主要目的就是协商
Connection ID
。
- 协商完成后,后续传输时只需要固定连接 ID,从而实现连接迁移。
- 首次建立连接时,所用的
Long Packet Header
需要双方连接 ID。 - 协商完成后,日常传输数据所用的
Short Packet Header
只需要目标连接 ID。
Packet Number
Packet Number
严格递增,具有唯一性。
好处:
- 精确计算 RTT,没有 TCP 重传的歧义性问题。
- 支持乱序确认
精确计算 RTT
- TCP 重传歧义性问题:
- 客户端重传报文的 seq 和原始报文的 seq 相同,则服务端针对这两个报文的 ACK 也相同。
- 客户端无法判断 ACK 报文是针对原始报文/重传报文的响应(即产生歧义),无法精确计算 RTT。
- RTO 基于 RTT 来计算的,则 RTO 也会不精确,可能导致重传的概率事件增大。
- QUIC 避免歧义:
- QUIC 触发重传机制时,重传报文的 Packer Number 与原始报文不同。
- 可以更加精确地计算报文 RTT。
支持乱序确认
- TCP 只能有序确认:
- 发生数据丢失时,接收方窗口会阻塞在原地,不会继续向右滑动。
- 引起 TCP 队头阻塞。
- QUIC 支持乱序确认:。假如发生数据丢失
- 接收端:只要有新的已接收包数据包确认,窗口就会继续向右滑动。
- 发送端:得知数据包丢失后,就会将需要重传的数据包放到待发送队列,重新编号后发送给接收端。
1.2、QUIC Frame Header
一个 QUIC Packet 可以存放多个 QUIC Frame。
Frame 有不同功能的类型。
示例:Stream 类型的 Frame,格式如下。
-
Stream ID:用于区分并发传输的不同 HTTP 消息(类似 HTTP/2 的 Stream ID)。
-
Offset:用于保证数据的顺序性和可靠性(类似 TCP 的 Seq)
-
Length:指明 Frame 数据的长度。
前面说到:Packet Header 中的
Packet Number
严格递增。
- 避免了 TCP 重传的歧义性,支持乱序确认。
- 但无法实现类似 TCP 的顺序性和可靠性。
引入 Frame Header 的意义:
- 通过
Stream ID
+Offset
字段信息实现数据的有序性。 - 比较两个数据包的
Stream ID
+Offset
,如果都一致说明数据包内容一致。
2、解决 TCP 队头阻塞
2.1、队头阻塞
HTTP/1.1 和 HTTP/2 均存在队头阻塞问题。
HTTP/1.1
基于请求-响应模型。- 同一个连接中,HTTP 完成一个事务(请求-响应),才能处理下一个事务。
- 如果等待响应的时间过长,客户端无法发送后续请求(即 HTTP 响应的队头阻塞)。
HTTP/2
引入 Stream 概念。- 多个 Stream 复用同一条 TCP 连接,Stream 可以包含多个 Message,Message 可以包含多个 Frame。
- 使用唯一的
Stream ID
区分 HTTP 请求,则通信双发可以并行交错地发送 Message(解决了 HTTP/1.1 队头阻塞)。 - HTTP/2 基于 TCP 传输数据,因此存在 TCP 队头阻塞问题。
TCP 队头阻塞
窗口知识点 👉 TCP-可靠性-窗口
- 发送窗口的队头阻塞:接收到已发送数据的 ACK 后,发送窗口才会向右移动。
- 如果某个 TCP 段(或者其 ACK)丢失,发送方不会移动窗口,就无法发送新的数据。
- 此 TCP 段的首个字节,就相当于队头。
- 接收窗口的队头阻塞:接收到有序数据时,接收窗口才会向右移动。
- 如果收到不连续的数据,接收方不会移动窗口,就无法接收新的数据。
- 如果某个 TCP 段超过接收窗口范围,就会被丢弃。
2.2、没有队头阻塞的 QUIC
QUIC 参考了 HTTP/2 Stream
- 一条 QUIC 连接可以并发发送多个 Stream,每个 Stream 相当于一个 HTTP 请求。
- 滑动窗口:
- HTTP/2 中的 Stream 共享一个滑动窗口,一个丢包将导致整条 TCP 连接的队头阻塞。
- QUIC 中的每个 Stream 都分配了独立的滑动窗口,多个 Stream 之间相互独立,一个阻塞不会影响其它 Stream。
3、流量控制
QUIC 实现流量控制的方式
- window_update 帧:接收方告诉对端,自己可以接收的字节数。
- BlockFrame:发送方告诉对端,由于流量控制被阻塞了无法发送数据。
QUIC 实现流量控制的级别
- Stream 级别:
- 每个 Stream 相当于一个 HTTP 请求,拥有独立的滑动窗口。
- 因此每个 Stream 都可以做流量控制,防止单个 Stream 消耗 Connection 的全部接收缓冲。
- Connection 级别:限制连接中所有 Stream 相加起来的总字节数,防止发送的数据超过连接的缓冲容量。
3.1、Stream 级别的流量控制
-
接收窗口的左边界:取决于接收到的最大 Offset。
-
TCP 接收窗口只有在前面所有的 Segment 都接收的情况下才会移动左边界。
-
QUIC 接收窗口只要收到了更大的 Offset,就会移动左边界
-
-
接收窗口的右边界:当已收到并被上层读取的数据大小超过最大接收窗口的一半后。
-
最大接收窗口、接收窗口的右边界均向右移动,同时给发送方发送 window_update 帧。
-
发送饭收到 window_update 帧 后,发送窗口的右边界也向右移动。
-
3.2、Connection 流量控制
Connection 级别的窗口,相当于各个 Stream 窗口之和。
-
接收窗口大小 = 各个 Stream 接收窗口大小之和
-
可用窗口大小 = 各个 Stream 可用窗口大小之和
4、对拥塞控制改进
QUIC 默认使用 TCP 的 Cubic 拥塞控制算法(慢启动、拥塞避免、拥塞发生、快恢复),
也支持 CubicBytes、Reno、RenoBytes、BBR、PCC 等拥塞控制算法。
- QUIC 处于应用层:不需要操作系统和内核支持。
- 方便实现不同的拥塞控制算法,迭代速度快。
- 可以针对不同应用设置不同的拥塞控制算法。
- TCP 处于操作系统内核:需要端到端的网络协议栈支持。
- 操作系统和内核的部署成本高,升级周期长,算法迭代速度慢。
- 对系统所有应用生效,无法为应用定制化。
5、连接
5.1、更快的连接建立
-
HTTP/1.x 和 HTTP/2:
- TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层。
- 需要分批次握手,先 TCP 握手(1RTT),再 TLS 握手(2RTT),耗时
3RTT
。
-
HTTP/3:
- QUIC 内部包含 TLS/1.3,它在自己的帧会携带 TLS 里的“记录”。
- 首次连接:在传输数据前进行 QUIC 握手,只需
1RTT
即可完成建立连接与密钥协商。 - 恢复连接:应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到
0RTT
的效果。
5.2、连接迁移
- HTTP 1.x 和 HTTP 2:
- 基于 TCP,通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。
- 如果 IP 地址变化就需要重新连接(e.g. 从 WiFi 切换到移动数据),TCP 三握+TLS 四握会给用户带来体验上的卡顿。
- HTTP 3:
- 基于 QUIC,通过连接 ID 来标记通信的两个端点。
- 即使 IP 地址变化,只要仍保存上下文信息(连接 ID、TLS 密钥等),就可以复用连接。
总结:QUIC 是一个基于 UDP 的,伪 TCP + TLS + HTTP/2 的多路复用的协议。
现状:QUIC 是新协议,很多网络设备不兼容,会当作 UDP 处理,甚至可能会被直接丢包。