网络篇03 | 应用层 quic

01 QUIC报文格式

红色部分是 Stream Frame 的报文头部,有认证。
绿色部分是报文内容,全部经过加密。
QUIC报文分为特殊报文和普通报文。
特殊报文又分为两类:版本协商报文(Version Negotiation Packets)及公共重置报文(Public Reset Packets)。
普通报文也分为两类:帧报文及FEC(Forward Error Correction)报文。
在这里插入图片描述

02 QUIC握手流程

在这里插入图片描述

QUIC在握手过程中使用Diffie-Hellman算法协商初始密钥,初始密钥依赖于服务器存储的一组配置参数,该参数会周期性的更新。初始密钥协商成功后,服务器会提供一个临时随机数,双方根据这个数再生成会话密钥。

具体握手过程如下:

  • 客户端判断本地是否已有服务器的全部配置参数,如果有则直接跳转到(5),否则继续
  • 客户端向服务器发送inchoate client hello(CHLO)消息,请求服务器传输配置参数
  • 服务器收到CHLO,回复rejection(REJ)消息,其中包含服务器的部分配置参数
  • 客户端收到REJ,提取并存储服务器配置参数,跳回到(1) 客户端向服务器发送full client
    hello消息,开始正式握手,消息中包括客户端选择的公开数。此时客户端根据获取的服务器配置参数和自己选择的公开数,可以计算出初始密钥。
  • 服务器收到full client hello,如果不同意连接就回复REJ,同(3);如果同意连接,根据客户端的公开数计算出初始密钥,回复server
    hello(SHLO)消息,SHLO用初始密钥加密,并且其中包含服务器选择的一个临时公开数。
  • 客户端收到服务器的回复,如果是REJ则情况同(4);如果是SHLO,则尝试用初始密钥解密,提取出临时公开数
  • 客户端和服务器根据临时公开数和初始密钥,各自基于SHA-256算法推导出会话密钥
  • 双方更换为使用会话密钥通信,初始密钥此时已无用,QUIC握手过程完毕。之后会话密钥更新的流程与以上过程类似,只是数据包中的某些字段略有不同。

对于上述流程可进行如下分解讨论:

  • 客户端没有缓存服务端的配置参数,此时流程如下图所示:

    • 此时的最短时间是1RTT,为什么说是最短想时间?因为服务端发送其配置参数可能需要多次交互才能完成。
      在这里插入图片描述
  • 客户端缓存服务端的配置参数,此时流程如下图所示
    在这里插入图片描述

quic时间分析:

  • 如果要取得0RTT的时间,客户端需要缓存已经验证的服务端配置信息,即使缓存了服务端的配置信息,也可能在于服务器的交互过程中,需要更新配置信息,此时的时间就会大于0RTT。如果客户端和服务端是第一次交互,那么必须发送CHLO信令,获取服务端配置,而这个过程就可能耗费多个RTT,因为服务端一次不一定愿意向未验证身份的客户端一次发送大量的配置信息。
  • 在客户端保存的服务端的配置信息有效且足够的情况下,QUIC握手能够取得0RTT的时间,这就是QUIC快的原因。

03 QUIC的优势

1)0-RTT 握手

TCP建立连接的三次握手,必然带来1个RTT(可以理解为网络延迟)的延时消耗。另外TLS加密协议,需要双方交换加密参数来进行加密,它需要2个RTT。所以HTTP2需要3个RTT的时间才能建立一个完整的传输链路。
如果RTT比较高,比如从中国到美国,距离太远,RTT至少几百毫秒,在中国打开美国的网站,3个RTT过去,光建立连接就要1-2秒,更别说传网页数据所花的时间。

QUIC协议对建链使用的RTT时间进行了改进。QUIC是可靠传输,所以也需要建链。但是它可以在建链的同时发送数据,链接建好,第一波数据也传输完了。所以它实现了0个RTT的数据下载,对于网页秒开,视频秒开场景能带来非常丝滑的体验。
对于首次建链的场景QUIC还是需要1-RTT来交换加密秘钥信息的,而建立过连接之后,后续的交互只能0-RTT就能完成了。

2)无队头阻塞

使用 TCP 时,如果丢失一个数据包,接下来所有的数据包都会停止传输,直到丢失的那个数据包被发送,这种现象被称为 “队头阻塞”,它会导致延迟明显增加。
下图的示例中显示了包含三个数据包的拥塞窗口的连接,其中 0 号数据包被丢弃。在只有单一数据流的 TCP 连接中,后续的数据包被阻止。QUIC 的多路连接拥有三个数据流,每个都能独立操作。因此,2 号和 3 号数据流仍然在正常传输,只有 1 号数据流中后续的数据包被阻止。
在这里插入图片描述

