互联网协议 — QUIC 快速 UDP 互联网连接

目录

文章目录

  • 目录
  • QUIC
  • 为什么 TCP 会被 UDP 取代?
    • 拥塞控制
    • 三次握手
    • 重传机制
    • 队头拥塞(head of line blocking)/多路复用
    • 用 TCP 还是 UDP?
    • 为什么不基于 UDP 使用 SCTP?
    • 协议僵化(Ossification)
    • 小结
  • QUIC 的特性
    • 基于 UDP
    • 可靠性
    • 数据流
    • 有序交付
    • 快速握手
    • 安全性
    • 连接迁移
    • 解决队头阻塞
    • HTTP/3 支撑
  • QUIC 的工作原理
    • 连接
    • 数据流
    • 用户态实现
    • API
  • 参考文档

QUIC

QUIC(Quick UDP Internet Connections,快速 UDP 互联网连接)是一种实验性的网络传输协议。从网络层级来看,QUIC 是类似于 TCP,UDP 和 SPDY 的数据传输协议,目前正在由 Internet 工程任务组(IETF)进行标准化。

关于 QUIC 的研究始于 2010 年代初期,最初的 QUIC 协议由 Jim Roskind 在 Google 设计并于 2012 年实现。当时 Google 希望创建一个更快,更强调性能的数据传输协议来代替 HTTPS/HTTP。经过 Google 的扩大试验后,于 2013 年向全世界公开发布。

QUIC 借鉴了 TCP、UDP 和 TLS(用于加密)的原理和功能,在这个基础上优化了传输的速度。QUIC 的数据传输从第一个数据包传送(0-RTT)开始立即开始,从而减少了应用程序延迟时间。并且可以在数据量已满时调整管理流程(拥塞控制),从而更快更安全。QUIC 协议在登录成功、推拉流成功的耗时,大幅低于 TCP 协议,优化百分比在 30% 以上,极端场景甚至超过 90%。

互联网协议 — QUIC 快速 UDP 互联网连接_第1张图片

为满足不局限于 HTTP 的传输需求,IETF QUIC 协议的架构被分为两个独立的层:传输层 QUIC 和 “基于 QUIC 的 HTTP”(HTTP over QUIC)层。

2018 年,基于 QUIC 协议的 HTTP(HTTP over QUIC)也就是 HTTP/3,正式被确定为下一代网络规范。HTTP/3 作为超文本传输协议的下一个主要迭代版本,目前仅被全球 3% 的互联网网站所使用。但好消息是,微软、谷歌等科技巨头都表现出了浓厚的兴趣并付出了实际的支持行动,结合 QUIC 协议深度优化信令服务,新形态的互联网技术,可能不用等太久了。

互联网协议 — QUIC 快速 UDP 互联网连接_第2张图片

为什么 TCP 会被 UDP 取代?

TCP 协议可以说是今天互联网的基石,作为可靠的传输协议,在今天几乎所有的数据都会通过 TCP 协议传输,然而 TCP 在设计之初没有考虑到现今复杂的网络环境,当你在地铁上或者火车上被断断续续的网络折磨时,你可能都不知道这一切可能都是 TCP 协议造成的。

底层的数据传输协议在设计时必须要对带宽的利用率和通信延迟进行权衡和取舍,所以想要解决实际生产中的全部问题是不可能的,TCP 选择了充分利用带宽,为流量而设计,期望在尽可能短的时间内传输更多的数据

在网络通信中,从发送方发出数据开始到收到来自接收方的确认的时间被叫做往返时延(Round-Trip Time,RTT)。弱网环境是丢包率较高的特殊场景,TCP 在类似场景中的表现很差,当 RTT 为 30ms 时,一旦丢包率达到了 2%,TCP 的吞吐量就会下降 89.9%,从下面的表中我们可以看出丢包对 TCP 的吞吐量极其显著的影响:

