网络协议设计与实现SPDY/HTTP2/QUICweb开发

QUIC协议规范

2017-05-09  本文已影响397人  hanpfei

title: QUIC协议规范
date: 2017-01-13 18:35:49
categories: 网络协议
tags:


介绍

QUIC (Quick UDP Internet Connection,快速UDP互联网连接) 是一个新的基于UDP的多路复用且安全的传输协议,它从头开始设计,且为 HTTP/2 语义做了优化。尽管以 HTTP/2 作为主要的应用协议而构建,然而 QUIC 的构建是基于传输和安全领域数十年的经验的,且实现了使它成为有吸引力的现代通用传输协议的机制。QUIC提供了等价于 HTTP/2 的多路复用和流控,等价于 TLS 的安全机制,及等价于 TCP 的连接语义、可靠性和拥塞控制。

QUIC完全运行于用户空间,它当前作为 Chromium 浏览器的一部分发布给用户,以便于快速的部署和实验。作为基于 UDP 的用户空间传输协议,QUIC 可以做一些由于遗留的客户端和中间设备,或旷日持久的操作系统开发和部署周期的阻碍,而被证明很难在现有的协议中部署的创新。

QUIC 的一个重要目标是通过快速的实验获得更好的传输设计相关的知识。作为结果,我们希望将其中的一些精华的改动迁移进 TCP 和 TLS,后者通常有着长得多的迭代周期。

这份文档描述标准化前 QUIC 协议的概念设计和协议规范。补充资料描述了加密和传输握手 [QUIC-CRYPTO],及丢失恢复和拥塞控制 [draft-iyengar-quic-loss-recovery]。其它资源,包括一份更详细的相关文档,可以在 Chromium 的 QUIC 主页 找到。

基于早期的部署的 QUIC 标准化建议为 [draft-hamilton-quic-transport-protocol],[draft-shade-quic-http2-mapping],[draft-iyengar-quic-loss-recovery],和 [draft-thomson-quic-tls]。

术语和定义

QUIC中使用的所有整型值,包括长度、版本号和类型,都是小尾端字节序,而不是网络字节序。QUIC不强制动态大小的帧中的类型对齐。

贯穿本文档使用的一些术语定义如下。

QUIC概述

我们现在简要介绍 QUIC 的关键机制和优势。QUIC 在功能上等价于 TCP + TLS + HTTP/2,但基于 UDP 实现。QUIC 相对于 TCP + TLS + HTTP/2 的主要优势包括:

连接建立延迟

QUIC将加密和传输握手结合在一起,减少了建立一条安全连接所需的往返。QUIC 连接通常是 0-RTT,意味着相比于 TCP + TLS 中发送应用数据前需要 1-3 个往返的情况,在大多数 QUIC 连接中,数据可以被立即发送而无需等待服务器的响应。

QUIC 提供了一个专门的流(流 ID 为1)用于执行握手,但握手协议的详细内容超出了本文档的范围。要查看当前握手协议的完整描述,请参考 QUIC Crypto Handshake 文档。QUIC 当前的握手协议将在未来被 TLS 1.3 替代。

灵活的拥塞控制

QUIC 具有可插入的拥塞控制,且有着比 TCP 更丰富的信令,这使得 QUIC 相对于 TCP 可以为拥塞控制算法提供更丰富的信息。当前,默认的拥塞控制是
TCP Cubic 的重实现;我们目前在实验替代的方法。

更丰富的信息的一个例子是,每个包,包括原始的和重传的,都携带一个新的包序列号。这使得 QUIC 发送者可以将重传包的 ACKs 与原始传输包的 ACKs 区分开来,这样可以避免 TCP 的重传模糊问题。QUIC ACKs 也显式地携带数据包的接收与其确认被发送之间的延迟,与单调递增的包序列号一起,这样可以精确地计算往返时间(RTT)。

最后,QUIC 的 ACK 帧最多支持 256 个 ack 块,因此在重排序时,QUIC 相对于 TCP(使用SACK)更有弹性,这也使得在重排序或丢失出现时,QUIC 可以在线上保留更多在途字节。客户端和服务器都可以更精确地了解到哪些包对端已经接收。