3)连接迁移

一条连接由5个元素决定:本机IP、本机端口、协议类型、对端IP、对端端口。就是我们俗称的IP五元组,它们其中任何一个发生变化,都会导致连接中断,数据传输终端。需要重新TCP握手建链。
由于QUIC底层使用的UDP,本身就不需要连接,而QUIC的连接是基于ID来识别唯一的,ID保存在用户手机和对端上,所以4G切换到WIFI时,不需要另外重新建立连接,即可继续观看视频。

4)流量控制和拥塞控制(改进)

流量控制算法—流级别

QUIC 拥有连接级(Connection)的流量控制,和流(Stream)级别的流量控制。
QUIC既可以管理一整个的QUIC连接的流量,也可以单独管理QUIC连接里的每一个流的流量。通过独立的Stream ID,一整套可靠传输的特性ACK、重传、传输速率等都可以针对Stream ID维度进行管理。可以实现让看视频用BBR算法,发消息用Cubic算法。

拥塞控制算法—自定义

TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复。
QUIC协议当前默认使用TCP的拥塞控制算法,并在其基础上进行了相应的改进;当然QUIC也支持其他的拥塞控制算法。主要的改进点有:
可插拔设计
单调递增的Packet Number
不允许Reneging
更多的Ack块
精确计算RTT时间

拥塞控制,对网络传输的重要性不言而喻,TCP的拥塞控制功能集成在操作系统的内核,应用程序是无法控制的。轻微变动就会影响整个机器上的全部TCP连接(已有的和新增的)。
而QUIC运行在用户态,应用程序不需要停机或升级,就能改变拥塞控制算法,可以像常规后台那样读取配置文件,即可设置不同的拥塞控制算法生效。
QUIC甚至可以对每一个连接实现不同的拥塞控制算法。如下图,同一台QUIC的服务器可以针对接入的不同业务,不同用户,不同网络制式,不同的地区,使用不同拥塞控制算法。这种多自定义用控能力,使得QUIC可以根据不同环境做出算法自适应调整。
在这里插入图片描述

5)前项纠错FEC

解决网络传输中被丢失的数据包,通常的技术手段是检测丢失、进而重发丢失的包来解决,这是TCP可靠传输的关键保障。从宏观的角度看这项技术,丢失重传其实是一种“事后弥补”的可靠性手段,也就是问题发生了(包丢失了),再去想办法解决(重传)。发现丢失,再重传,再确认,整个过程需要不少的时间来解决数据传输以外的网络问题。
QUIC协议的每个数据包除了本身的数据以外,会带有其他数据包的部分数据,在少量丢包的情况下,可以使用其他数据包的冗余数据完成数据组装而无需重传,从而提高数据的传输速度。具体实现类似于RAID5,将N个包的校验和(异或)建立一个单独的数据包发送,这样如果在这N个包中丢了一个包可以直接恢复出来,除此之外还可以用来校验包的正确性。
FEC(Forward error correction)是指在发送数据包的时候,提前多发一个或多个数据包,通过编码技术,将多发的包和原始包编码后一起发送。如果原始的数据包丢了一个,就能通过多发的这个包恢复回来。
在这里插入图片描述

早期的谷歌浏览器Chrom的QUIC版本存在一个FEC丢包恢复功能,不过在现网测试时,考虑FEC所带来的收益,和额外增加的带宽消耗,这一功能被废弃。
笔者曾经做过测试,FEC在小文件传输的场景下,是非常有效的。相比没有FEC的QUIC,带来的速度提升达到80%以上。

6)提升的安全性和隐私保护

QUIC 在传输层中内置了加密功能,从而验证整个负载(包括 header)。TCP 在 header 中不包含加密,使它非常容易受到攻击。QUIC 默认支持安全的 TLS,意味着端到端完全安全。

04 QUIC 的局限性

TCP 发明时,网络都是有线连接,而且相当可靠。但显然,情况已经发生改变。QUIC 对非可靠、无法预测的无线连接进行了改进,但并没有改变互联网传输的本质,它的局限性导致它只能改变某些特定使用场景。下面列举了一些额外的 QUIC 局限性:

1)迁移 app 面临巨大挑战

将 app 从 HTTP/2 迁移到 HTTP/3(或者从 TCP 迁移到 UDP)要费很大力气。整个过程需要将整个应用层实现和传输层实现转移到 UDP,并在服务端和客户端构建全新的解决方案。
这对于流媒体领域中资源相对有限的小厂商而言无疑挑战重重,同时也解释了谷歌和微软这样的科技巨头可以率先采用 QUIC 协议的原因。