互联网协议 — QUIC 快速 UDP 互联网连接_第3张图片

下面将分析在弱网环境下(丢包率高)影响 TCP 性能的三个原因:

  1. TCP 的拥塞控制算法会在丢包时主动降低吞吐量;
  2. TCP 的三次握手增加了数据传输的延迟和额外开销;
  3. TCP 的累计应答机制导致了数据段的传输;

在上述的三个原因中,拥塞控制算法是导致 TCP 在弱网环境下有着较差表现的首要原因,三次握手和累计应答两者的影响依次递减,但是也加剧了 TCP 的性能问题。

拥塞控制

TCP 拥塞控制算法是互联网上主要的拥塞控制措施,它使用一套基于线増积减(Additive increase/multiplicative decrease,AIMD)的网络拥塞控制方法来控制拥塞,也是造成 TCP 性能问题的主要原因。

第一次发现的互联网拥塞崩溃是在 1986 年,NSFnet 阶段一的骨干网的处理能力从 32,000bit/s 降到了 40bit/s,该骨干网的处理能力直到 1987 和 1988 年,TCP 协议实现了拥塞控制之后才得到解决。正是因为发生过网络阻塞造成的崩溃,所以 TCP 的拥塞控制算法就认为只要发生了丢包当前网络就发生了拥堵,从这一假设出发,TCP 就使用了慢启动和线增积减的机制实现拥塞控制。

每一个 TCP 连接都会维护一个拥塞控制窗口(Congestion Window),拥塞控制窗口的作用有两个:

  1. 防止发送方向接收方发送了太多数据,导致接收方无法处理;
  2. 防止 TCP 连接的任意一方向网络中发送大量数据,导致网络拥塞崩溃;

除了拥塞窗口大小(cwnd)之外,TCP 连接的双方都有接收窗口大小(rwnd),在 TCP 连接建立之初,发送方和接收方都不清楚对方的接收窗口大小,所以通信双方需要一套动态的估算机制改变数据传输的速度,在 TCP 三次握手期间,通信双方会通过 ACK 消息通知对方自己的接收窗口大小,接收窗口大小一般是带宽延迟乘积(Bandwidth-delay product, BDP)决定的。

客户端能够同时传输的最大数据段的数量是接收窗口大小和拥塞窗口大小的最小值,即 min(rwnd, cwnd)。TCP 连接的初始拥塞窗口大小是一个比较小的值,在 Linux 中是由 TCP_INIT_CWND 定义的:

/* TCP initial congestion window as per rfc6928 */
#define TCP_INIT_CWND		10

初始拥塞控制窗口的大小从出现之后被多次修改,几个名为 Increasing TCP’s Initial Window 的 RFC 文档:RFC2414、RFC3390 和 RFC6928 分别增加了 initcwnd 的值以适应不断提高的网络传输速度和带宽。

互联网协议 — QUIC 快速 UDP 互联网连接_第4张图片
如上图所示,TCP 连接发送方的拥塞控制窗口大小会根据接收方的响应而变化:

  1. 线性增长:经过 1 个 RTT ,拥塞窗口大小会加一;
  2. 积式减少:当发送方发送的数据包丢包时,拥塞控制阈值会减半;

如果 TCP 连接刚刚建立,由于 Linux 系统的默认设置,客户端能够同时发送 10 个数据段,假设我们网络的带宽是 10M,RTT 是 40ms,每个数据段的大小是 1460 字节,那么使用 BDP 计算的通信双方窗口大小上限应该是 35,这样才能充分利用网络的带宽:

互联网协议 — QUIC 快速 UDP 互联网连接_第5张图片

