零。前言
客户端和服务器端一旦握手协商成功接建立连接,端点之间可以基于HTTP/2协议传递交换帧数据了。
一。帧通用格式
下图为HTTP/2帧通用格式:帧头+负载的比特位通用结构:+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
帧头为固定的9个字节((24+8+8+1+31)/8=9)呈现,变化的为帧的负载(payload),负载内容是由帧类型(Type)定义。
帧长度Length:无符号的自然数,24个比特表示,仅表示帧负载所占用字节数,不包括帧头所占用的9个字节。默认大小区间为为0~16,384(2^14),一旦超过默认最大值2^14(16384),发送方将不再允许发送,除非接收到接收方定义的SETTINGS_MAX_FRAME_SIZE(一般此值区间为2^14 ~ 2^24)值的通知。
帧类型Type:8个比特表示,定义了帧负载的具体格式和帧的语义,HTTP/2规范定义了10个帧类型,这里不包括实验类型帧和扩展类型帧
帧的标志位Flags:8个比特表示,服务于具体帧类型,默认值为0x0。有一个小技巧需要注意,一般来讲,8个比特可以容纳8个不同的标志,比如,PADDED值为0x8,二进制表示为00001000;END_HEADERS值为0x4,二进制表示为00000100;END_STREAM值为0X1,二进制为00000001。可以同时在一个字节中传达三种标志位,二进制表示为00001101,即0x13。因此,后面的帧结构中,标志位一般会使用8个比特表示,若某位不确定,使用问号?替代,表示此处可能会被设置标志位
帧保留比特为R:在HTTP/2语境下为保留的比特位,固定值为0X0
流标识符Stream Identifier:无符号的31比特表示无符号自然数。0x0值表示为帧仅作用于连接,不隶属于单独的流。
关于帧长度,需要稍加关注: - 0 ~ 2^14(16384)为默认约定长度,所有端点都需要遵守 - 2^14 (16,384) ~ 2^24-1(16,777,215)此区间数值,需要接收方设置SETTINGS_MAX_FRAME_SIZE参数单独赋值 - 一端接收到的帧长度超过设定上限或帧太小,需要发送FRAME_SIZE_ERR错误 - 当帧长错误会影响到整个连接状态时,须以连接错误对待之;比如HEADERS,PUSH_PROMISE,CONTINUATION,SETTINGS,以及帧标识符不该为0的帧等,都需要如此处理 - 任一端都没有义务必须使用完一个帧的所有可用空间 - 大帧可能会导致延迟,针对时间敏感的帧,比如RST_STREAM, WINDOW_UPDATE, PRIORITY,需要快速发送出去,以免延迟导致性能等问题
二。报文头压缩和解压
和HTTP/1一样,HTTP/2报头字段包含一个或多个相关的键值对。报头字段会在HTTP请求/响应报头和服务器推送操作中使用。原先为文本字段,现在需要使用HTTP报头压缩进行序列化成报头分块,作为HEADERS 、 PUSH_PROMISE、CONTINUATION等帧的负载传输出去。
接收端合并接收到的帧组装成报头分块,解压缩还原报头集合。
一个完整的报头分块包含: - 单个包含报头终止标记END_HEADERS的HEADERS、PUSH_PROMISE帧,或者 - HEADERS、PUSH_PROMISE帧不包含的END_HEADERS标记,后续跟随一个或多个CONTINUATION帧,最后一个CONTINUATION帧包含了END_HEADERS标记。
报头压缩是有状态的,在一个完整的连接中,一方的压缩上下文环境,另一方的解压的上下文环境,都是需要具备的。报头解码失败需要作为连接错误COMPRESSION_ERROR对待。
报头块彼此之间离散,作为连续的同一类型帧序列存在,不存在交错帧以及来自其他类型帧或流。举一个例子,一个连续的HEADERS/CONTINUATION/PUSH_PROMISE帧序列,最后一个帧包含了END_HEADERS标记,表示一个报头完结。一个报头块逻辑上是一个帧,但是否完整取决于同类型连续的帧的最后一个包含END_HEADERS标记。
报头块作为HEADERS/PUSH_PROMISE/CONTINUATION等帧负载被一端发向另一端。接收端需要从HEADERS/PUSH_PROMISE/CONTINUATION等帧负载中进行组装报头块,执行解压还原报头集合,不管帧需要不需要被丢弃。接收端在解压时若不能够正常解压报头块,需要回应COMPRESSION_ERROR错误,然后终止连接。
三。HTTP/2定义的帧
规范定义了10个正式使用到帧类型,扩展实验类型的ALTSVC、BLOCKED等不在介绍之列。下面按照优先使用顺序重新排排序。
1. SETTINGS+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| 0x4 (8) | 0000 000? (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier/0x0 (32) |
+=+=============================+===============================+
| Identifier (16) |
+-------------------------------+-------------------------------+
| Value (32) |
+---------------------------------------------------------------+
| Identifier (16) |
+-------------------------------+-------------------------------+
| Value (32) |
+---------------------------------------------------------------+
设置帧,接收者向发送者通告己方设定,服务器端在连接成功后必须第一个发送的帧。
字段Identifier定义了如下参数: - SETTINGS_HEADER_TABLE_S