流和连接的流量控制

QUIC 实现了流级和连接级的流量控制,紧跟 HTTP/2 的流量控制。QUIC 的流级流控工作如下。QUIC 接收者通告每个流中接收者最多想要接收的数据的绝对字节偏移。随着数据在特定流中的发送,接收和传送,接收者发送WINDOW_UPDATE 帧,帧增加该流的通告偏移量限制,允许对端在该流上发送更多的数据。

除了每个流的流控制外,QUIC 还实现连接级的流控制,以限制 QUIC 接收者愿意为连接分配的总缓冲区。连接的流控制工作方式与流的流控制一样,但传送的字节和最大的接收偏移是所有流的总和。

与 TCP 的接收窗口自动调整类似,QUIC 实现流和连接流控制器的流控制信用的自动调整。如果 QUIC 的自动调整似乎限制了发送方的速率,并且在接收应用程序缓慢的时候抑制发送方,则 QUIC 的自动调整会增加每个 WINDOW_UPDATE 帧发送的信用额。

多路复用

基于 TCP 的 HTTP/2 深受 TCP 的队首阻塞问题困扰。由于 HTTP/2 在 TCP 的单个字节流抽象之上多路复用许多流,一个 TCP 片段的丢失将导致所有后续片段的阻塞直到重传到达,而封装在后续片段中的 HTTP/2 流可能和丢失的片段毫无关系。

由于 QUIC 是为多路复用操作从头设计的,携带个别流的的数据的包丢失时,通常只影响该流。每个流的帧可以在到达时立即发送给该流,因此,没有丢失数据的流可以继续重新汇集,并在应用程序中继续进行。

警告:当前 QUIC 在一个专门的首部流 (3) 中,通过 HTTP/2 HPACK 首部压缩压缩 HTTP 首部,则只有首部帧会出现队首阻塞问题。

认证和加密的首部和载荷

TCP 首部在网络中以明文出现,它没有经过认证,这导致了大量的 TCP 注入和首部管理问题,比如接收窗口管理和序列号覆写。尽管这些问题中的一些是主动攻击,有时其它则是一些网络中的中间盒子用来尝试透明地提升 TCP 性能的机制。然而,甚至 “性能增强” 中间设备依然有效地限制着传输协议的发展,这已经在 MPTCP 的设计及其后续的部署问题中观察到。

QUIC 数据包总是经过认证的,而且典型情况下载荷是全加密的。数据包头部不加密的部分依然会被接收者认证,以阻止任何第三方的数据包注入或操纵。QUIC 保护连接的端到端通信免遭智能或不知情的中间设备操纵。

警告:复位连接的 PUBLIC _RESET 包当前未经认证。

连接迁移

TCP 连接由源地址,源端口,目标地址和目标端口的4元组标识。TCP 一个广为人知的问题是,IP 地址改变(比如,由 WiFi 网络切换到移动网络)或端口号改变(当客户端的NAT绑定超时导致服务器看到的端口号改变)时连接会断掉。尽管 MPTCP 解决了 TCP 的连接迁移问题,但它依然为缺少中间设备和OS部署支持所困扰。

QUIC连接由一个 64-bit 连接 ID 标识,它由客户端随机地产生。在IP地址改变和 NAT 重绑定时,QUIC 连接可以继续存活,因为连接 ID 在这些迁移过程中保持不变。由于迁移客户端继续使用相同的会话密钥来加密和解密数据包,QUIC还提供了迁移客户端的自动加密验证。

当连接明确地用4元组标识时,比如服务器使用短暂的端口给客户端发送数据包时,有一个选项可用来不发送连接 ID 以节省线上传输的字节。

包类型和格式

QUIC 具有特殊包和普通包。有两种类型特殊包:版本协商包 (Version Negotiation Packets) 和 公共复位包 (Public Reset Packets),普通包包含帧。