2)采用受限

QUIC 的最大问题就是它的采用依然受限。几乎每个浏览器都接受使用 QUIC 进行简单的网页浏览,但是除了 chromium,没有浏览器将它设置为默认选项。
除此之外,在流媒体领域,除了谷歌和 Facebook(现更名为 Meta)之外,少有公司使用 QUIC。只有少数 CDN 提供商支持 QUIC,而其中的一些也只是验证了 QUIC 的实现,并没有为大规模部署准备好。这就带来了问题:如果你推出了使用 multi-CDN 并基于 QUIC 的新服务,那么将只有 20% 的访问使用 QUIC,因为你无法向用户证明它对用户体验的显著影响。

3)QUIC 包含 TCP 回退

QUIC 之所以被构建在 UDP 之上,部分原因是极少有中间件和网络设备拦截 UDP,但确实存在被拦截的风险,所以基于 QUIC 的 app 必须设计成能够回退到 TCP,以防万一。
这意味着 app(基于 QUIC)的开发者要同时开发和维护两个不同的版本(由于 TCP 回退和受到限制的采用率),导致他们的负担很重。
好消息是,随着最新的 DEVOPS 结构与 HTTP 的 Alt-Svc 标签的使用,支持两种协议要比以前简单得多。

4)无法检查数据包

网络防火墙无法解密 QUIC 流量来检查数据包,所以潜在的恶意流量非常有可能没有被标准安全功能检测出来而进入网络。因此,思科和 Palo Alto Networks 等安全厂商通常会在端口 80(Web 服务器)和 443(TSL)拦截 QUIC 数据包(认为它们包含恶意软件),迫使客户端回退使用 HTTP/2 和 TCP 协议。
但上述操作并不会显著影响内容用户体验,因为正确实现的流媒体服务会默认回退到 TCP+TLS,但这种操作可能会阻止率先部署 QUIC 的想法。只有解决这一挑战,QUIC 才能被各大企业广泛接受。

5)不具备某些 TCP 特性

人们理所当然地使用 TCP 中所默认包含的一些特性(比如 Throttling)。但使用 QUIC,你可能需要自己构建这些特性。
除此之外,HTTP/3 缺乏一些采用某些特定协议时所需的特性。比如,HTTP/3 仍然不支持成块传输(chunked transfer,即将视频切片分割为小块的能力),但 HTTP1.1 却支持该特性。这就限制了用于基于 QUIC 的视频传输的协议数量。
因此,尽管 QUIC 支持大部分常见传输协议(如 HLS、MPEG-DASH),但目前它无法支持更多新的协议,这些协议主要用于降低 glass-to-glass 延迟,比如依赖于成块传输的 LL CMAF(Low Latency Common Media Format)。
glass-to-glass 延迟: 指显示器屏幕和相机镜片之间的延迟,也可以叫做 “端到端延迟”,意思是开始( 捕获)并结束(显示)之间整个传输管道上的延迟 [1]。

6)更容易被 fingerprinting

恶意行为者很可能嗅探到互联网用户与所访问网站之间的网络流量,并通过被发现的数据包创建与特定网站相对应的不同模式,这种操作被称为 web fingerprinting。在早期流量连接阶段,TCP+HTTPS 似乎更能抵御 fingerprinting。

7)QUIC 可能需要更高的 CPU 使用率

一些观点认为 QUIC 所需的 HTTP/3 在客户端和服务端都占用了更多的 CPU 资源。然而,谷歌却持相反观点,认为 QUIC 有助于延长电池寿命。
无论如何,一旦 QUIC 进入主流技术栈,这一问题预计不会有太大影响。

8)需要实现的协议众多

由于 IETF 历经 5 年多才发布第一版 QUIC,所以目前市面上有 60 种 QUIC 版本实现,都开发于 QUIC 标准之前。因此,大部分 QUIC 版本或不支持完整的 QUIC 标准,或只支持自己版本的实现。只有当不同版本的 QUIC 与官方标准保持一致时,它才能被广泛采用。

9)互联网依然针对 TCP 进行优化

TCP 传输已经存在几十年,多年以来,TCP 应用通过在软件(如操作系统内核)和硬件(如网络接口和智能 NIC)中构建卸载性能而彻底得到了优化。而 QUIC 却不具备这一能力。它基于 UDP,位于用户空间内,所以它的端点,以及一些中间件功能在现阶段存在明显的劣势。不过,一旦 QUIC 被广泛采用,就会得到这种优化,所以这对于 QUIC 而言只是暂时性问题。

05 QUIC相关开源库

chromium:quic-client/server-demo模块

