目录
5.2 与TLS(Transport Layer Security)集成
1 前言
作者在实习期间简单学习过QUIC协议的基本原理,也在GitHub上找过各类QUIC实现版本,具体的QUIC协议实现涉及到数据包、连接等多个状态机,想要深入研究的小伙伴可以去找些代码看。本篇仅做粗浅的简介。
2 什么是QUIC
QUIC(Quick UDP Internet Connection)是一种传输层协议,与TCP和UDP协议同运行在传输层。
QUIC协议起源于HTTP协议的需求。
2.1 HTTP/1.0
在HTTP/1.0中,客户端(Client)想要发送多个请求(Request)时,当前请求必须上一个请求对应的、来自服务器(Server)的响应(Response)到达之后才能发送。这样会导致完成多个任务的时间很长。
![](https://i-blog.csdnimg.cn/blog_migrate/c0a2977e9d490a724521bef31de59026.png)
2.2 HTTP/1.1
因此,在HTTP/1.1中,提出了所谓的客户端可以管道化(pipelining)并行发送多个请求。
但是对于服务器而言,还是必须得将上一个响应发送完后,才能发送下一个响应,即在服务器端并没有实现“管道”发送。
![](https://i-blog.csdnimg.cn/blog_migrate/802af337ceaf55dc95f29f7f5fdd4269.png)
2.3 HTTP/2
之后,在HTTP/2中,则实现了基于应用层(即HTTP协议)的“多流复用”,以缩小多个任务场景下的完成时间。
![](https://i-blog.csdnimg.cn/blog_migrate/d29243fb7cc55a47315438ce815c0d4e.png)
HTTP/2中,客户端和服务器将数据进行“分帧”,将不同资源请求通过消息中的“stream ID”来区分,即哪个响应对应哪个请求,以及可以区分同时到达的多个请求/响应。
这样,在应用层层面就解决了队头阻塞的问题,但是HTTP协议是基于TCP的,而TCP协议本身存在固有的阻塞问题。
2.4 TCP协议的阻塞问题
TCP协议是一种基于字节流传输数据的协议,无法区分上层业务逻辑的区别,对于来自多个应用程序的数据流,都通过同一个TCP滑动窗口来维护传输。
![](https://i-blog.csdnimg.cn/blog_migrate/5b7eaca3c8eeea88c1fe6e1d3d7d2878.png)
如图所示,如果对来自应用程序1(stream ID 为1)的数据Seq3的确认ACK丢失后,客户端的滑动窗口会不滑动,直至超时重传Seq3,而这也会导致来自应用程序2(stream ID 为2)和应用程序3(stream ID 为3)的数据传输都堵塞。
这就是TCP协议固有的队头阻塞问题。而UDP协议不需要维护连接和滑动窗口等,在这方面有很大的可拓展性。因此,在HTTP/3中提出了配合应用协议的传输层协议QUIC,即HTTP/3是基于QUIC传输层协议去传输数据的。
而现在,QUIC协议作为一个独立的传输层协议,可作为接口服务于各类应用协议(不仅限于HTTP)。这就是QUIC协议的来源。
3 QUIC的协议栈
QUIC协议在TCP/IP协议栈所处的位置如下图,位于应用层和UDP层之间。
![](https://i-blog.csdnimg.cn/blog_migrate/ab68f53a0bcf474227bd017dcb00f52c.png)
QUIC协议使用UDP协议,在此基础上模拟了TCP的三大功能:差错重传、拥塞控制、流量控制。
为什么明明是弥补TCP的缺陷,却还要模拟TCP的功能呢?
这是因为虽然TCP具有队头阻塞这样的问题,但是它和UDP协议比起来,通过差错重传和拥控流控机制,保证了数据传输的准确性和网络通信性能。这也是QUIC协议希望做到的。(取其精华去其糟粕)
同时,可以从图中看到,QUIC协议将原本应用层的“多流复用”功能在传输层实现了,即应用层不需要对不同请求进行特殊分流处理,扔给QUIC去做就行。
其次,QUIC集成了TLS1.3模块的加密功能,它也需要在数据传输前提前建立连接(和TCP类似),这部分是由TLS的握手(handshake)模块实现的。
4 QUIC的报文结构
QUIC协议基于UDP实现,因此一个QUIC报文(QUIC Packet)是作为一个UDP数据报(datagram)的负载(payload)部分,加上UDP数据报的头部(header)就组成一个UDP报文。
QUIC报文的具体结构如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/a9cc9bf9e683bb15c98a209fbe38d5a2.png)
首先数据传输的管道是一个连接(Connection),如黄色框框,表示一个QUIC连接管道,进行数据传输。
第一级报文是UDP报文,QUIC packet作为UDP数据报的负载,如红色框框。
第二级报文是QUIC报文,包括头部(橘色框框)和负载(蓝色框框)。
第三级是帧结构(frame),如紫色框框,每个帧可以来自不同的应用程序,通过帧结构中的stream ID区分,对于来自同一个应用程序的数据,通过offset字段保证该数据在整个数据中的额有序位置。
5 QUIC的核心技术
5.1 修改难度小
(1)对TCP协议进行修改:TCP协议是在内核态实现,而操作系统的升级迭代太慢。
(2)通过QUIC协议来实现:QUIC协议在用户态实现,可以通过用户进行编程实现,容易修改。
5.2 与TLS(Transport Layer Security)集成
如图5所示,TCP与TLS是串行关系,通过调用TLS模块来对应用数据进行加密。
QUIC协议与TLS协议是相互集成的关系,QUIC协议使用TLS的Handshake模块进行建立连接,TLS使用QUIC的可靠性、有序交付作为记录层(Record模块)。
5.3 建立连接低时延
TLS和QUIC协议都存在两种建立连接的场景,一种是首次建连,一种是后续建连,如下图所示。
![](https://i-blog.csdnimg.cn/blog_migrate/84d69d108f1dddc0bea80e2d1bf829ae.png)
可以看到,TLS1.2和TLS1.3建立连接相差了一个RTT(Round-Trip Time, RTT),这时因为TLS1.2采用RSA秘钥交换算法,在第2个RTT时交换秘钥,而TLS1.3采用ECDHE密钥交换算法,在第1个RTT时交换秘钥。
TLS1.2的秘钥交换过程如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/6d4575060315a4c5489240346d2bf438.png)
TLS1.3的秘钥交换过程如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/15d4b708ff8690cc61c01fbd9d814780.png)
(以上都是作者个人理解,简单草图,有错误之处还请指正)
5.4 连接迁移
(1)TCP协议:通过<源IP地址,源端口号,目的IP地址,目的端口号>标识,若客户端所在4G网络转到WiFi网络,客户端IP地址发生变化,TCP需要重新建立连接。
(2)QUIC协议:通过一个随机生成的64位的connection ID(CID)标识连接,只要CID不变,连接就不断开。
5.5 多流复用,缓解队头阻塞
QUIC基于UDP,UDP本身无队头阻塞(没有流量控制和确认机制)
QUIC对每个stream,维护各自的滑动窗口,一个QUIC packet丢失并不影响其他stream的窗口滑动。
![](https://i-blog.csdnimg.cn/blog_migrate/ffbde205707997a1d40af6d7efb3fc55.png)
举例:
Packet N丢失:stream 2阻塞,stream1、3正常;
Packet N+2丢失:stream1、3阻塞,stream 2正常;
思考:
①QUIC的多流复用能够提高并发流的性能,但并发流常见吗?
②121212对资源加载性能不一定最优(11112222对于业务1来说是最优的)。
③对被阻塞的资源,虽然有些浏览器可以开始编译/解析部分资源,但仍需等待完整资源到达后才能使用。
④对同一个Packet,最好别同时封装多个stream。
5.6 改进的拥塞控制机制
5.6.1 可插拔
(1)应用层面实现不同的拥塞控制算法;
(2)单个应用程序的不同连接支持不同的拥塞控制;
(3)应用程序不许停机和升级就能实现拥塞控制的变更;
(4)针对不同业务,不同网络制式,不同RTT,使用不同的拥塞控制算法。
5.6.2 单调递增的Packet Number
(1)TCP协议:原始Segment的序列号(Sequence Number,SN)与重传Segment相同。
![](https://i-blog.csdnimg.cn/blog_migrate/b7d0bf3411b94f1fbe06e46b83d1ef01.png)
需判断ACK N+1是对原始还是重传的响应。
重传:如果判断为原始,则RTT偏大;
延迟:如果判断为重传,则RTT偏小;
(2)QUIC协议:QUIC packet number单调递增。
![](https://i-blog.csdnimg.cn/blog_migrate/817371944318ed52d059b13e90c34fe0.png)
对于QUIC协议,客户端能区分对原始还是重传的ACK,可以精确估计RTT。
5.6.3 ACK Delay
(1)TCP:只回显发送方发送的时间戳。
(2)QUIC:能计算ACK Delay,使RTT估计更精确。
![](https://i-blog.csdnimg.cn/blog_migrate/d6c652a511bf6bd216c8735a2d4e4c8a.png)
5.6.4 不允许Reneging
(1)Reneging:是指接收方丢弃已接收且上报给SACK选项的内容。
(2)TCP:协议层面允许Reneging(服务器资源有限)。Reneging会对重传造成干扰,SACK表明已收到,但实际接收方丢弃了该数据。
5.6.5 更多ACK块
(1)TCP协议:头部header最大有60字节,标准头部20个字节,option有40字节。其中,TCP timestamp option 10字节留给SACK的只有30字节;每个SACK Block有8字节,SACK头部占2字节,则最多3个SACK Block。
(2)QUIC协议:ACK frame最多可提供256个SACK Block。
5.7 双层流量控制
5.7.1 stream级别
限制单个stream可发送的数据量,防止单个stream消耗connection的全部缓冲区(通过流量控制frame通知发送方)。
5.7.2 connection级别
限制一个connection里所有stream加起来发送的字节数,防止发送方超过connection的缓冲容量。
6 QUIC的缺点
(1)根据Google的仿真数据和论文,可以看到,在网络质量好的网络中,如高带宽、低时延、低丢包率的场景,QUIC协议的性能不一定比TCP好。
(2)QUIC协议的CPU消耗是TCP/TLS的两倍左右。可能原因有:
- QUIC是基于UDP协议进行设计开发的,而UDP在批量收发包的接口性能方面不如TCP;
- QUIC需要对报文进行加解密,强制使用TLS模块;而TCP可选择使用TLS对数据进行安全加密,也可不使用。
- ACK处理。TCP的ACK报文直接在内核通过tcp_ack函数进行处理;而QUIC先将所有报文放到用户态,再处理,导致了额外的用户级与内核级的切换,和数据从内核转移到用户的拷贝开销。
综上,可以看到QUIC协议并不是一类适用于所有通信场景下的协议,它在存在多并发流的场景下可以发挥良好的通信性能,缓解TCP的队头阻塞,提高数据包传输的时延。因此,需要因地制宜,用在合适的场景下,才能发挥QUIC协议性能。