所有 QUIC 包的大小应该适配在路径的 MTU 以避免IP分片。路径 MTU 发现是正在进行中的工作,而当前 QUIC 实现为 IPv6 使用 1350 字节的最大QUIC包大小,IPv4 使用1370字节。两个大小都没有 IP 和 UDP 过载。

QUIC公共包头

传输的所有 QUIC 包以大小介于1至51字节的公共包头开始。公共包头的格式如下:

--- src
     0        1        2        3        4            8
+--------+--------+--------+--------+--------+---    ---+
| Public |    Connection ID (64)    ...                 | ->
|Flags(8)|      (optional)                              |
+--------+--------+--------+--------+--------+---    ---+
     9       10       11        12   
+--------+--------+--------+--------+
|      QUIC Version (32)            | ->
|         (optional)                |                           
+--------+--------+--------+--------+
    13       14       15        16      17       18       19       20
+--------+--------+--------+--------+--------+--------+--------+--------+
|                        Diversification Nonce                          | ->
|                              (optional)                               |
+--------+--------+--------+--------+--------+--------+--------+--------+
    21       22       23        24      25       26       27       28
+--------+--------+--------+--------+--------+--------+--------+--------+
|                   Diversification Nonce Continued                     | ->
|                              (optional)                               |
+--------+--------+--------+--------+--------+--------+--------+--------+
    29       30       31        32      33       34       35       36
+--------+--------+--------+--------+--------+--------+--------+--------+
|                   Diversification Nonce Continued                     | ->
|                              (optional)                               |
+--------+--------+--------+--------+--------+--------+--------+--------+
    37       38       39        40      41       42       43       44
+--------+--------+--------+--------+--------+--------+--------+--------+
|                   Diversification Nonce Continued                     | ->
|                              (optional)                               |
+--------+--------+--------+--------+--------+--------+--------+--------+
    45      46       47        48       49       50
+--------+--------+--------+--------+--------+--------+
|           Packet Number (8, 16, 32, or 48)          |
|                  (variable length)                  |
+--------+--------+--------+--------+--------+--------+
---

载荷可以包含多个如下所述类型相关的头部字节。

公共头部中的字段如下:

公共标记处理流程图如下:

--- src
Check the public flags in public header
                 |
                 |
                 V
           +--------------+
           | Public Reset |    YES
           | flag set?    |---------------> Public Reset Packet
           +--------------+
                 |
                 | NO
                 V
           +------------+          +-------------+
           | Version    |   YES    | Packet sent |  YES
           | flag set?  |--------->| by server?  |--------> Version Negotiation
           +------------+          +-------------+               Packet
                 |                        |
                 | NO                     | NO
                 V                        V
           Regular Packet         Regular Packet with
                               QUIC Version present in header
---

特殊包

版本协商包

只有服务器会发送版本协商包。版本协商包以8位的公共标记和64位的连接ID开始。公共标记必须设置PUBLIC_FLAG_VERSION,并指明64位的连接ID。版本协商包的其余部分是服务器支持的版本的4字节列表:

--- src
     0        1        2        3        4        5        6        7       8
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| Public |    Connection ID (64)                                                 | ->
|Flags(8)|                                                                       |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
     9       10       11        12       13      14       15       16       17
+--------+--------+--------+--------+--------+--------+--------+--------+---...--+
|      1st QUIC version supported   |     2nd QUIC version supported    |   ...
|      by server (32)               |     by server (32)                |             
+--------+--------+--------+--------+--------+--------+--------+--------+---...--+
---

公共复位包

公共复位包以8位的公共标记和64位的连接ID开始。公共标记必须设置 PUBLIC_FLAG_RESET,并表明64位的连接ID。公共复位包的其余部分像标记 PRST 的加密握手消息那样编码(参考[QUIC-CRYPTO]):

--- src
     0        1        2        3        4         8
+--------+--------+--------+--------+--------+--   --+
| Public |    Connection ID (64)                ...  | ->
|Flags(8)|                                           |
+--------+--------+--------+--------+--------+--   --+
     9       10       11        12       13      14       
