三分钟,带你了解下一代传输层协议QUIC
QUIC(Quick UDP Internet Connection,快速UDP网络连接)发音同 "quick",是 Google 公司在 2012 年提出的使用 UDP 进行多路并发传输的协议。
2016 年,互联网工程任务组 IETF 开始标准化 QUIC。
2017 年,Google 开发并部署了 QUIC 协议的初始设计 gQUIC。
2021 年,QUIC 协议的正式标准化版本 RFC900 发布。
从 QUIC 的命名中可以看到两个关键词——快速和 UDP。这个两个关键词概括了 QUIC 的特性,提供更快速、更可靠、更安全的数据传输方式。
快速:QUIC 比 TCP 更简单,能够更快速地连接。其安全性也不亚于 TCP+TLS。
UDP:QUIC 是建立在 UDP 之上的新型传输协议,一般在应用层发挥作用。
QUIC协议栈
一个 QUIC 数据包由头部(Header)和数据(Data)组成。
QUIC数据格式
头部(Header)中包含以下字段:
标志位(Flags):用来指示该数据包的类型、状态等信息。
连接ID(Connection ID):用于唯一标识一个连接。
版本号(Version):表示使用的协议版本号。
包编号(Packet Number):表示数据包的顺序。
数据(Data)被拆分成多个小的数据帧(Frame),每个数据帧都有自己的类型、长度和负载。数据帧通过 Stream ID 进行标识,以便于在多路复用时进行管理。
PING 帧:用于测试连接的可用性。
ACK 帧:用于确认收到数据包。
RESET_STREAM 帧:用于重置数据流的状态。
STOP_SENDING 帧:用于停止向特定的数据流发送数据。
CRYPTO 帧:用于传输加密数据。
STREAM 帧:用于传输普通数据流。
STREAM帧结构
下面介绍 QUIC 协议中的三个核心特性原来:0-RTT 连接建立、无队头阻塞的多路复用、无歧义重传。
0-RTT 连接建立
QUIC 协议使用了 0-RTT(零往返时间)连接建立技术,可以在客户端发送第一个请求时就建立起安全连接,从而减少连接建立所需的时间。这个技术通过使用 TLS 的 Session Ticket,在服务端重启后仍然保留连接状态,从而避免了重新握手的过程。
传统 HTTPs 握手包含了 TCP 和 TLS 握手,如下图所示,总共需要 3 个 RTT。
TCP和TLS握手
从中可以看出,TLS 握手需要 1 个 RTT。通过 1 次 RTT,客户端和服务端就协商好了通信密钥。
客户端:生成随机数 a,选择公开的大数 G 和 P,计算 A=a*G%P。发送 Client Hello 消息,将 A 和 G 传递给服务端。
服务端:生成随机数 b,计算 B=b*G%P。发送 Server Hello 消息。将 B 传递给客户端。
客户端:通过秘钥加密算法生成通信密钥 KEY = aB = ab*G%P。
服务端:通过秘钥加密算法生成通信密钥 KEY = bA = ba*G%P。
QUIC 的握手是基于 TLS1.3 实现的,建立连接时也应该需要1次 RTT,那 QUIC 的 0-RTT 连接是如何实现的?
首次握手后,QUIC 的客户端缓存了 Server Hello,那么在下次建连时,可以直接使用缓存数据计算通信密钥,如下图所示。
0-RTT连接
客户端:生成随机数 c,选择公开的大数 G 和 P,计算 A=c*G%P。发送 Client Hello 消息,将 A 和 G 传递给服务器。
客户端:直接使用缓存的 Server Hello 计算通信密钥 KEY = cB = cb*G%P,加密并发送应用数据。
服务端:根据 Client Hello 消息计算通信密钥 KEY = bA = bc*G%P。
通过缓存 Server Hello,在生成 Client Hello 的同时,加密了应用数据,所以客户端不需要经过握手就可以发送应用数据,这就是 QUIC 的 0-RTT 连接。
无队头阻塞的多路复用
浏览器限制了同一个域名下的请求数量。当请求达到最大数量时,剩余的资源需要等待其他资源请求完成后才能发起请求,这就是队头阻塞(Head of Line Blocking)。
在传统的 HTTP/1 协议中,每个 TCP 连接只能处理唯一的请求,因此当需要请求多个资源时,需要建立多个 TCP 连接。为了解决 HTTP/1 的核心问题,在 HTTP/2 中引入了多路复用的技术,这个技术可以只通过一个 TCP 连接就可以传输所有的请求数据,如下图。
多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,从而提高网络吞吐量。此外,QUIC 协议还支持数据流级别的拥塞控制,可以更精细地控制网络拥塞。
TCP连接
HTTP/2 虽然通过多路复用解决了 HTTP 层的队头阻塞,但仍然存在 TCP 层的队头阻塞问题。在 HTTP/2 中,如果 TCP 连接中出现了丢包的情况,那么整个 TCP 都要开始等待重传,后面的所有数据都将被阻塞。在这种情况下, HTTP/2 的表现情况反倒不如 HTTP/1 。
QUIC 通过为每个请求流都分配一个独立的滑动窗口,解决 TCP 层的队头阻塞。如下图,A 请求流上的丢包不会影响 B 请求流上的数据发送。
QUIC连接
无歧义重传
为了保证可靠性,TCP 使用基于序号的 Sequence Number 和 Ack 来确认消息的有序到达。在 TCP 中,重传包的 sequence number 和原始包的Sequence Number 是不变的,也正是因此这个特性,引发了 Tcp 重传歧义问题。当超时事件 RTO 发生后,客户端发起重传,然后接收到了 Ack 数据。但由于 Sequence Number 是一样的,这个接收到的 Ack 到底是原始请求的响应还是重传请求的响应呢?这导致了 RTT 计算歧义。
TCP重传歧义性
QUIC 则是采用了单向递增的 Packet Number 来标识数据包,因此不会像 TCP 一样,不会因为超时重传了同样序列的数据包,造成 RTT 和 RTO(Retransmission Time Out,重传超时时间)的计算不准确。每个 Packet Number 都严格递增,就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值。
QUIC的RTT计算
QUIC 对于 RTT 的计算更为准确,预估的超时时间能够有效防止更多的重传请求被错误地发送回客户端。同时也给予了 QUIC 网络更为快速的反应时间,及时通知客户端重传数据包。
但是单纯依靠严格递增的 Packet Number 肯定是无法保证数据的顺序性和可靠性。
QUIC 引入了 Stream Offset 的概念,通过 Stream Offset 可以保证应用数据的顺序。假设客户端先后发送了 Pakcet N 和 Pakcet N+1,Stream Offset 分别是 x 和 x+y。如果 Packet N 丢失,引发重传,重传的 Packet Number 是 N+2,但是它的 Stream Offset 依然是 x,这样就算 Packet N + 2 是后到的,依然可以将 Stream x 和 Stream x+y 按照顺序组织起来,交给应用程序处理。
QUIC 为各种领域提供了可靠、高效和安全的数据传输方案,其中最具潜力的应用场景包括:
实时 Web 和移动应用
这些应用需要低延迟和可靠的数据传输。通过相互独立的数据流和拥塞控制机制,QUIC 可以快速高效地发送和接收数据。在多路复用模式下,QUIC 同一连接内不同数据流之间的数据传输互不干扰。
与物联网设备通信
物联网设备通常使用 TCP 和 MQTT 等协议进行通信,在受限的网络环境中可能存在高延迟和丢包等问题。相比之下,QUIC 可以极近实现 0-RTT,为高延迟和丢包的网络环境,提供了更可靠和高效的替代方案。
支付和电子商务应用
这些应用需要安全可靠的数据传输。QUIC 通过 TLS 加密和可靠的数据流,使其成为这些应用的理想选择,有助于保证数据安全完整地传输。从用户的角度来看,QUIC 通过保证更快、更顺畅的交易,优化了用户体验。
当前,QUIC 协议已经成为 IETF 标准化工作组的一个项目,并且越来越多的互联网公司开始支持 QUIC 协议。随着 QUIC 协议的普及,我们可以期待更快、更安全、更可靠的网络体验。