UDP可靠性传输-QUIC

一、QUIC协议

QUIC ,即 快速UDP网络连接 ( Quick UDP Internet Connections ), 是由 Google 提出的实验性网络传输协议 ,位于 OSI 模型传输层。 QUIC 旨在解决 TCP 协议的缺陷,并最终替代 TCP 协议, 以减少数据传输,降低连接建立延 迟时间,加快网页传输速度。

标准文档地址:https://quicwg.org/base-drafts/rfc9000.html

1. QUIC框图

UDP可靠性传输-QUIC_第1张图片

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对比

UDP可靠性传输-QUIC_第2张图片

2. QUIC报文格式

2.1 QUIC数据包格式

UDP可靠性传输-QUIC_第3张图片

  • Header是明文的,包含4个字段:Flags、Connection ID、QUIC Version、Packet Number
  • Data 是加密的,可以包含 1 个或多个 frame,每个 frame 又分为 type 和 payload, 其中 payload 就是应用数据

2.2 QUIC Stream帧

数据帧有很多类型:Stream、ACK、Padding、Window_Update、Blocked 等,这里重点介 绍下用于传输应用数据的 Stream 帧。

UDP可靠性传输-QUIC_第4张图片

Frame Type:帧类型,占用1个字节

  1. Bit7:必须设置为 1,表示 Stream 帧
  2. Bit6:如果设置为 1,表示发送端在这个 stream 上已经结束发送数据,流将处于半关闭状态
  3. Bit5:如果设置为 1,表示 Stream 头中包含 Data length 字段
  4. Bit4-2:表示 offset 的长度。000 表示 0 字节,001 表示 2 字节,010 表示 3 字节,以此类推
  5. Bit1-0:表示 Stream ID 的长度。00 表示 1 字节,01 表示 2 字节,10 表示 3 字节,11 表示 4 字 节

Stream ID:流 ID,用于标识数据包所属的流。后面的流量控制和多路复用会涉及到。

Offset:偏移量,表示该数据包在整个数据中的偏移量,用于数据排序。

Data Length: 数据长度,占用 2 个字节,表示实际应用数据的长度.

Data: 实际的应用数据

3. QUIC的特点

  1. 连接建立低时延
  2. 多路复用
  3. 无队头阻塞
  4. 灵活的拥塞控制机制
  5. 连接迁移
  6. 数据包头和包内数据的身份认证和加密
  7. FEC前向纠错
  8. 可靠性传输
  9. 其他

3.1 连接建立低延时

3.1.1 典型TCP+TLS连接

UDP可靠性传输-QUIC_第5张图片

  1. 首先,执行三次握手,建立TCP连接(蓝色部分)
  2. 然后,执行TLS握手,建立TLS连接(黄色)
  3. 此后开始传输业务数据

注意到,三次握手中的 ACK 包与 handshake 合并在一起发送。 这是 TCP 实现中使用的 延迟确认 技术, 旨在减少协议开销,改善网络性能。

客户端和服务器之间要进行多轮协议交互,才能建立 TLS 连接,延迟相当严重。 平时访问 https 网站明显比 http 网站慢,三次握手和 TLS 握手难辞其咎。

3.1.2 首次连接对比

UDP可靠性传输-QUIC_第6张图片

  • 共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 回复其配置参数;传输数据

3.1.2 再次连接对比

UDP可靠性传输-QUIC_第7张图片

  • 再次连接的概念: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 的全部配置 参数(缓存),据此计算出初始密钥,直接发送加密的数据包。

3.2 多路复用

在一个网页里面总是会有多个数据要传输,总是希望多个数据能够同时传输,以此 来提高用户的体验。

UDP可靠性传输-QUIC_第8张图片

3.2.1 HTTP1.1

每个TCP连接同时只能处理一个请求—响应,为了提高响应速度,需要 同时创建多个连接,但是多个连接管理比较复杂。

3.2.2 HTTP2

  • 每个TCP连接里面有多个逻辑上独立的多个流(stream)
  • 每个流可以传输不同的文件数据
  • 解决了HTTP1一个连接无法同时传输多个数据的问题
  • 缺点:容易出现队头阻塞问题

3.2.3 QUIC(HTTP3)

  • 借鉴了HTTP2中流的概念
  • 流之间互相独立,即不同流之间的数据之间交付顺序无关(如果 stream2的数据丢失,只会影响排在stream2后面的数据,stream1和 stream3的数据不会被影响)
  • 建立在UDP之上,没有依赖性

3.3 无队头阻塞

HTTP/2会出现队头阻塞问题,而在基于QUIC的HTTP/3中则很好地解决这一问题。

UDP可靠性传输-QUIC_第9张图片

  • UDP中的各个数据报独立,基于UDP的QUIC中的各个stream独立
  • 即使stream2里有一个包丢失,因为stream之间互相独立无关联,所以不会 阻塞stream3、stream4,依然可以交付给上层

3.4 灵活的拥塞控制和机制

TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复。

  • New Reno:基于丢包检测
  • CUBIC:基于丢包检测
  • BBR:基于网络带宽

QUIC协议可以自己设置或实现拥塞控制协议。

QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法。 这个机制还是可插拔的,能够非常灵活地生效,变更和停止,可以根据场景来切换不同的方法。

