QUIC协议分析与测试

GH_SA21225193 2022-01-19 22:00:08

简介

首先介绍UDP和TCP,UDP和TCP都是传统的数据传输协议,所以存在许多问题。

TCP:TCP 的队头阻塞,我们都知道,TCP是一种可靠传输,这个可靠就是体现在它能够“按序到达”,然后再被上层接收,这里的按序到达指的是最终顺序是按序排列的,也就是说每当有一个或几个Packet丢失的时候,会等待它到达后合并,然后再向上交付。因此,很容易可以理解,当一个流的第一个数据包丢失了,那么即使后面的数据包都到达了,后面的这些数据包也不能被处理,而是要等第一个数据包到了之后才能被上层接收处理,那么这个时候不止是第一个数据包需要额外等待一个或多个RTT,后面的第二个Packet、第三个Packet......都需要同样多等待那么多时间才能处理,但实际上他们是按时到达了的,这也就是所谓的TCP队头阻塞

例如三次握手共需耗费三次RTT时间,还有对数据包的处理有严格顺序的要求,否则将会等待重传,因此我们考虑使用一种基于UDP的协议替代基于TCP的协议来达到更高的传输效率

QUICQuick UDP Internet Connections)是由谷歌设计的一种基于UDP的传输层网络协议

 

QUIC原理分析

比如发生如下图所示场景下的问题时,当其中一个数据没有发送成功,TCP 连接需要等待这个包完成重传之后才能继续进行。因此,即使逻辑上一个 TCP 连接上并行的在进行多路数据传输,其他毫无关联的数据也会因此阻塞:

QUIC 协议直接通过传输层使用 UDP 协议就可以避免该问题的发送。由于 UDP 协议没有严格的顺序要求,当一个数据包遇到问题需要重传时只会影响该数据包对应的资源,其他独立的资源不会受到影响而阻塞传输:

 

连接迁移

在传统的TCP协议中,如果连接的某一条件发生了变动,那么就会引起TCP的重新连接。例如手机连接的WIFI发生了切换而导致IP地址发生了切换,那么基于TCP的HTTP协议就无法保持原有的状态而必须重新连接;如果使用公共 NAT 出口时,有些连接竞争时需要重新绑定端口,导致客户端的端口发生变化,同样需要重新建立 TCP 连接。

在QUIC协议中,连接将不再由TCP四元组和IP地址进行标识,这种标识方式是造成TCP连接必须重新连接的“罪魁祸首”,四元组和IP任何一个发生了变化,就是一条新的TCP连接了。QUIC使用一个随机的64位标识符表示一个连接,就算IP地址和端口发生了变化,只要ID不变,这条连接依然存在和有效,因此可以继续使用该连接。

随机ID的生成方式很多,UUID算法或者雪花算法都能生成全局唯一ID,而QUIC有自己的生成模式,确保了任意连接不会发生冲突。

 

低延时连接

目前任何一个连接,只要使用了TCP连接,就必有一个RTT的TCP握手延迟,这是TCP协议的“通病”,而且在TCP握手阶段无法传输任何数据,传输数据最快也得等到TCP握手结束。

而QUIC基于UDP协议,无须进行TCP的握手阶段,在最好情况下,短连接下 QUIC 可以做到 0RTT 开启数据传输。而基于 TCP 的 HTTPS,即使在最好的 TLS1.3 的 early data 下仍然需要 1RTT 开启数据传输。而对于目前线上常见的 TLS1.2 完全握手的情况,则需要 3RTT 开启数据传输。对于 RTT 敏感的业务,QUIC 可以有效的降低连接建立延迟。

QUIC具体握手过程如下:

1. 客户端判断本地是否已有服务器的全部配置参数(证书配置信息),如果有则直接跳转到(5),否则继续 。

2. 客户端向服务器发送 `inchoate client hello(CHLO)` 消息,请求服务器传输配置参数。

3. 服务器收到 `CHLO`,回复 `rejection(REJ)` 消息,其中包含服务器的部分配置参数