+--------+--------+--------+--------+--------+--------+---
|      Quic Tag (32)                |  Tag value map      ... ->
|         (PRST)                    |  (variable length)                         
+--------+--------+--------+--------+--------+--------+---
---

标记值映射:标记值映射包含如下的标记值:

(TODO:公共复位包应该包含认证的(目标)服务器 IP/端口。)

普通包

普通包已经过认证和加密。公共头部已认证但未加密,从第一帧开始的包的其余部分已加密。紧随公共头部之后,普通包包含 AEAD(authenticated encryption and associated data)数据。要解释内容,这些数据必须先解密。解密之后,明文由一系列帧组成。

(TODO: 文档化加密和解密的输入,并描述试用解密)

帧包

帧包具有一个载荷,它是一系列的类型前缀帧。帧类型的格式将在本文档的后面定义,但帧包的通用格式如下:

--- src
+--------+---...---+--------+---...---+
| Type   | Payload | Type   | Payload |
+--------+---...---+--------+---...---+
---

QUIC 连接的生命周期

连接建立

QUIC 客户端是初始化连接的端点。QUIC 的连接建立将版本协商与加密和传输握手交织在一起以减少连接建立延迟。我们下面先描述版本协商。

最初由客户端发向服务器的每个包必须设置版本标记,而且必须指定使用的协议版本。客户端发送的每个包必须开启版本标记,直到它从服务器收到了版本标记关闭的包。在服务器从客户端收到了第一个版本标记关闭的包后,它必须忽略(可能由于延迟)任何版本标记打开的包。

当服务器收到一个含有新连接连接ID的包时,它将对比客户端的版本和它支持的版本。如果客户端的版本对于服务器来说可以接受,服务器将在连接的整个生命周期中使用这个协议版本。在这种情况下,服务器发送的所有包的版本标记都是关闭的。

如果服务器不接受客户端的版本,则导致 1-RTT 的延迟。服务器将给客户端发送一个版本协商包。该包将设置版本标记,并包含服务器支持版本的集合。

当客户端从服务器收到一个版本协商包时,它将选择一个可接受的协议版本并使用该版本重发所有包。这些包必须继续设置版本标记,且必须包含新协商的协议版本。最后,客户端从服务器收到首个普通包(比如,一个非版本协商包)表明版本协商的结束,此后客户端发送的所有后续包版本标记关闭。

为了避免降级攻击,客户端在首包中指定的协议版本,以及服务器支持的版本集合必须包含在加密的握手数据中。客户端需要验证握手中的服务器版本列表与版本协商包中的版本列表匹配。服务器需要验证握手中的客户端版本表示一个它实际上不支持的协议版本。

连接建立的其余部分在握手文档中描述 [QUIC-CRYPTO]。加密握手在专门的加密流(流 ID 1)中执行。

在连接握手期间,握手必须协商多个传输参数。当前已定义的传输参数在本文的后面描述。

数据传输

QUIC 实现了连接可靠性,拥塞控制,和流控。QUIC 流控紧随 HTTP/2 的流控之后。QUIC 可靠性和拥塞控制在一份附带文档中描述。QUIC 连接使用一个单独的包序列号空间,以此跨连接共享拥塞控制和丢失恢复。

QUIC 连接中传输的所有数据,包括加密握手,被作为流内数据发送,但
ACKs 确认 QUIC 包。

这个部分概念性地描述 QUIC 连接内数据传输中流的使用。本节提到的各种帧将在 帧类型和格式 一节中介绍。

QUIC流的生命

流是被分割为流帧的双向数据的独立序列。流可以由客户端或服务器创建,可以与其它流并发交错地发送数据,且可以取消。QUIC 流的生命周期模型紧随 HTTP/2 [RFC 7540] 的之后。(在文件的后面更详细地描述了HTTP / 2的QUIC流的使用。)

流创建通过为给定的流发送一个 STREAM 帧显式地完成。为了避免流 ID 冲突,如果流由服务器初始化,则流 ID 必须是偶数,而如果由客户端初始化则必须为奇数。0 不是一个有效的流 ID。流 1 为加密握手保留,它应该是第一个客户端初始化的流。当基于 QUIC 使用 HTTP/2 时,流 3 为传输所有其它流的压缩首部而保留,以确保首部的处理和传送可靠且有序。