然而拥塞控制窗口的大小从 10 涨到 35 需要 2RTT 的时间,具体的过程如下:

  1. 发送方向接收方发送 initcwnd = 10 个数据段(消耗 0.5RTT);
  2. 接收方接收到 10 个数据段后向发送方发送 ACK(消耗 0.5RTT);
  3. 发送方接收到发送方的 ACK,拥塞控制窗口大小由于 10 个数据段的成功发送 +10,当前拥塞控制窗口大小达到 20;
  4. 发送方向接收方发送 20 个数据段(消耗 0.5RTT);
  5. 接收方接收到 20 个数据段后向发送方发送 ACK(消耗 0.5RTT);
  6. 发送方接收到发送方的 ACK,拥塞控制窗口大小由于 20 个数据段的成功发送 +20,当前拥塞控制窗口大小达到 40;

从 TCP 三次握手建立连接到拥塞控制窗口大小达到假定网络状况的最大值 35 需要 3.5RTT 的时间,即 140ms,这是一个比较长的时间了。

早期互联网的大多数计算设备都通过有线网络连接,出现网络不稳定的可能性也比较低,所以 TCP 协议的设计者认为丢包意味着网络出现拥塞,一旦发生丢包,客户端疯狂重试就可能导致互联网的拥塞崩溃,所以发明了拥塞控制算法来解决该问题。

但是如今的网络环境更加复杂,无线网络的引入导致部分场景下的网络不稳定成了常态,所以丢包并不一定意味着网络拥堵,如果使用更加激进的策略传输数据,在一些场景下会得到更好的效果。

三次握手

三次握手的主要目的是避免历史错误连接的建立并让通信的双方确定初始序列号,然而三次握手的成本相当高,在不丢包的情况下,它需要建立 TCP 连接的双方进行三次通信。

互联网协议 — QUIC 快速 UDP 互联网连接_第6张图片
如果我们要从北京访问上海的服务器,由于北京到上海的直线距离约为 1000 多公里,而光速是目前通信速度的极限,所以 RTT 一定会大于 6.7ms:
在这里插入图片描述

然而因为光在光纤中不是直线传播的,真正的传输速度会比光速慢 ~31%,而且数据需要在各种网络设备之间来回跳转,所以很难达到理论的极限值。在生产环境中从北京到上海的 RTT 大概在 40ms 左右,所以 TCP 建立连接所需要最短时间也需要 60ms(1.5RTT)。

在网络环境较差的地铁、车站等场景中,因为丢包率较高,客户端很难与服务端快速完成三次通信并建立 TCP 连接。当客户端长时间没有收到服务端的响应时,只能不断发起重试,随着请求次数逐渐增加,访问的延迟也会越来越高。

由于大多数的 HTTP 请求都不会携带大量的数据,未被压缩的请求和响应头大小在 ~200B 到 2KB 左右,而 TCP 三次握手带来的额外开销是 222 字节,其中以太网数据帧占 3 * 14 = 42 字节,IP 数据帧占 3 * 20 = 60 字节,TCP 数据帧占 120 字节:

互联网协议 — QUIC 快速 UDP 互联网连接_第7张图片

虽然 TCP 不会为每一个发出的数据段建立连接,但是三次握手建立连接需要的成本还是相当高,不仅需要额外增加 1.5RTT 的网络延时,还需要增加 222 字节的额外开销,所以在弱网环境下,通过三次握手建立连接会加剧 TCP 的性能问题。

重传机制

TCP 传输的可靠性是通过序列号和接收方的 ACK 来保证的,当 TCP 传输一个数据段时,它会将该数据段的副本放到重传队列上并开启计时器:

  • 如果发送方收到了该数据段对应的 ACK 响应,当前数据段就会从重传队列中删除;
  • 如果发送方在计时器到期之间都没有收到该数据段对应的 ACK,就会重新发送当前数据段;

TCP 的 ACK 机制可能会导致发送方重新传输接收方已经收到了数据段。TCP 中的 ACK 消息表示该消息之前的全部消息都已经被成功接收和处理,例如:

  1. 发送方向接收方发送了序号为 1-10 的消息;
  2. 接收方向发送方发送 ACK 8 响应;
  3. 发送方认为序号为 1-8 的消息已经被成功接收;