QUIC协议在用户空间实现,应用程序不需要停机和升级就能实现拥塞控制 的变更,在服务端只需要修改一下配置,reload 一下,完全不需要停止服务 就能实现拥塞控制的切换。甚至可以为每一个请求都设置一种拥塞控制算法。

而TCP在内核态,其拥塞控制难以进行修改和升级。

3.5 连接迁移

连接迁移:当客户端切换网络时,和服务器的连接并不会断开,仍然可以正常通信。

对于 TCP 协议而言,这是不可能做到的。因为 TCP 的连接基于 4 元组:源 IP、源端口、 目的 IP、目的端口,只要其中 1 个发生变化,就需要重新建立连接。

但 QUIC 的连接是 基于 64 位的 Connection ID,网络切换并不会影响 Connection ID 的变化,连接在逻辑上 仍然是通的。IP地址或者端口号发生变化时,只要ID不变,依然能够维持原有连接,上层业务逻辑感 知不到变化,不会中断。

客户端的Connection ID是唯一的。

3.6 数据包头和包内数据的身份认证和加密

相比于TCP,QUIC的安全性是内置的,也就是说是必须的。

在恶意环境下性能与TLS类似,友好环境下优于TLS,因为TLS使用一个会话密钥, 如果这个密钥被截获的话就不能保证之前数据的安全性。

而QUIC使用两个密钥——初始密钥和会话密钥,并且QUIC提供密码保护,TLS不提供。

除此之外,QUIC的包头经过身份认证,包内数据是加密的。这样如果QUIC数据 被恶意修改的话接收端是可以发现的,降低了安全风险。

而且数据加密之后,可以通过像防火墙或者nat这些中间件。

两个密钥的生成可参考:https://blog.csdn.net/chuanglan/article/details/85106706

3.7 FEC前向纠错

FEC是Forward Error Correction前向错误纠正的意思,就是通过多发一些冗余的包, 当有些包丢失时,可以通过冗余的包恢复出来,而不用重传。这个算法在多媒 体网关拥塞控制有重要的地位。QUIC的FEC是使用的XOR的方式,即发N + 1个包, 多发一个冗余的包,在正常数据的N个包里面任意一个包丢了,可以通过这个冗 余的包恢复出来,使用异或可以做到切换网络操持连接。

3.8 可靠性传输

QUIC 是基于 UDP 协议的,而 UDP 是不可靠传输协议,那 QUIC 是如何实现可靠传输的呢?

可靠性传输有2个重要特点:

  1. 完整性:发送端发出的数据包,接收端都能收到
  2. 有序性:接收端能按序组装数据包,解码得到有效的数据

3.8.1 有序性设计

QUIC每个Stream帧中都有offset字段和StreamID字段,这使得乱序接收的数据能够有序排列。

3.8.2 完整性设计

发送端通过包号(PKN)和确认应答(SACK)确认发送数据完整性。

UDP可靠性传输-QUIC_第10张图片

  1. 客户端:发送 3 个数据包给服务器(PKN = 1, 2,3)
  2. 服务器:通过 SACK 告知客户端已经收到了 1 和 3,没有收到 2
  3. 客户端:重传第 2 个数据包(PKN=4)

尽管QUIC会重传数据包,但是新的数据包的PKN的继续递增的,即之前发送的数据包(PKN=2)和重传的 数据包(PKN=4),虽然数据一样,但包号不同。这也解决了TCP中,原始包和重传包的序列号一样带来的重传歧义问题。

由于TCP原始包和重传包的序列号是一样的,客户端不知道服 务器返回的 ACK 包到底是原始包的,还是重传包的。但 QUIC 的原始包和重传包的序列号是不同的,也就可以判 断 ACK 包的归属。

4. QUIC开源库

  1. google的gquic 起源最早, 不过它不是单独项目, 代码在chromium项目里边, 用的 是c++写的, 可能不是很适合。
  2. 微软的msquic, 用c写的, 跨平台, 不过开始得比较晚。
  3. facebook的quic 用的是c++写的. 暂不考虑。
  4. nginx的quic 没有自带client, 但它可与ngtcp2联调。
  5. litespeed的 lsquic 是基于MIT的, 开始于2017年, 还算比较稳定, 用c语言编写, 各 主流平台都有通过测试, 有server/client/lib, 它用于自家的各种产品, 暂时看上去 是最合适的。
  6. ngtcp2, 它是一个实验性质的quic client, 很简洁, 实现了几乎每一版ietf draft. 从 代码简洁性上来看, 它无疑是最好的。目前srs流媒体服务器、curl等开源项目有 基于ngtcp2做二次开发。

UDP可靠性传输-QUIC_第11张图片

4.1 ngtcp2

  1. https://github.com/ngtcp2/ngtcp2
  2. 采用C语言实现
  3. 范例client和server使用了c++17的特性,我们需要升级编译器(比如, clang >= 8.0, 或 gcc >= 8.0)
  4. 编译文档见《QUIC开源库安装和实践.pdf》

参考博客:UDP可靠性传输-QUIC - 幻cat - 博客园

你可能感兴趣的:(Linux网络编程,udp,网络,tcp/ip)