随着新流的创建,连接每一端的 流 IDs 必须单调地递增。比如,流 2 可能在 流 3 之后创建,但流 7 一定不能在流 9 之后创建。对端可以接收乱序的流。比如,在服务器收到包含 流 7 的帧的 包 9 之前,如果它收到了包 10,其中包含 流 9 的帧,它应该优雅地处理这种情况。

如果端点收到一个它不想接受的流的STREAM帧,它可以立即响应一个RST_STREAM 帧(稍后描述)。注意,然而,初始化流的端点可能也已经在那个流上发送了数据;这些数据必须被忽略。

流一旦创建完成,它可被用于发送和接收数据。这意味着 QUIC 端点可在那个流上发送一系列流帧,直到流在那个方向上终止。

每个 QUIC 端点都可以正常地终止流。有三种方式可以终止流:

  1. 正常终止: 由于流是双向的,流可以是 "half-closed(半关闭)" 或 "closed(关闭)" 状态。当流的一边发送了 FIN 位被设为ture的帧时,在那个方向上流被认为是 "half-closed(半关闭)" 的。FIN 表明 FIN 的发送者将不会在这个流上发送更多数据了。当 QUIC 的两端都已经发送并接收了 FIN,则端点认为流是 "closed(关闭)" 状态的。尽管 FIN 应该随最后的流用户数据一起发送,但 FIN 位可以在最后的流数据后面的空流帧中发送。

  2. 异常终止: 客户端或服务器可以在任何时候为一个流发送RST_STREAM 帧。RST_STREAM 帧包含一个错误码用以指示失败原因(本文档的后面部分会列出错误码)。当流的发起者发送了一个RST_STREAM 帧,它表示无法完成流,而且不会在流上发送更多数据。当RST_STREAM 帧是由流的接收者发送时,发送者,一旦接收,应该停止在流上发送任何数据。流接收者应该意识到发送者已经传输的数据和RST_STREAM 帧接收的时间之间存在着竞态。为了确保连接级流控被正确的认定,即使收到 RST_STREAM 帧,发送者依然需要确保两者之一:对端收到流的 FIN 和所有字节,或者对端收到一个 RST_STREAM 帧。这也意味着 RST_STREAM 帧的发送者需要使用适当的 WINDOW_UPDATE 来继续响应此流上的传入 STREAM_FREAMEs ,以确保发送方未尝试传递 FIN 来阻塞流量控制。

  3. 如在下一节中描述的那样,当连接终止时流也会终止。

连接终止

连接应该保持打开状态,直到他们在预协商周期的时间后变为空闲。当服务器决定终止空闲连接时,它不应该通知客户端来避免唤醒移动设备的无线电模块。QUIC 连接,一旦建立,可由两种方式之一终止:

  1. 显式关闭: 一端发送 CONNECTION_CLOSE 帧给对端来初始化连接终止。一端可在 CONNECTION_CLOSE 之前发送 GOAWAY 帧给对端表明连接将在不久后终止。当发送 GOAWAY 帧时,通知对端任何活跃的流将继续处理,但 GOAWAY 的发送者将不再初始化任何额外的流,且不接受任何新进入的流。当活跃的流终止时,可以发送 CONNECTION_CLOSE。如果一端在未终止的流活跃时发送了 CONNECTION_CLOSE 帧(一个或多个流还没有发送或接收 FIN 位或 RST_STREAM 帧),则对端必须假设流是不完整的且被异常终止。

  2. 隐式关闭: QUIC连接的默认空闲超时是30秒,且是连接协商中的必要参数("ICSL")。最大值是10分钟。如果在空闲超时期间没有网络活动,连接被关闭。默认情况下将发送一个 CONNECTION_CLSOE 帧。当发送显式的关闭比较昂贵时可以启用静默关闭选项,比如必须唤醒无线电模块的移动网络。