这种 ACK 的方式在实现上比较简单,更容易保证消息的顺序性,但是在以下情况可能会导致发送方重传已经接收的数据:

互联网协议 — QUIC 快速 UDP 互联网连接_第8张图片

如上图所示,接收方已经收到了序号为 2-5 的数据,但是由于 TCP ACK 的语义是当前数据段前的全部数据段都已经被接收和处理,所以接收方无法发送 ACK 消息,由于发送方没有收到 ACK,所有数据段对应的计时器就会超时并重新传输数据。在丢包较为严重的网络下,这种重传机制会造成大量的带宽浪费。

队头拥塞(head of line blocking)/多路复用

如果 HTTP/2 连接双方的网络中有一个数据包丢失,或者任何一方的网络出现中断,整个 TCP 连接就会暂停,丢失的数据包需要被重新传输。因为 TCP 是一个按序传输的链条,因此如果其中一个点丢失了,链路上之后的内容就都需要等待。

如下图所示,我们一个用链条来表现一个连接上发送的两个流(传输),红色的与绿色的数据流。这种单个数据包造成的阻塞,就是 TCP 上的队头阻塞(head of line blocking)。随着丢包率的增加,HTTP/2 的表现越来越差。在 2% 的丢包率(一个很差的网络质量)中,测试结果表明 HTTP/1 用户的性能更好,因为 HTTP/1 一般有六个 TCP 连接,哪怕其中一个连接阻塞了,其他没有丢包的连接仍然可以继续传输。而采用 HTTP/2 时,浏览器一般会在单个 TCP 连接中创建并行的几十个乃至上百个传输。

互联网协议 — QUIC 快速 UDP 互联网连接_第9张图片
互联网协议 — QUIC 快速 UDP 互联网连接_第10张图片

在限定的条件下,在 TCP 下解决这个问题相当困难。而使用 QUIC 时,两端间仍然建立一个连接,该连接也经过协商使得数据得到安全且可靠的传输。但是,当我们在这个连接上建立两个不同的数据流时,它们互相独立。也就是说,如果一个数据流丢包了,只有那个数据流必须停下来,等待重传。下面是两个端点间的示意图,黄色与蓝色是两个独立的数据流。

互联网协议 — QUIC 快速 UDP 互联网连接_第11张图片
互联网协议 — QUIC 快速 UDP 互联网连接_第12张图片

TCP 是个面向连接的协议,即发送请求后需要收到 ACK 消息,以确认对方已接收到数据。如果每次请求都要在收到上次请求的 ACK 消息后再请求,那么效率无疑很低。后来 HTTP/1.1 提出了 Pipelining 技术,允许一个 TCP 连接同时发送多个请求,这样就大大提升了传输效率。

互联网协议 — QUIC 快速 UDP 互联网连接_第13张图片

在这个背景下,下面就来谈 HTTP/1.1 的队头阻塞。下图中,一个 TCP 连接同时传输 10 个请求,其中第 1、2、3 个请求已被客户端接收,但第 4 个请求丢失,那么后面第 5 - 10 个请求都被阻塞,需要等第 4 个请求处理完毕才能被处理,这样就浪费了带宽资源。

互联网协议 — QUIC 快速 UDP 互联网连接_第14张图片

因此,HTTP 一般又允许每个主机建立 6 个 TCP 连接,这样可以更加充分地利用带宽资源,但每个连接中队头阻塞的问题还是存在。

HTTP/2 的多路复用解决了上述的队头阻塞问题。不像 HTTP/1.1 中只有上一个请求的所有数据包被传输完毕下一个请求的数据包才可以被传输,HTTP/2 中每个请求都被拆分成多个 Frame 通过一条 TCP 连接同时被传输,这样即使一个请求被阻塞,也不会影响其他的请求。如下图所示,不同颜色代表不同的请求,相同颜色的色块代表请求被切分的 Frame。

