571
社区成员




QUIC(Quick UDP Internet Connections)是由Google提出的用于替代TCP(Transmission Control Protocol)的互联网数据传输协议.它引入了许多新的特性,从而在理论上拥有比TCP更好的性能.例如,它通过多路传输解决了队头阻塞问题,通过0-RTT握手降低了传输层握手延时,以及通过连接迁移更好地对移动性提供支持。与目前使用最为广泛的传输层协议TCP相比,QUIC引入了许多新的特性。例如,它支持一条传输层连接上的多路传输和延时更低的握手。这些新特性使得QUIC在大部分场景下表现出比TCP更好的性能。例如更短的网页加载时间、更低的握手延时等。在安全性方面,QUIC使用了比现有的TLS1.2更加安全的加密传输协议TLS1.3,从而更好地保障通信安全以及用户隐私。此外,IEFT正致力于第3代HTTP协议。即HTTP/3标 准的制定,HTTP/3借鉴了其前身HTTP/2设计上的优点并从其不足之处中吸取了教训,最终基于QUIC实现了HTTP所规定的语义。HTTP/3标准的确立将有利于QUIC在互联网上获得更为广泛的关注与应用。下图是QUIC在协议栈中的位置。
在IETF工作组的推进下,QUIC已经在国内外很多场大厂落地,相信在不久的将来,取代TCP只是时间问题。
Quic 相比现在广泛应用的 http2+tcp+tls 协议有如下优势:
1、减少了 TCP 三次握手及 TLS 握手时间。
首先体现在低延迟连接上,相比 HTTPS,QUIC 建立连接的过程中至少少一个 RTT;与 TCP 不同,UDP 不是面向连接的,因此 QUIC 连接只需首次建立一次 QUIC 握手,后续便可在 0RTT 完成。其次是优化了多路复用,解决了 TCP 连接下请求丢包导致的队头阻塞的问题。
2、改进的拥塞控制。
QUIC 将拥塞控制放在应用层,更新拥塞控制算法不需要停机升级,使得在某些场景下可更有效地改变拥塞策略,达到更优的效果;二是连接迁移,TCP 使用四元组 (源 IP,源端口,目的 IP,目的端口) 标识连接,网络切换时会导致重连,而 QUIC 使用自定义的 Connection ID 作为链接标识,只要客户端使用的 Connection ID 不变,即使网络切换也能保证链接不中断。
3、更安全。
TCP 协议头部未经过加密和认证,但 QUIC 所有的头部信息均经过认证,并且对所有信息进行加密,可有效避免数据传输过程中被中间设备截取并篡改。
QUIC-GO是QUIC协议基于Go语言的一种实现,我们可以用它来构建客户端或服务端来对QUIC协议进行测试。
首先安装Go
wget https://go.dev/dl/go1.17.6.linux-amd64.tar.gz
解压到/usr/local目录下
tar -C /usr/local -xzf go1.17.6.linux-amd64.tar.gz
配置环境变量,将如下两行插入/etc/profile中
vim /etc/profile
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
source /etc/profile
配置成功后输入go version可看到版本代表配置成功
接下来下载QUIC-GO
git clone https://github.com/lucas-clemente/quic-go.git
如未安装git则需
apt install git
安装好后进入quic-go根目录下的exmaple文件夹,进行编译
cd exmaple
go build main.go
然后运行服务端
./main -qlog -v -tcp
必须带上-tcp
参数是因为浏览器第一次访问时仍然是要通过TCP进行的,如果不带浏览器将无法访问。启动成功后如下所示。
接下来使用firefox浏览器对服务器进行访问,先在浏览器输入about:config,然后搜索http3,将值设置为true以打开http3的实验特性。
输入网址https://localhost:6121/demo/tile,F12打开调试工具
可以看到浏览器是以HTTP3的协议来进行访问的。接下来wireshark进行抓包。
可看到quic-go的服务端测试已经成功。
建立连接的低延迟可以说是QUIC的核心特性。发送数据之前,QUIC只需要0RTT
就能够建立连接,而传统的TCP+TLS则需要1-3RTT
才能够建立连接。具体的QUIC建立连接的方法如下:
QUIC客户端第一次连接到服务器时,客户端必须执行1次往返握手,以获取完成握手所需的信息。客户端发送早期(empty)客户端Hello问候(CHLO)
,服务器发送拒绝(rejection)(REJ)
,其中包含客户端前进所需的信息,包括源地址令牌和服务器的证书。客户端下次发送CHLO时,可以使用以前连接中的缓存凭据来立即将加密的请求发送到服务器。
比如上图左边是 HTTPS 的一次完全握手的建连过程,需要 3 个 RTT。而 QUIC 呢?由于建立在 UDP 的基础上,同时又实现了 0RTT 的安全握手,所以在大部分情况下,只需要 0 个 RTT 就能实现数据发送,在实现前向加密的基础上,并且 0RTT 的成功率相比 TLS 的 Sesison Ticket 要高很多。
传输的基本单元将是一个标准的UDP包(又名,包)。注意确保所有的数据传输将会被分解成刚好放入一个包的块。
包括用来协商一个连接的第一包在内的所有包,将利用AEAD加密。第一包将利用一个默认的空加密密钥,并且AEAD将只是用来排除意外干预(作为一个高质量的校验和)。加密协商将发生在流1,由客户端产生。
QUIC包由头(header)和有效载荷(payload)组成。其中,payload是AEAD算法认证的密文。
每个包的头包括:
*1字节的公共标识,详述头的剩下部分的设计
*全局唯一标识符
*QUIC版本
*包序列号
数据包有效载荷由一系列帧组成:
FEC包有效载荷由冗余数据组成:
在解密之后,我们将有一个明文有效载荷块,它包括:
*1字节的私有标识
*FEC组号
*一系列自我标识帧
我们所熟悉的TCP拥塞控制包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复。其中TCP中拥塞控制是被编译进内核中的,如果想要更改就需要改变内核参数,但是想要对已有的拥塞控制算法进行更改就需要重新编译内核,Linux 4.9 中引入了基于时延的拥塞控制算法 BBR,这打破了以往是靠丢包驱动的拥塞控制算法,但是想要在TCP中使用,就必须升级内核至4.9以上。QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法,同时也支持 CubicBytes, Reno,RenoBytes, BBR, PCC 等拥塞控制算法。
QUIC和TCP的对比
可插拔:
QUIC 可以针对某个特殊场景使用不同的拥塞控制算法,且切换十分简单。
TCP 想要切换拥塞控制算法是针对全局的,要么更改内核参数,要么重新编译内核。切换十分不便。
单调递增的 Packet Number:
QUIC 的数据包是单调递增的Packet Number。这帮助传输解决了重传歧义,方便计算RTO,解决了TCP的队头阻塞。因为TCP的数据是流,确认机制是 seq和ack。QUIC使用 Packet Number 代替了 TCP 的 sequence number,并且每个 Packet Number 都严格递增,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值。而 TCP 呢,重传 segment 的 sequence number 和原始的 segment 的 Sequence Number 保持不变,也正是由于这个特性,引入了 Tcp 重传的歧义问题。后面tcp引入了timestamp解决了这些问题。
TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改,注入和窃听。比如修改序列号、滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。但是 QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。这样只要对 QUIC 报文任何修改,接收端都能够及时发现,有效地降低了安全风险。
作者:NP323