一端还可以在连接期间的任何时候发送 PUBLIC_RESET 包来突然地终止活跃的连接。PUBLIC_RESET 是 TCP RST 的 QUIC等价物。

帧类型和格式

QUIC帧数据包由帧填充。它具有帧类型字节,其本身具有类型相关的解释,后跟类型相关的帧首部字段。所有帧都包含在单个 QUIC 包中,且没有帧可以跨越 QUIC 数据包边界。

帧类型

帧类型字节有两种解释,产生两种帧类型:特殊帧类型,和常规帧类型。特殊帧类型在帧类型字节中编码帧类型和对应的标志,而常规帧类型简单地使用帧类型字节。

当前定义的特殊帧类型如下:

--- src
   +------------------+-----------------------------+
   | Type-field value |     Control Frame-type      |
   +------------------+-----------------------------+
   |     1fdooossB    |  STREAM                     |
   |     01ntllmmB    |  ACK                        |
   |     001xxxxxB    |  CONGESTION_FEEDBACK        |
   +------------------+-----------------------------+
---

当前定义的常规帧类型如下:

--- src
   +------------------+-----------------------------+
   | Type-field value |     Control Frame-type      |
   +------------------+-----------------------------+
   | 00000000B (0x00) |  PADDING                    |
   | 00000001B (0x01) |  RST_STREAM                 |
   | 00000010B (0x02) |  CONNECTION_CLOSE           |
   | 00000011B (0x03) |  GOAWAY                     |
   | 00000100B (0x04) |  WINDOW_UPDATE              |
   | 00000101B (0x05) |  BLOCKED                    |
   | 00000110B (0x06) |  STOP_WAITING               |
   | 00000111B (0x07) |  PING                       |
   +------------------+-----------------------------+
---

STREAM帧

STREAM 帧同时被用于隐式地创建流和在流上发送数据,其格式如下:

--- src
     0        1       …               SLEN
+--------+--------+--------+--------+--------+
|Type (8)| Stream ID (8, 16, 24, or 32 bits) |
|        |    (Variable length SLEN bytes)   |
+--------+--------+--------+--------+--------+
  SLEN+1  SLEN+2     …                                         SLEN+OLEN   
+--------+--------+--------+--------+--------+--------+--------+--------+
|   Offset (0, 16, 24, 32, 40, 48, 56, or 64 bits) (variable length)    |
|                    (Variable length: OLEN  bytes)                     |
+--------+--------+--------+--------+--------+--------+--------+--------+
  SLEN+OLEN+1   SLEN+OLEN+2
+-------------+-------------+
| Data length (0 or 16 bits)|
|  Optional(maybe 0 bytes)  |
+------------+--------------+
---

STREAM 帧首部中的字段如下:

流帧必须总是具有非零数据长度,或设置 FIN 位。

ACK帧

发送 ACK 帧以通知对端哪些包已经收到,以及接收者仍然认为哪些包丢失了(丢失包的内容可能需要被重发)。ACK 帧包含 1 到 256 个 ack 块。Ack 块是确认的包的范围,与 TCP 的 SACK 块类似,但 QUIC 没有 TCP 的累积ack 点的等价物,因为包将以新的序列号重传。

要限制 ACK 块为还没有被对端接收的,对端周期性地发送 STOP_WAITING 帧,来通知接收者停止确认小于特定序列号的包,在接收端提高 "最小未确认" 包号。ACK 帧的发送者以此只报告接收到的最小未确认和报告的最大已发现包号之间的 ACK 块。建议发送者在 ack 中发送它接收到的最近最大确认包,作为 stop waiting 帧的最小未确认值。

不像TCP SACK,QUIC ACK 块是不可撤销的,因此一旦一个包被确认,即使它没有出现在未来的ack帧中,它也被假设已经确认。

作为 QUIC 已废弃的熵的替代,发送者可以有意地跳过包号来为连接引入熵。如果一个包号未发送就被确认,则发送者必须总是关闭连接,因此这种机制会自动防御任何潜在的攻击者。Ack 的格式在表达丢失包的块上是比较有效的,因此这对于接收者和发送者成本比较低,且可以根据需要有效地提供至多 8 位熵,而不是通过恒定的开销来实现 8 位的熵。8位是 Ack 格式可以有效表达的 Ack 范围之间最长的间隔。

