欢迎访问我的个人网站获取更好的阅读排版体验: [译] QUIC Wire Layout Specification - Frame Types and Formats | QUIC协议标准中文翻译(4) 帧类型和格式 | yoko blog (https://pengrl.com/p/47156/)
目录
- Frame Types | 帧类型
- STREAM Frame | 流类型帧
- ACK Frame | ACK帧
- STOP_WAITING Frame | 停止等待帧
- WINDOW_UPDATE Frame | 窗口更新帧
- BLOCKED Frame | 阻塞信息帧
- CONGESTION_FEEDBACK Frame | 拥塞反馈帧
- PADDING Frame | 填充帧
- RST_STREAM Frame | 流重置帧
- PING frame | PING帧
- CONNECTION_CLOSE frame | 连接关闭帧
Frame Types and Formats | 帧类型和格式
QUIC的帧类型包由一个或多个帧组成。帧有一个字节大小的帧类型,有自己的类型解释,后面跟着该帧类型的字段。一个或多个帧只能包含在一个QUIC包中,没有帧可以跨越多个QUIC包。
Frame Types | 帧类型
一共有两种帧类型:特殊帧类型和常规帧类型,它们的解析是不一样的。特殊帧类型在帧类型的字节中同时编码了帧类型和相应的标志位,而常规帧的帧类型字节只用来标识类型。
当前定义的特殊帧类型有:
--- 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 Frame | 流类型帧
流类型帧用于隐式创建流和在流上发送数据,格式为:
--- 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) |
+------------+--------------+
---
流类型帧的头部字段含义如下:
- 帧类型: 8bit,包含可变的标志(1fdooossB):
- 最左边的bit必须设置为1标志这是一个流类型帧。
- 'f' bit是Fin bit。如果设置为1,标识发送端完成了这条流上的发送并且希望进入半关闭状态(后续再讨论细节)。
- 'd' bit标识当前流的头中是否包含数据长度。如果设置为0,标识这个流类型帧的大小为包的大小。
- 接下来的'ooo' 3个bit编码了头中偏移的长度,比如0,16,24,32,40,48,56,64bit长。
- 接下来的'ss'两个bit编码了头中流ID的长度,比如8,16,24,32bit长。
- 流ID: 一个可变长度的无符号整型用于唯一标识这条流
- 偏移: 一个可变长度的无符号整型标识这块数据在流上的偏移
- 数据长度: 可选的16bit无符号整型标识这个类型帧的数据长度。当这个包的长度是一个完全大小时可以省略这个字段,用于避免填充的风险。
流类型帧必须是非0大小的数据长度或者设置了Fin标志。
ACK Frame | ACK帧
发送ACK帧用于通知对端哪些包已经被接收,换句话说,通知对端哪些包接收端没有收到(丢失包的内容可能需要重新发送)。ACK帧包含了1到256个ack块。ack块是已确认的包的区间,类似于TCP的SACK的块,但是不同于TCP的不可撤销的ack,因为QUIC重传时使用了新的包序号。
为了限制对于那些对端还没有接收到的ACK的块,数据发送端周期性发送STOP_WAITING
帧来通知接收端停止ack小于一个指定序号的包。使得接收端产生一个"最小还没ack"的包序号。于是ACK帧的发送者值需要报告那些最小还没ack与收到的最大序号之间的ACK块。建议ACK帧的发送者在ACK帧中发送最近最大已ack的包,就像STOP_WAITING
帧的最小还没ack一样。
不同于TCP的SACK,QUIC的ACK块是不可撤销的,如果一个包被ack了,那么即使它不再出现在之后的ACK帧中,依然认为它已经被ack了。
作为QUIC已弃用的熵信息的替代方案,发送端可以有意的跳过包序号来将熵信息注入连接中。发送端如果收到一个没有发送过的包序号对应的ack,应该关闭这个连接,这个机制可以自动预防任何潜在的攻击者。ack用于表示丢失包的块的格式是高效的。
各区域偏移
0: ack帧的开始 T: 时间区域的偏移 A: ack块区域的偏移 N: 最大ack的长度
--- 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帧中的字段含义如下:
- 帧类型: 8bit,包含了可变内容的标志(01nullmmB)。
- 前面两个bit必须设置为01来标识这是一个ACK帧。
- 'n'标识这个帧是否包含一个以上ack区间
- 'u'没有被使用
- 'll'这2bit编码了
Largest Observed
字段的长度,可以是1,2,4,6字节 - 'mm'这2bit编码了
Missing Packet Sequence Number Delta
字段的长度,可以是1,2,4,6字节
- 最大已确认包序号: 一个可变长度的无符号数标识对端收到的最大包序号
- 最大已确认差值时间: 一个16bit无符号浮点数,协议中的低11bit用于标识尾数,高5bit用于标识指数。该字段用于标识该ack帧的发送时间减去最大已确认包的接收时间,单位微妙。格式的定义近似IEEE754。举例,0x1表示1微妙,指数为0,在高5bit中表示,尾数为1,在低11bit中表示。当协议中的指数部分大于0,尾数隐含的第12bit默认为1。举例,协议中值为0x800的浮点数有一个显式的指数为1,有一个隐式的尾数为0,由于协议中的指数大于1,所以我们认为有一个有效的尾数值4096(尾数隐含的第12位被认为是1)。此外,真正的指数比协议中的指数小1,所以这个值标识4096微秒。任何大于可表示范围的值都被限定为0xFFFF。
- Ack块区域:
- 块数量: 一个可选的8bit无符号值,等于ack块的数量减一。只有在'n'标志设置为1时存在。
- 块大小: 一个可变的包序号差值。对于第一个丢包区间,ack块从最大已ack的序号开始。对于非第一个块,0标识超过256个包丢失了。
- 下一个块的间隔: 一个8bit无符号值标识ack块之间包的数量。
- 时间戳区域:
- 时间戳数量: 一个8bit无符号值标识了ACK帧中有多个时间戳。后面会有多个包序号,时间戳的对
- 与最大已观察到的差值: 8bit无符号值,标识第一个时间戳和最大已观察到的差值。所以,包号等于最大已观察到的包号减去这个差值。
- 第一个时间戳: 32位无符号值,标识以微妙为单位的时间差值,等于最大已观察到的包的接收时间减去最大已观察到的差值。
- 与最大已观察到的差值(重复的): (和上面描述的一样。)
- 与前一个时间戳的差值(重复的): 16bit无符号值,标识与前一个时间戳的差值。编码方式和Ack延时时间相同。
STOP_WAITING Frame | 停止等待帧
发送停止等待帧来通知对端不再等待小于指定包序号的包。包序号可以被编码为1,2,4,6字节。编码方式和QUIC帧类型包的公共标志中包序号字段一样。帧格式如下:
--- src
0 1 2 3 4 5 6
+--------+--------+--------+--------+--------+-------+-------+
|Type (8)| Least unacked delta (8, 16, 32, or 48 bits) |
| | (variable length) |
+--------+--------+--------+--------+--------+--------+------+
---
停止等待帧的字段含义如下:
- 帧类型: 8bit,必须设置成0x06。
- 最小还没确认序号差值: 一个可变长度的包序号和使用同样长度的包头序号的差值。用包序号减去这个值得到最小还没ack的包序号。最小还没ack的包序号是发送端还在等待ack的所有包序号中最小的序号。如果接收端丢失了任何比这个序号还要小的包,那么接收端应该认为这些包永久丢失了。
WINDOW_UPDATE Frame | 窗口更新帧
窗口更新帧用来告知对端本端的一次流量控制接收窗口的增长。流ID为0时标识这个窗口更新帧作用于连接层面的流量控制窗口,> 0
标识一个指定的流需要增长它的流量控制窗口。这个帧如下定义:
指定一个绝对的字节偏移,窗口更新帧的接收端可能在指定的流上最多发送指定的字节。违反流量控制发送更多的数据会导致数据接收端关闭这个连接。
如果一个流上收到了多个窗口更新帧,只需要关注最大的字节偏移。
流和连接的窗口开始的默认值是16KB,但是一般会在握手阶段增长。为了达到这个效果,一端需要在握手阶段协商SFCW(流上的流量控制窗口)和CFCW(连接/会话上的流量控制窗口)参数。流层面和连接层面的初始窗口大小需分开指定。
帧格式如下:
--- src
0 1 4 5 12
+--------+--------+-- ... --+-------+--------+-- ... --+-------+
|Type(8) | Stream ID (32 bits) | Byte offset (64 bits) |
+--------+--------+-- ... --+-------+--------+-- ... --+-------+
---
窗口更新帧的字段含义如下:
- 帧类型: 8bit,必须设置为0x04
- 流ID: 需要更新哪个流的流量控制窗口,设置为0则更新连接层面的流量控制窗口。
- 字节偏移: 64bit无符号整型,标识指定流上可以被发送绝对字节偏移的数据。如果是连接层面的流量控制,那么是连接上当前所有打开的流的总和。
BLOCKED Frame | 阻塞信息帧
阻塞信息帧用来通知对端本端已经准备好发送数据(并且有数据需要发送),但是当前被流量控制所阻塞。这是一个对调试极其有用的纯信息帧。阻塞信息帧的接收者应该简单的把这个帧丢弃掉(比如再打印一条有用的日志信息之后)。帧格式如下:
--- src
0 1 2 3 4
+--------+--------+--------+--------+--------+
|Type(8) | Stream ID (32 bits) |
+--------+--------+--------+--------+--------+
---
阻塞信息帧中的字段含义如下:
- 帧类型: 8bit,必须设置为0x05标志这是一个阻塞信息帧。
- 流ID: 32bit无符号数表示哪个流被流量控制阻塞了。非0的流ID标识特定的流。流ID为0标识连接在连接层面被流量控制阻塞了。
CONGESTION_FEEDBACK Frame | 拥塞反馈帧
拥塞反馈帧时一个实验性质的帧当前没有被使用。目的是在标准的ack帧范围之外提供额外的拥塞反馈信息。拥塞反馈帧的帧类型的头3bit必须设置为001,后5bit保留用于未来使用。
PADDING Frame | 填充帧
填充帧在包中填充一个0x00字节。当遇到了填充帧,该包的剩余部分都是填充帧。填充帧包含0x00的字节,并且填充在QUIC包的末尾。填充帧只有一个8bit大小设置为0x00的帧类型的字段。
RST_STREAM Frame | 流重置帧
流重置帧用于异常关闭一个流。当由流创建者发送时,标识了创建者希望取消这个流。当由流接收端发送,标识了一个错误或者接收端不希望接受这个流,所以这个流应该被关闭。这个帧的格式如下:
--- src
0 1 4 5 12 8 16
+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+
|Type(8)| StreamID (32 bits) | Byte offset (64 bits)| Error code (32 bits)|
+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+
---
流重置帧的字段含义如下:
- 帧类型: 8bit,必须设置为0x01。
- 流ID: 32bit,被关闭的流ID。
- 字节偏移: 64bit无符号整型标识这条流上结束数据的绝对字节偏移。
- 错误码: 32bit错误码标识流为什么被关闭。错误码的定义在文档后续部分。
PING frame | PING帧
PING帧用于验证对端是否还存活。PING没有负载信息。PING帧的接收端需要返回这个包的确认包。当流被打开时,PING帧需要用来保证连接活跃。默认静止15秒后发送,这比大部分NAT超时的时间要短得多。PING帧只有帧类型字段,8bit的帧类型必须设置为0x07。
CONNECTION_CLOSE frame | 连接关闭帧
连接关闭帧用于通知连接被关闭。如果还有流在传输,所有这些流都被隐式关闭(理论上,一个GOAWAY帧可以被发送以留有足够时间用于关闭所有流)。帧格式如下:
--- src
0 1 4 5 6 7
+--------+--------+-- ... -----+--------+--------+--------+----- ...
|Type(8) | Error code (32 bits)| Reason phrase | Reason phrase
| | | length (16 bits)|(variable length)
+--------+--------+-- ... -----+--------+--------+--------+----- ...
---
连接关闭帧的字段含义如下:
帧类型: 8bit,必须设置为0x02 错误码: 32bit,标识关闭链接原因的错误码 描述信息长度: 16bit无符号数,标识描述信息的长度。可以为0如果发送端认为在错误码之上不需要给出更详细的信息 描述信息: 可选字段,可读字符串用于解释连接关闭的原因
英文原文链接
QUIC Wire Layout Specification - Google 文档
本文作者: yoko 本文链接: http://www.pengrl.com/p/47156/ 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!