4. 客户端收到 `REJ`,提取并存储服务器配置参数,跳回到 (1) 。

5. 客户端向服务器发送 `full client hello` 消息,开始正式握手,消息中包括客户端选择的公开数。此时客户端根据获取的服务器配置参数和自己选择的公开数,可以计算出初始密钥 `K1`。

6. 服务器收到 `full client hello`,如果不同意连接就回复 `REJ`,同(3);如果同意连接,根据客户端的公开数计算出初始密钥 `K1`,回复 `server hello(SHLO)` 消息, `SHLO` 用初始密钥 `K1` 加密,并且其中包含服务器选择的一个临时公开数。

7. 客户端收到服务器的回复,如果是 `REJ` 则情况同(4);如果是 `SHLO`,则尝试用初始密钥 `K1` 解密,提取出临时公开数。

8. 客户端和服务器根据临时公开数和初始密钥 `K1`,各自基于 `SHA-256` 算法推导出会话密钥 `K2`。

9. 双方更换为使用会话密钥 `K2` 通信,初始密钥 `K1` 此时已无用,QUIC 握手过程完毕。之后会话密钥 `K2` 更新的流程与以上过程类似,只是数据包中的某些字段略有不同。

 

自定义拥塞控制

拥塞控制技术是网络在出现阻塞,超时等因流量过多情况而“自我调节”的能力。TCP提供了拥塞控制的功能,但是对于用户来说是“透明的”,及拥塞控制的流程和用户不相关,是自动进行的,而QUIC提供了一种方式让用户能够自定义拥塞控制信息。比如对于每一个包,不管是原始包还是重传包,都带有一个新的序列号(seq),这使得 QUIC 能够区分 ACK 是重传包还是原始包,从而避免了 TCP 重传模糊的问题。QUIC 同时还带有收到数据包与发出 ACK 之间的时延信息。这些信息能够帮助更精确的计算 RTT。

这种拥塞控制方法不再依赖内核的算法,而且能够在业务层根据业务场景实现自定义拥塞控制,配置和使用不同的拥塞控制算法和参数。

QUIC 在网络传输中所处的位置

QUIC只是一个协议,可以通过多种方法来实现,目前常见的实现有Google的quiche,微软的msquic,mozilla的neqo,以及基于go语言的quic-go等。本文将选用quic-go进行quic协议的分析

QUIC实现与测试(基于quic-go)

Ubuntu14华清远见

键入命令:git clone https://github.com/lucas-clemente/quic-go.git

下载库完成后进行编译go build,生成main文件

在服务端运行main,并新建终端作为客户端,访问支持QUIC的网站https://127.0.0.1:6121/demo/tile


 

./main -v -insecure -keylog ssl.log

Wireshark 对QUIC包的抓包和解密

由于QUIC是基于TLS1.3构建的,所以在用wireshark对quic进行抓包时,经常会出现只能看到部分quic包数据的情况,为了更好的了解和研究quic,我们需要对包进行解密。

未解密quic包:

解密quic包:

用wireshark打开本次抓到的包

然后打开wireshark 软件设置: [编辑]-[首选项]-[协议]-[TLS]-[(Pre)-Master-Secret log filename] 选中sslkeylog.log ,如下

得到解密后的quic包

继续之前的步骤,使用wireshark对QUIC协议进行抓包,使用前要确保wireshark更新到最新版本,以便能够识别QUIC协议,将过滤器设置为udp.port==6121,进行捕获。

可以看到捕获到的Connection ID与终端使用的链接ID相同,均为38598ebf9a75f4827304c696ec748e。以及一些其他的链接信息,如第一个包的类型为Initial,进行了0RTT的初始化。

再次对客户端进行测试,访问https://quic.rocks:4433/,可以看到网页的html内容,“You have successfully loaded quic.rocks using QUIC!”,以及网页的相关信息,Status:”200 OK” StatusCode:200 状态码为200表示访问成功。Proto:”HTTP/3”表示协议为http3.0。

在浏览器中输入地址,查看内容

作者:NP193

...全文
745 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