段偏移

0:Ack 帧的起始位置。
T:时间戳段起始位置的字节偏移量。
A:Ack 块段起始位置的字节偏移量。
N:最大已确认包的字节长度。

--- src
     0                            1  => N                     N+1 => A(aka N + 3)
+---------+-------------------------------------------------+--------+--------+
|   Type  |                   Largest Acked                 |  Largest Acked  |
|   (8)   |    (8, 16, 32, or 48 bits, determined by ll)    | Delta Time (16) |
|01nullmm |                                                 |                 |
+---------+-------------------------------------------------+--------+--------+
     A             A + 1  ==>  A + N
+--------+----------------------------------------+              
| Number |             First Ack                  |
|Blocks-1|           Block Length                 |
| (opt)  |(8, 16, 32 or 48 bits, determined by mm)|
+--------+----------------------------------------+
  A + N + 1                A + N + 2  ==>  T(aka A + 2N + 1)
+------------+-------------------------------------------------+
| Gap to next|              Ack Block Length                   |
| Block (8)  |   (8, 16, 32, or 48 bits, determined by mm)     |
| (Repeats)  |       (repeats Number Ranges times)             |
+------------+-------------------------------------------------+
     T        T+1             T+2                 (Repeated Num Timestamps)
+----------+--------+---------------------+ ...  --------+------------------+  
|   Num    | Delta  |     Time Since      |     | Delta  |       Time       |
|Timestamps|Largest |    Largest Acked    |     |Largest |  Since Previous  |
|   (8)    | Acked  |      (32 bits)      |     | Acked  |Timestamp(16 bits)|
+----------+--------+---------------------+     +--------+------------------+
---

ACK 帧中的字段如下:

STOP_WAITING 帧

STOP_WAITING 帧用于通知对端,它不应该继续等待包号小于特定值的包。包号以1,2,4或6字节编码,using the same coding length as is specified for the packet number for the enclosing packet's header (specified in the QUIC Frame Packet's Public Flags field.) 这个帧如下:

--- src
     0        1        2        3         4       5       6  
+--------+--------+--------+--------+--------+-------+-------+
|Type (8)|   Least unacked delta (8, 16, 32, or 48 bits)     |
|        |                       (variable length)           |
+--------+--------+--------+--------+--------+--------+------+
---

STOP_WAITING帧中的字段如下:

WINDOW_UPDATE 帧

WINDOW_UPDATE 帧用于通知对端一个端点的流量控制接收窗口的增长。流ID可以是0,表示这个WINDOW_UPDATE应用于连接级的流量控制窗口,或者 > 0 表示指定的流应该增长它的流量控制窗口。帧如下:

指定一个完全的字节偏移量,WINDOW_UPDATE帧的接收者可以只在那个流上至多发送那个字节数。发送更多字节而违背流量控制将导致接收端关闭连接。

为特定流ID收到多个WINDOW_UPDATE帧时,只需要追踪最大的字节偏移即可。

流和会话窗口都以一个默认值16KB开始,但是这个值典型地在握手期间增长。为了做到这一点,端点应该在握手中协商 SFCW (Stream Flow Control Window) 和 CFCW (Connection/Session Flow Control Window) 参数。与每个标记关联的值应该分别是初始流窗口和初始连接窗口的字节数。

帧如下:

--- src
    0         1                 4        5                 12
+--------+--------+-- ... --+-------+--------+-- ... --+-------+
|Type(8) |    Stream ID (32 bits)   |  Byte offset (64 bits)   | 
+--------+--------+-- ... --+-------+--------+-- ... --+-------+
---

WINDOW_UPDATE帧中的字段如下:

BLOCKED 帧