互联网协议 — QUIC 快速 UDP 互联网连接_第15张图片

事情还没完,HTTP/2 虽然可以解决“请求”这个粒度的阻塞,但 HTTP/2 的基础 TCP 协议本身却也存在着队头阻塞的问题。HTTP/2 的每个请求都会被拆分成多个 Frame,不同请求的 Frame 组合成 Stream,Stream 是 TCP 上的逻辑传输单元,这样 HTTP/2 就达到了一条连接同时发送多条请求的目标,这就是多路复用的原理。我们看一个例子,在一条 TCP 连接上同时发送 4 个 Stream,其中 Stream1 已正确送达,Stream2 中的第 3 个 Frame 丢失,TCP 处理数据时有严格的前后顺序,先发送的 Frame 要先被处理,这样就会要求发送方重新发送第 3 个 Frame,Stream3 和 Stream4 虽然已到达但却不能被处理,那么这时整条连接都被阻塞。

互联网协议 — QUIC 快速 UDP 互联网连接_第16张图片

不仅如此,由于 HTTP/2 必须使用 HTTPS,而 HTTPS 使用的 TLS 协议也存在队头阻塞问题。TLS 基于 Record 组织数据,将一堆数据放在一起(即一个 Record)加密,加密完后又拆分成多个 TCP 包传输。一般每个 Record 16K,包含 12 个 TCP 包,这样如果 12 个 TCP 包中有任何一个包丢失,那么整个 Record 都无法解密。

互联网协议 — QUIC 快速 UDP 互联网连接_第17张图片

队头阻塞会导致 HTTP/2 在更容易丢包的弱网络环境下比 HTTP/1.1 更慢!

用 TCP 还是 UDP?

如果我们无法解决 TCP 内的队头阻塞问题,那么按道理,我们应该在网络栈中发明一个 UDP 和 TCP 之外的新型传输层协议。或者我们应该用 IETF 在 RFC 4960 中标准化的 SCTP 传输层协议,它也有多个我们所需的特征。

但在近些年来,因为在互联网上部署遭遇很大的困难,创造新型传输层协议的努力基本上都失败了。用户与服务器之间要经过许多防火墙、NAT(地址转换)、路由器和其他中间设备(Middle-box),这些设备有很多只认 TCP 和 UDP。如果使用另一种传输层协议,那么就会有 N% 的连接无法建立,这些中间设备会认为除 TCP 和 UDP 协议以外的协议都是不安全或者有问题的。如此高的的失败率一般被认为不值得再做出努力。

另外,网络栈中的传输层协议改动一般意味着操作系统内核也要做出修改。更新和部署新款操作系统内核的过程十分缓慢,需要付出很大的努力。由 IETF 标准化的许多 TCP 新特性都因缺乏广泛支持而没有得到广泛的部署或使用。

为什么不基于 UDP 使用 SCTP?

SCTP 是一个支持数据流的可靠的传输层协议,而且在 WebRTC 上已有基于 UDP 的对它的实现。这看上去很好,但与 QUIC 相比还不够好:

  • 没有解决数据流的队头阻塞问题。
  • 连接建立时需要决定数据流的数量。
  • 没有稳固的 TLS 安全性支持。
  • 建立连接时候需要 4 次握手,而 QUIC 一次都不用(0-RTT)。
  • QUIC 是类 TCP 的字节流,而 SCTP 是信息流(message-based)。
  • QUIC 连接支持 IP 地址迁移,SCTP 不行。

协议僵化(Ossification)

互联网(Internet),即多个网络互联之网。为了保证互联网工作正常,我们需要在互联网各处搭建各种设备。这些在互联网上分布式架设的设备被称为中间设备(middle-box)。传统网络传输中两个端点之间的中间设备服务于网络数据传输过程。