参考地址
https://www.chromium.org/quic
简介
Google提供的一个QUIC的源码使用Demo,但是值得注意的其是封装了支持HTTPS的QUIC实现,如果你想在模仿TCP Socket进行QUIC传输开发的话这个方案可能不适合你。
这个Demo主要用于集成测试,其并不具备大规模的生产环境使用的性能的可能性;换句话说,它就只是一个玩具,但是这个玩具挺值得你去玩味。

chromium:net模块

参考地址
https://chromium.googlesource.com/chromium/src.git
简介
如果你想在Android、iOS、Linux上面更灵活的使用QUIC的话,我觉得chromium的net模块是你的最好选择。
如果你需要封装QUIC在HTTP/HTTPS上面使用的话,可以参考上面的 quic-client/server-demo 的源码的相关使用方法,其实也就主要是研究QUIC源码库在spdy部分的内容。
如果你需要封装QUIC在更底层模仿TCP Socket操作的话,不妨看看quartc这个模块下面的API实现,具体的参考net下面的 quartc_session_test.cc 这个文件或者参考github上面的开源库posix_quic,不过后者是基于libquic的,API的调用流程并不一定适合你开发的版本,但是可以提供大方向的参考。

quic-go

参考地址
https://github.com/lucas-clemente/quic-go
简介
quic-go是使用Go语言来重写的QUIC协议实现库,从github上面看其对于iQUIC和gQUIC这两个分支流派都提供了支持,这个库当前也是比较活跃的。
从测试结果来看其稳定性和对于多端的支持相对于chromium来说仿佛就是一个小弟弟,但其也不能掩盖这个就是以前我们爸妈口中别人家的好孩子般存在。当然其性能还需要打磨,对于大规模线上应用还是需要谨慎考虑。

libquic

参考地址
https://github.com/devsisters/libquic
简介
libquic已经有多年没有更新了,其应该是民间从chromium中提取QUIC相关源码以及其依赖项而形成一个简易的开源代码库,对于苦于需要找个梯子下载动辄数十个GB的chromium源码开发者来说,无疑是一个福音,这个库很方便我们快速尝试QUIC的开发。
其支持 ninja 和 cmake 两种编译方式,但是遗憾的是从反馈上来看这个库并不支持iOS平台的编译。
基于这个平台的 HTTP 封装实现有 goquic 和 TCP Socket 封装实现有 posix_quic。

proto-quic

参考地址
https://github.com/google/proto-quic
简介
这个库是Google在chromium上面抽取出来发布于github的一个快速验证QUIC的开源库,同libquic一样并不需要下载太多的源码,但是其仅仅保证在Ubuntu上是可用的,现在Google已经转向了quiche这个开源分支上面进行独立QUIC库的开发。

quiche

参考地址
https://quiche.googlesource.com/quiche
简介
更新速度极快的谷歌QUIC开源代码库,其主要目的是希望将QUIC从chromium这个庞大的库独立出来作为其上游的实现方案来提供QUIC协议的支持,但是由于其也只是刚刚开始从 https://cs.chromium.org/chromium/src/net/third_party 中迁移过来,目前相关文档比较缺乏而且源码结构变动过大;暂时并不建议入手研究,但是强烈建议重点关注。

chromium:Cronet模块

参考地址
https://developer.android.com/guide/topics/connectivity/cronet
简介
Cronet主要为chromium的net模块进行了Android/iOS端的封装,并提供了相应的Java和OC接口,所以我们在移动端也是可以通过Cronet使用net模块里面QUIC协议。如果在客户端APP想要快速验证使用基于QUIC的HTTP请求的话,Cronet是一个非常合适的方案,但是在简单使用的前提下其灵活性也相对较差。

Stellite

参考地址
https://github.com/line/stellite
简介
这个是利用了Cronet,用C++封装了一层API而得到的这个Stellite开源库,解决了我们希望能在C/C++层面进行简单快速使用QUIC相关协议的需求。当然,对于灵活性和效率肯定没有比不上chromium的原生逻辑实现。

Caddy:

参考地址
https://github.com/mholt/caddy
简介
Caddy是当前支持QUIC的一个比较健壮的Web服务器,其底层是基于quic-go的实现。相对于nginx等框架还未提供对quic的支持,实验性支持quic的caddy也是当前web服务器支持QUIC协议的唯一理想选择。

06 参考资料

QUIC开发具体参考:
https://www.jianshu.com/p/f0aa0ae21809
https://www.jianshu.com/p/65daa1578808
https://www.jianshu.com/p/f0aa0ae21809
https://cloud.tencent.com/developer/news/385657
https://www.cnblogs.com/mod109/p/7372577.html
http://www.52im.net/forum.php?mod=viewthread&tid=1309

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值