文章目录
- 一、QUIC协议历史
- 1.1 问题:QUIC为什么在应用层实现
- 1.2 QUIC协议相关术语
- 1.3 QUIC和TCP对比
- 1.4 QUIC报文格式
- 1.4.1 QUIC报文格式-Stream帧1
- 1.4.2 QUIC报文格式-Stream帧2
- 二、QUIC的特点
- 2.1 连接建立低时延,
- 2.2 多路复用
- 流复用-HTTP1.1
- 流复用-HTTP2
- 流复用-HTTP3(QUIC)
- 2.3 无队头阻塞
- 2.4 灵活的拥塞控制机制
- 2.5 连接迁移
- 连接迁移1
- 连接迁移2
- 2.6 数据包头和包内数据的身份认证和加密
- 2.7 FEC前向纠错
- 2.8 可靠性传输
- 问题 1:发送端怎么知道发出的包是否被接收端收到了?
- 问题 2:既然包号是单调递增的,那接收端怎么保证数据的有序性呢?
- 2.9 带宽优化
- 三、QUIC开源库和应用
- 四、QUIC面临的挑战
一、QUIC协议历史
QUIC(Quick UDP Internet Connections)即 快速UDP网络连接,是由Google开发的基于UDP的传输层网络协议。QUIC的设计目标是提高网络应用程序的性能和可靠性,特别是在高延迟和不稳定网络环境下。
quic文档地址:https://quicwg.org/base-drafts/rfc9000.html
quic架构图:
1.1 问题:QUIC为什么在应用层实现
新的传输层协议通常会经过严格的设计,分析和评估可重复的结果,证明候选协议对 现有协议的正确性和公平性,开发新的传输层协议和它在操作系统进行广泛部署之间 通常需要花费数年的时间。
再者,用户与服务器之间要经过许多防火墙、NAT(地址转换)、路由器和其他中间设 备,这些设备很多只认TCP和UDP。如果使用另一种传输层协议,那么就会有可能无法 建立连接或者报文无法转发,这些中间设备会认为除TCP和UDP协议以外的协议都是不 安全或者有问题的。
1.2 QUIC协议相关术语
● QUIC连接:Client和Server之间的通信关心,Client发起连接,Server接受连接
● 流(Stream):一个QUIC连接内,单向或者双向的有序字节流。一个QUIC连 接可以同时包含多个Stream
● 帧(Frame):QUIC连接内的最小通信单元。一个QUIC数据包(packet)中的 数据部分包含一个或多个帧
1.3 QUIC和TCP对比
ORTT 指的是“Operation Round-Trip Time”(操作往返时间)。这通常是指在计算机网络中,一个数据包从发送端发出,到达接收端并得到响应,然后返回到发送端所需的总时间。这是一个关键的性能指标,因为它可以反映网络的延迟和响应能力。
ORTT的关键点包括:
- 延迟(Latency):这是数据从发送端到达接收端所需的时间。延迟可能受到物理距离、网络设备处理时间和传输介质等因素的影响。
- 处理时间(Processing Time):接收端处理数据包并生成响应所需的时间。这包括服务器处理请求的时间。
- 往返时间(Round-Trip Time, RTT):这是数据包从发送端发送,到达接收端并得到响应,然后返回到发送端的总时间。RTT 包括了延迟和处理时间。
如何测量ORTT:
- Ping命令:这是最常用的测量RTT的工具。Ping命令向目标主机发送ICMP ECHO请求,并计算从发送请求到接收响应所用的时间。
- Traceroute命令:Traceroute工具不仅测量RTT,还显示数据包经过的每一跳路由器的RTT,从而帮助识别网络路径中的延迟来源。
- 网络性能监测工具:如Wireshark、Nagios、Zabbix等,可以提供更详细的ORTT分析和报告。
优化ORTT的方法:
- 优化网络路径:通过选择更短或更高效的网络路径来减少延迟。
- 减少服务器处理时间:提高服务器性能,优化应用程序代码,以减少处理请求的时间。
- 使用内容分发网络(CDN):通过在地理上分布的服务器缓存内容,减少物理距离带来的延迟。
- 网络协议优化:使用更高效的通信协议,如HTTP/2、QUIC等,可以减少往返次数和时间。
理解和优化ORTT对于提高网络应用的性能和用户体验至关重要。
1.4 QUIC报文格式
一个 QUIC 数据包的格式
● Header 是明文的,包含 4 个字段:Flags、Connection ID、QUIC Version、Packet Number;
● Data 是加密的,可以包含 1 个或多个 frame,每个 frame 又分为 type 和 payload, 其中 payload 就是应用数据
1.4.1 QUIC报文格式-Stream帧1
数据帧有很多类型:Stream、ACK、Padding、Window_Update、Blocked 等,这里重点介 绍下用于传输应用数据的 Stream 帧。
Frame Type: 帧类型,占用 1 个字节
(1)Bit7:必须设置为 1,表示 Stream 帧,一个udp通道可以发送多个流
(2)Bit6:如果设置为 1,表示发送端在这个 stream 上已经结束发送数据,流将处于半关闭状态
(3)Bit5:如果设置为 1,表示 Stream 头中包含 Data length 字段
(4)Bit432:表示 offset 的长度。000 表示 0 字节,001 表示 2 字节,010 表示 3 字节,以此类推
(5)Bit10:表示 Stream ID 的长度。00 表示 1 字节,01 表示 2 字节,10 表示 3 字节,11 表示 4 字 节
1.4.2 QUIC报文格式-Stream帧2
Stream ID:流 ID,用于标识数据包所属的流。后面的流量控制和多路复用会涉及到。
Offset:偏移量,表示该数据包在整个数据中的偏移量,用于数据排序。
Data Length: 数据长度,占用 2 个字节,表示实际应用数据的长度 Data: 实际的应用数据。
二、QUIC的特点
2.1 连接建立低时延,
典型TCP+TLS连接
1.首先,执行三次握手,建立 TCP 连接(蓝色部分);
2.然后,执行 TLS 握手,建立 TLS 连接(黄色部分);
3.此后开始传输业务数据;
客户端和服务器之间要进行多轮协议交互,才能建立 TLS 连接,延迟相当严重。 平时访问 https 网站明显比 http 网站慢,三次握手和 TLS 握手难辞其咎。
QUIC首次连接
● 共3RTT:TCP+TLS1.2中: TCP三次握手建立连接需要1个RTT;TLS需要2个RTT完成 身份验证;传输数据
● 共2RTT:TCP+TLS1.3中: TCP三次握手建立连接需要1个RTT;TLS需要1个RTT完成 身份验证;传输数据
● 共1RTT: 首次QUIC连接中,Client 向 Server 发送消息,请求传输配置参数和加 密相关参数;Server 回复其配置参数;传输数据
注解:
注意到,三次握手中的 ACK 包与 handshake 合并在一起发送。 这是 TCP 实现中使用的 延迟确认 技术, 旨在减少协议开销,改善网络性能。
QUIC再次连接
● 再次连接的概念:Client已经访问过Server,在本地存放了Cookie。
● 2RTT:TCP+TLS1.2中: TCP三次握手建立连接需要1个RTT;TLS需要1个RTT完成身份 验证(由于缓存的存在,减少1RTT);传输数据
● 1RTT:TCP+TLS1.3 中: TCP三次握手建立连接需要1个RTT;传输数据
● 0RTT:在客户端与服务端的再次QUIC连接中,Client 本地已有 Server 的全部配置 参数(缓存),据此计算出初始密钥,直接发送加密的数据包。
2.2 多路复用
在一个网页里面总是会有多个数据要传输,总是希望多个数据能够同时传输,以此 来提高用户的体验。
流复用-HTTP1.1
在HTTP1.1中:
● 每个TCP连接同时只能处理一个请求—响应,为了提高响应速度,需要 同时创建多个连接,但是多个连接管理比较复杂
流复用-HTTP2
在HTTP2中:
● 每个TCP连接里面有多个逻辑上独立的多个流(stream)
● 每个流可以传输不同的文件数据
● 解决了HTTP1一个连接无法同时传输多个数据的问题
● 缺点:容易出现队头阻塞问题
流复用-HTTP3(QUIC)
而在基于QUIC的HTTP3中:
● 借鉴了HTTP2中流的概念
● 流 之 间 互 相 独 立 , 即 不 同 流 之 间 的 数 据 之 间 交 付 顺 序 无 关 ( 如果 stream2的数据丢失,只会影响排在stream2后面的数据,stream1和 stream3的数据不会被影响)
● 建立在UDP之上,没有依赖性
2.3 无队头阻塞
HTTP/2会出现队头阻塞问题,而在基于QUIC的HTTP/3中则很好地解决这一问题.
UDP中的各个数据报独立,基于UDP的QUIC中的各个stream独立
即使stream2里有一个包丢失,因为stream之间互相独立无关联,所以不会 阻塞stream3、stream4,依然可以交付给上层
2.4 灵活的拥塞控制机制
TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快 速恢复
(1)New Reno:基于丢包检测
(2)CUBIC:基于丢包检测
(3)BBR:基于网络带宽 可以自己去设置拥塞控制机制,也可以自己去实现拥 塞控制协议
QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法。
这个机制还是可插拔的,什么叫可插拔呢?就是能够非常灵活地生效,变 更和停止,可以根据场景来切换不同的方法。
QUIC协议在用户空间实现,应用程序不需要停机和升级就能实现拥塞控制 的变更,在服务端只需要修改一下配置,reload 一下,完全不需要停止服务 就能实现拥塞控制的切换。甚至可以为每一个请求都设置一种拥塞控制算法。
而TCP在内核态,其拥塞控制难以进行修改和升级。
2.5 连接迁移
连接迁移1
场景:
- 使用WIFI上网的时候突然网络不好,于是切换成移动数据模式;又或者没有流量上 不了网,于是连接WIFI
- 户外4G基站的切换,比如在做地铁时正在语音通话,不同的站点会切换不同的基站。 对于5G基站则切换更为频繁。
连接迁移:当客户端切换网络时,和服务器的连接并不会断开,仍然可以正常通信, 对于 TCP 协议而言,这是不可能做到的。因为 TCP 的连接基于 4 元组:源 IP、源端口、 目的 IP、目的端口,只要其中 1 个发生变化,就需要重新建立连接。但 QUIC 的连接是 基于 64 位的 Connection ID,网络切换并不会影响 Connection ID 的变化,连接在逻辑上 仍然是通的。
IP地址或者端口号发生变化时,只要ID不变,依然能够维持原有连接,上层业务逻辑感 知不到变化,不会中断。
连接迁移2
假设客户端先使用 IP1 发送了 1 和 2 数据包,之后切换网络,IP 变更为 IP2,发送了 3 和 4 数据包,服务器根据数据包头部的 Connection ID 字段可以判断这 4 个包是来自于同一 个客户端。QUIC 能实现连接迁移的根本原因是底层使用 UDP 协议就是面向无连接的。
2.6 数据包头和包内数据的身份认证和加密
相比于TCP,QUIC的安全性是内置的,也就是说是必须的。
在恶意环境下性能与TLS类似,友好环境下优于TLS,因为TLS使用一个会话密钥, 如果这个密钥被截获的话就不能保证之前数据的安全性。
而QUIC使用两个密钥——初始密钥和会话密钥,并且QUIC提供密码保护,TLS不提供。
除此之外,QUIC的包头经过身份认证,包内数据是加密的。这样如果QUIC数据 被恶意修改的话接收端是可以发现的,降低了安全风险。
而且数据加密之后,可以通过像防火墙或者nat这些中间件。
2.7 FEC前向纠错
FEC是Forward Error Correction前向错误纠正的意思,就是通过多发一些冗余的包, 当有些包丢失时,可以通过冗余的包恢复出来,而不用重传。这个算法在多媒 体网关拥塞控制有重要的地位。QUIC的FEC是使用的XOR的方式,即发N + 1个包, 多发一个冗余的包,在正常数据的N个包里面任意一个包丢了,可以通过这个冗 余的包恢复出来,使用异或可以做到切换网络操持连接。
1 2 3 4,重新编码 出来 5 4 3 2 1 -> 5 4 3 2 -> 4 3 2 1
2.8 可靠性传输
QUIC 是基于 UDP 协议的,而 UDP 是不可靠传输协议,那 QUIC 是如何实现可靠传输的呢? 可靠传输有 2 个重要特点:
(1)完整性:发送端发出的数据包,接收端都能收到
(2)有序性:接收端能按序组装数据包,解码得到有效的数据
问题 1:发送端怎么知道发出的包是否被接收端收到了?
解决方案:通过包号(PKN)和确认应答(SACK)
(1)客户端:发送 3 个数据包给服务器(PKN = 1, 2,3)
(2)服务器:通过 SACK 告知客户端已经收到了 1 和 3,没有收到 2
(3)客户端:重传第 2 个数据包(PKN=4) 由此可以看出,QUIC 的数据包号是单调递增的。 也就是说,之前发送的数据包(PKN=2)和重传的 数据包(PKN=4),虽然数据一样,但包号不同。
问题 2:既然包号是单调递增的,那接收端怎么保证数据的有序性呢?
解决方案:通过数据偏移量 offset
每个数据包都有一个 offset 字段,表示在整个数据中的偏移量。
接收端根据 offset 字段就可以对异步到达的数据包进行排 序了。为什么 QUIC 要将 PKN 设计为单调递增?解决 TCP 的重传歧义问题:
由于原始包和重传包的序列号是一样的,客户端不知道服 务器返回的 ACK 包到底是原始包的,还是重传包的。但 QUIC 的原始包和重传包的序列号是不同的,也就可以判 断 ACK 包的归属。
2.9 带宽优化
QUIC协议对带宽使用进行了优化,通过更高效的ACK机制和流控算法,减少了不必要的数据传输和重复确认。
QUIC最初由Google在其自家服务中使用,并且已经成为HTTP/3的基础。HTTP/3是HTTP协议的最新版本,旨在利用QUIC的优势来提高网页加载速度和用户体验。
QUIC协议已经得到了广泛的支持和应用,许多现代浏览器(如Google Chrome、Mozilla Firefox)和服务器(如NGINX)都已经实现了对QUIC的支持。
QUIC协议通过结合UDP的低延迟和TCP的可靠性,为现代互联网应用提供了一种高效、安全的传输方案。
三、QUIC开源库和应用
- google的gquic 起源最早, 不过它不是单独项目, 代码在chromium项目里边, 用的 是c++写的, 可能不是很适合.
- 微软的msquic, 用c写的, 跨平台, 不过开始得比较晚(好像2020才开始, 不是很成 熟).
- facebook的quic 用的是c++写的. 暂不考虑.
- nginx的quic 没有自带client, 但它可与ngtcp2联调.
- litespeed的 lsquic 是基于MIT的, 开始于2017年, 还算比较稳定, 用c语言编写, 各 主流平台都有通过测试, 有server/client/lib, 它用于自家的各种产品, 暂时看上去 是最合适的.
- ngtcp2, 它是一个实验性质的quic client, 很简洁, 实现了几乎每一版ietf draft. 从 代码简洁性上来看, 它无疑是最好的。目前srs流媒体服务器、curl等开源项目有 基于ngtcp2做二次开发。
ngtcp2 仓库 https://github.com/ngtcp2/ngtcp2,ngtcp2采用c语言实现,范例client和server使用了c++17的特性,我们需要升级编译器(比如, clang >= 8.0, 或 gcc >= 8.0).
四、QUIC面临的挑战
- 路由封杀UDP 443端口(这正是QUIC部署的端口)
- UDP包过多,由于QoS(Quality of Service,服务质量)限定,会被服务器商认 为是攻击,UDP包被丢弃
- 无论是路由器还是防火墙目前对QUIC都还没做好准备
- QUIC有较多的开源库,标准尚未真正落地,对应的开源库没有经过充分的验证, 不建议中小厂商在QUIC上完全跟进。