这些中间设备有着许多、各式各样的用途和目的,我们简单来说,这些设备是为了实现放置人在该位置所要完成的特定目的而安置。中间设备的目的包括:

  • 在网络之间转发(路由)数据包
  • 阻挡恶意流量
  • 执行地址转换(NAT)
  • 提升性能
  • 监视流量

为了完成这些目的,这些设备必须了解网络以及它们所要监视或修改的数据包协议。它们为此依赖于一些很少会进行升级的软件。虽然这些设备是将互联网 “粘” 在一起的关键元素,但是它们经常跟不上最新的技术。网络的核心部分与边缘部分(客户端、服务器)相比,更新很慢。

所以当这些设备决定了经过的流量是否合格时,就有些问题了:在这些设备部署之后的一段时间里,协议有了新的特征。而在这些设备引入(了解)这些新特性之前,它们会认为这种特征的数据包是非法的、恶意的,于是会将这种流量直接扔掉,或是拖延到用户不再想使用这些新特征的程度。这种问题就被称之为 “协议僵化”。

协议僵化也影响了 TCP 协议的改变:当客户端与远程服务器之间的某些中间设备检测到对于它们来说未知的新的 TCP 选项时,中间设备将拦截这些流量,因为它们不知道这些选项的作用。如果中间设备监测了协议的实现细节,它们会学习到协议的典型行为,在一段时间后,这些行为变得没法更改。

尽可能将通信加密是对抗僵化的唯一有效手段,加密可以防止中间设备看到协议传输的绝大部分内容。

小结

TCP 协议的一些设计在今天来看虽然仍然具有巨大的价值,但是并不能适用于所有场景。为了解决 TCP 的性能问题,目前业界有两种解决方案:

  1. 使用 UDP 构建性能更加优异、更灵活的传输协议,例如:QUIC。
  2. 过不同的手段优化 TCP 协议的性能,例如:选择性 ACK(Selective ACK, SACK)、TCP 快开启(TCP Fast Open, TFO)。

由于 TCP 协议在操作系统内核中,不利于协议的更新,所以第一种方案目前发展的更好,HTTP/3 就使用了 QUIC 作为传输协议。

在这里重新回顾一下导致 TCP 性能问题的三个重要原因:

  1. TCP 的拥塞控制在发生丢包时会进行退让,减少能够发送的数据段数量,但是丢包并不一定意味着网络拥塞,更多的可能是网络状况较差;
  2. TCP 的三次握手带来了额外开销,这些开销不只包括需要传输更多的数据,还增加了首次传输数据的网络延迟;
  3. TCP 的重传机制在数据包丢失时可能会重新传输已经成功接收的数据段,造成带宽的浪费;

TCP 协议作为互联网数据传输的基石可以说是当之无愧,虽然它确实在应对特殊场景时有些问题,但是它的设计思想有着非常多的借鉴意义并值得我们学习。

QUIC 的特性

互联网协议 — QUIC 快速 UDP 互联网连接_第18张图片

基于 UDP

QUIC 是基于 UDP 在用户空间实现的传输协议。如果不观察细节,你会觉得 QUIC 跟 UDP 报文差不多。这也意味着它使用 UDP 端口号来识别指定机器上的特定服务器。目前已知的所有 QUIC 实现都位于用户空间,这使它能得到更快速的迭代(相较于内核空间中的实现)。

可靠性

虽然 UDP 不提供可靠的传输,但 QUIC 在基于 UDP 之上增加了一层带来可靠性的层。它提供了数据包重传、拥塞控制、调整传输节奏(pacing)以及其他一些 TCP 中存在的特性。只要连接没有中断,从 QUIC 一端传输的数据迟早会出现在另一端,这是可靠的。

数据流

类似 SCTP、SSH 和 HTTP/2,QUIC 在同一物理连接上可以有多个独立的逻辑数据流。这些数据流并行在同一个连接上传输,不影响其他流。连接在两个端点之间经过类似 TCP 连接的方式协商建立。QUIC 连接基于 UDP 端口和 IP 地址建立,而一旦建立,连接通过其连接ID(connection ID)关联。