BLOCKED帧用于向远端指明本端点已经准备好发送数据了(且有数据要发送),但是当前被流量控制阻塞了。这是一个纯粹的信息帧,它对于调试极其有用。BLOCKED帧的接收者应该简单的丢弃它(可能在打印了一条有帮助的log消息之后)。帧如下:

--- src
     0        1        2        3         4
+--------+--------+--------+--------+--------+
|Type(8) |          Stream ID (32 bits)      |  
+--------+--------+--------+--------+--------+
---

BLOCKED帧中的字段如下:

CONGESTION_FEEDBACK 帧

The CONGESTION_FEEDBACK frame is an experimental frame currently not used. It is intended to provide extra congestion feedback information outside the scope of the standard ack frame. A CONGESTION_FEEDBACK frame must have the first three bits of the Frame Type set to 001. The last 5 bits of the Frame Type field are reserved for future use.

PADDING 帧

The PADDING frame pads a packet with 0x00 bytes. When this frame is encountered, the rest of the packet is expected to be padding bytes. The frame contains 0x00 bytes and extends to the end of the QUIC packet. A PADDING frame only has a Frame Type field, and must have the 8-bit Frame Type field set to 0x00.

RST_STREAM 帧

The RST_STREAM frame allows for abnormal termination of a stream. When sent by the creator of a stream, it indicates the creator wishes to cancel the stream. When sent by the receiver of a stream, it indicates an error or that the receiver did not want to accept the stream, so the stream should be closed. The frame is as follows:

--- src
     0        1            4      5              12     8             16
+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+
|Type(8)| StreamID (32 bits) | Byte offset (64 bits)| Error code (32 bits)|
+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+
---

The fields in a RST_STREAM frame are as follows:

PING 帧

The PING frame can be used by an endpoint to verify that a peer is still alive. The PING frame contains no payload. The receiver of a PING frame simply needs to ACK the packet containing this frame. The PING frame should be used to keep a connection alive when a stream is open. The default is to do this after 15 seconds of quiescence, which is much shorter than most NATs time out. A PING frame only has a Frame Type field, and must have the 8-bit Frame Type field set to 0x07.**

CONNECTION_CLOSE 帧

The CONNECTION_CLOSE frame allows for notification that the connection is being closed. If there are streams in flight, those streams are all implicitly closed when the connection is closed. (Ideally, a GOAWAY frame would be sent with enough time that all streams are torn down.) The frame is as follows:**

--- src
     0        1             4        5        6       7       
+--------+--------+-- ... -----+--------+--------+--------+----- ...
|Type(8) | Error code (32 bits)| Reason phrase   |  Reason phrase  
|        |                     | length (16 bits)|(variable length)
+--------+--------+-- ... -----+--------+--------+--------+----- ...
---

The fields of a CONNECTION_CLOSE frame are as follows:

GOAWAY 帧

The GOAWAY frame allows for notification that the connection should stop being used, and will likely be aborted in the future. Any active streams will continue to be processed, but the sender of the GOAWAY will not initiate any additional streams, and will not accept any new streams. The frame is as follows:**

--- src
     0        1             4      5       6       7      8
+--------+--------+-- ... -----+-------+-------+-------+------+
|Type(8) | Error code (32 bits)| Last Good Stream ID (32 bits)| ->
+--------+--------+-- ... -----+-------+-------+-------+------+
      9        10       11  
+--------+--------+--------+----- ... 
| Reason phrase   |  Reason phrase
| length (16 bits)|(variable length)
+--------+--------+--------+----- ...
---

The fields of a GOAWAY frame are as follows:

QUIC传输参数

The handshake is responsible for negotiating a variety of transport parameters for a QUIC connection.

必需参数

可选参数

QuicErrorCodes

优先级

基于 QUIC 的 HTTP/2 层

流管理

HTTP/2 首部压缩

解析 HTTP/2 首部

HTTP 中的 QUIC 协商

握手协议要求

0-RTT 的连接建立

源地址欺骗防护

不透明的源地址令牌

传输参数协商

证书压缩

服务器配置更新

最新版本更新

贡献者

致谢

打赏

原文

上一篇下一篇

猜你喜欢

热点阅读