网络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 连接

    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:用于日常传输数据

    Packet Header

Connection ID

QUIC 握手的主要目的就是协商 Connection ID

  • 协商完成后,后续传输时只需要固定连接 ID,从而实现连接迁移。
  • 首次建立连接时,所用的 Long Packet Header 需要双方连接 ID。
  • 协商完成后,日常传输数据所用的 Short Packet Header 只需要目标连接 ID。

Packet Number

Packet Number 严格递增,具有唯一性。

好处

  1. 精确计算 RTT,没有 TCP 重传的歧义性问题。
  2. 支持乱序确认

精确计算 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 级别的流量控制

参考 Flow control in QUIC

  • 接收窗口的左边界:取决于接收到的最大 Offset

    • TCP 接收窗口只有在前面所有的 Segment 都接收的情况下才会移动左边界。

    • QUIC 接收窗口只要收到了更大的 Offset,就会移动左边界

  • 接收窗口的右边界:当已收到并被上层读取的数据大小超过最大接收窗口的一半后。

    • 最大接收窗口、接收窗口的右边界均向右移动,同时给发送方发送 window_update 帧

    • 发送饭收到 window_update 帧 后,发送窗口的右边界也向右移动。

      接收窗口触发的滑动

3.2、Connection 流量控制

Connection 级别的窗口,相当于各个 Stream 窗口之和。

  • 接收窗口大小 = 各个 Stream 接收窗口大小之和

  • 可用窗口大小 = 各个 Stream 可用窗口大小之和

    Connection 流量控制

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 处理,甚至可能会被直接丢包。

posted @ 2023-07-09 20:06  Jaywee  阅读(92)  评论(0编辑  收藏  举报

👇