在已建立的连接上,双方均可以建立传输给对方的数据流。单一数据流的传输是可靠、有序的,但不同的数据流间可能无序传送。QUIC 可对连接和数据流分别进行流量控制(flow control)。

有序交付

QUIC 的单个数据流可以保证有序交付,但多个数据流之间可能乱序。这意味着单个数据流的传输是按序的,但是多个数据流中接收方收到的顺序可能与发送方的发送顺序不同。

举个例子:服务器传送流 A 和 B 到客户端。流 A 先启动,然后是流 B。在 QUIC 中,丢包只会影响该包所处的流。如果流 A 发生了一次丢包,而流 B 没有,流 B 将继续传输直到结束,而流 A 将会进行丢包重传过程。而在 HTTP/2 中这不可能发生。

下图展示了连通两个 QUIC 端点的单一连接中的黄色与蓝色的数据流。它们互相独立,所以可能乱序到达,但是每个流内的信息将按序可靠到达。

互联网协议 — QUIC 快速 UDP 互联网连接_第19张图片

快速握手

与 TCP 的 3 次握手相比,QUIC 提供了 0-RTT 和 1-RTT 的握手,这减少了协商和建立新连接时所需的时间。其中更快的 0-RTT 仅在两个主机之间建立过连接且缓存了该连接的 Secret 时可以使用。

此外,QUIC 提供了提早传输更多数据的 “早期数据”(early data)特性,并且它的使用比 TCP 快速打开(TCP Fast Open)更加简便。同时,因为数据流概念的引入,客户端不用等待前一个连接结束,便可以与同一个主机建立另一个逻辑连接。

安全性

QUIC 使用 TLS 1.3 传输层安全协议,并且 QUIC 协议没有明文的版本,所以想要建立一个 QUIC 连接,就必须通过 TLS 1.3 来安全地建立一个加密连接。如上文所说,加密可以避免协议僵化等拦截和特殊处理。这也使 QUIC 具有了 Web 用户所期望的所有的 HTTPS 安全特性。QUIC 只在加密协议协商时会发送几个明文传送的初始握手报文。

连接迁移

TCP 连接基于四元组(源 IP、源端口、目的 IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间。

QUIC 的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。那这是怎么做到的呢?道理很简单,QUIC 连接不以四元组作为标识,而是使用一个 64 位的随机数,这个随机数被称为 Connection ID,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。

互联网协议 — QUIC 快速 UDP 互联网连接_第20张图片

解决队头阻塞

  • QUIC 的传输单元是 Packet,加密单元也是 Packet,整个加密、传输、解密都基于 Packet,这样就能避免 TLS 的队头阻塞问题;
  • QUIC 基于 UDP,UDP 的数据包在接收端没有处理顺序,即使中间丢失一个包,也不会阻塞整条连接,其他的资源会被正常处理。

互联网协议 — QUIC 快速 UDP 互联网连接_第21张图片

HTTP/3 支撑

HTTP/3 协议使用了 QPACK 进行 HTTP 头部压缩,这和 HTTP/2 中使用 HPACK 压缩头部类似。HPACK 算法依赖于数据流的有序交付,由于 HTTP/3 的数据流之间可能乱序,所以该算法需要修改才能使用。QPACK 可被视作适用于 QUIC 版本的 HPACK。

QUIC 的工作原理

连接

QUIC 连接是两个 QUIC 端点之间的单次会话(conversation)过程。QUIC 建立连接时,加密算法的版本协商与传输层握手合并完成,以减小延迟。在连接上实际传输数据时需要建立并使用一个或多个数据流。

每个连接过程都有一组连接标识符,或称连接 ID,该 ID 用以识别该连接。每个端点各自选择连接 ID。每个端点选择对方使用的连接 ID。连接 ID 的基本功能是确保底层协议(UDP、IP 及其底层协议)的寻址变更不会使 QUIC 连接传输数据到错误的端点。

利用连接 ID 的优势,连接可以在 IP 地址和网络接口迁移的情况下得到保持,这是 TCP 永远也做不到的。举例来说,当用户的设备连接到一个 Wi-Fi 网络时,将进行中的下载从蜂窝网络连接转移到更快速的 Wi-Fi 连接。与此类似,当 Wi-Fi 连接不再可用时,将连接转移到蜂窝网络连接。

由于 QUIC 基于 UDP 建立,因此使用 16 比特的 UDP 端口号字段来区分传入的不同连接。

在初始的数据包建立连接之后,连接发起者会马上发一个加密的帧以开始安全层握手。安全层使用 TLS 1.3 协议。

数据流

数据流(Streams)在 QUIC 中提供了一个轻量级、有序的字节流的抽象化。QUIC 中有两种基本的数据流类型:

  1. 从发起者到对等端(Peer)的单向数据流。
  2. 双向均可发出数据的双向数据流。

连接端点的任意一方都可以建立这两种数据流,数据流之间可并行、交错地传输,并且可以被取消。通过 QUIC 发送数据需要建立一个或多个数据流。

  • 流量控制(Flow control):每个数据流都有独立的流量控制,端点可以通过此实现内存控制和反压(back pressure)。数据流的创建本身也有流量控制,连接双方可以声明最多愿意创建几个流 ID。
  • 流标识符:数据流通过一个无符号的 62 比特整数标识,也称流 ID。流 ID 的最低 2 位比特用于识别流的类型(单向或双向)和流的发起者。流 ID 的最低 1 位比特(0x1)用于识别流的发起者。客户端发起双数(最低位置 0)流,服务器发起单数(最低位置 1)流。第 2 个比特(0x2)识别单/双向流。单向流始终置 1,双向流则置 0。
  • 流并发:QUIC 允许任意数量的并发流。端点通过闲置最大流 ID 来控制并发活动的传入流数量。每个端点指定自己的最大流 ID 数,并只对对等端端点有效。
  • 收发数据:端点使用流来收发数据,这是流的最终用途。QUIC 数据流是有序的字节流抽象。但是,不同流之间是无序的。
  • 流优先度:如果正确设置了各流的优先度,流复用机制可以显著提升应用的效率。使用其他多路复用协议(如 HTTP/2)的经验表明,有效的优先度划分策略对效率具有显著的正面影响。QUIC 本身没有提供交换优先度信息的报文。接收优先度信息依赖于使用 QUIC 的应用层。应用层可以定义所有复合其语义的优先度方案。基于 QUIC 使用 HTTP/3 时,优先度信息在 HTTP 层完成。

用户态实现

在用户空间中实现一个传输层协议有助于协议的快速迭代,协议的演进更为容易,不需要客户端和服务器更新其操作系统内核才能部署新的版本。同时,QUIC 本身没有固有的东西阻碍未来在操作系统内核中实现和提供 QUIC 协议。

API

常规 TCP 与程序最成功的因素之一便是标准化的套接字(Socket)API。其 API 有着定义良好的功能,使用它能让你轻松地在各操作系统之间移植程序,因为 TCP 采用同样的方式运作。

但 QUIC 不是如此。QUIC 目前没有标准化的 API。使用 QUIC 时,你需要选择一个现有的库实现,并坚持使用它的 API。这在某种程度上把应用 “绑定” 到了单一的库上。换库意味着使用另外一套 API,这可能带来相当的工作量。

另外,由于 QUIC 一般在用户空间中实现,所以它不像现有的 TCP 和 UDP 套接字 API 那样能轻松扩展。使用 QUIC 意味着选择了套接字 API 之外的另一套 API。

参考文档

https://mp.weixin.qq.com/s/NpQJKqe4itF69f5H-qkCcg

你可能感兴趣的:(计算机网络)