HTTP/2 帧的基本结构
每个 HTTP/2 帧由两个部分组成:
固定 9 字节,包含以下字段:
- 长度(Length):24 位,表示帧负载的长度,不包括帧头本身。
- 类型(Type):8 位,表示帧的类型。
- 标志(Flags):8 位,表示帧的标志,用于指示特定的控制信息。
- 流标识符(Stream Identifier):31 位,标识该帧所属的流,最高位保留为 0。
帧负载(Payload)
长度可变,内容根据帧类型不同而不同。
HTTP/2 的帧类型
HTTP/2 定义了以下 10 种帧类型,每种帧类型都有唯一的类型编号(Type):
- DATA 帧(Type = 0x0)
- HEADERS 帧(Type = 0x1)
- PRIORITY 帧(Type = 0x2)
- RST_STREAM 帧(Type = 0x3)
- SETTINGS 帧(Type = 0x4)
- PUSH_PROMISE 帧(Type = 0x5)
- PING 帧(Type = 0x6)
- GOAWAY 帧(Type = 0x7)
- WINDOW_UPDATE 帧(Type = 0x8)
- CONTINUATION 帧(Type = 0x9)
以下是对每种帧类型的详细介绍:
1. DATA 帧(Type = 0x0)
功能
用于传输 HTTP 消息的主体数据(例如请求体或响应体)。
结构
-
帧头:
- 长度:可变
- 类型:0x0
- 标志:可能包含
END_STREAM
(0x1)、PADDED
(0x8)
- 流标识符:所属的流 ID
-
帧负载:
- 可选的 PADDED 字段(1 字节):表示填充字节的长度。
- 数据:实际的有效负载数据。
- 可选的填充字节:用于填充数据,长度由 PADDED 标志指定。
标志
END_STREAM
(0x1):指示这是该流的最后一个帧。
PADDED
(0x8):指示帧负载包含填充字节。
功能
用于传输 HTTP 请求或响应的头部信息。
结构
-
帧头:
- 长度:可变
- 类型:0x1
- 标志:可能包含
END_STREAM
(0x1)、END_HEADERS
(0x4)、PADDED
(0x8)、PRIORITY
(0x20)
- 流标识符:所属的流 ID
-
帧负载:
- 可选的 PADDED 字段(1 字节):表示填充字节的长度。
- 可选的 PRIORITY 字段(5 字节):包含依赖关系和权重。
- 依赖的流 ID(31 位):指定当前流依赖的父流。
- 权重(8 位):指定当前流的权重(1-256)。
- 头部块:使用 HPACK 压缩的头部字段。
- 可选的填充字节:用于填充数据,长度由 PADDED 标志指定。
标志
- END_STREAM (0x1):指示这是该流的最后一个帧。
- END_HEADERS (0x4):指示头部块已经完全传输。
- PADDED (0x8):指示帧负载包含填充字节。
- PRIORITY (0x20):指示帧负载包含优先级信息。
3. PRIORITY 帧(Type = 0x2)
功能
用于设置流的优先级和依赖关系。
结构
-
帧头:
- 长度:5 字节
- 类型:0x2
- 标志:无定义
- 流标识符:所属的流 ID
-
帧负载:
- 依赖的流 ID(31 位):指定当前流依赖的父流。
- 权重(8 位):指定当前流的权重(1-256)。
标志
4. RST_STREAM 帧(Type = 0x3)
功能
用于中止一个流,表明该流发生了错误或被取消。
结构
-
帧头:
- 长度:4 字节
- 类型:0x3
- 标志:无定义
- 流标识符:所属的流 ID
-
帧负载:
- 错误码(32 位):指示中止原因(例如
NO_ERROR
、CANCEL
、INTERNAL_ERROR
等)。
标志
5. SETTINGS 帧(Type = 0x4)
功能
用于配置和调整 HTTP/2 连接的参数,双方可以通过此帧来交换设置。
结构
-
帧头:
- 长度:可变(必须是 0 或 6 的倍数)
- 类型:0x4
- 标志:可能包含
ACK
(0x1)
- 流标识符:必须为 0(全连接设置)
-
帧负载:
- 设置项:每个设置项 6 字节,包含:
- 设置标识符(16 位):表示设置的类型(例如
SETTINGS_HEADER_TABLE_SIZE
、SETTINGS_ENABLE_PUSH
等)。
- 设置值(32 位):对应设置项的值。
标志
- ACK (0x1):确认接收到的 SETTINGS 帧,无需包含设置项。
6. PUSH_PROMISE 帧(Type = 0x5)
功能
服务器使用 PUSH_PROMISE 帧向客户端推送资源,提前发送可能需要的响应。
结构
-
帧头:
- 长度:可变
- 类型:0x5
- 标志:可能包含
END_HEADERS
(0x4)、PADDED
(0x8)
- 流标识符:所属的流 ID(父流 ID)
-
帧负载:
- 可选的 PADDED 字段(1 字节):表示填充字节的长度。
- 承诺的流 ID(31 位):被推送的流 ID。
- 头部块:使用 HPACK 压缩的头部字段。
- 可选的填充字节:用于填充数据,长度由 PADDED 标志指定。
标志
- END_HEADERS (0x4):指示头部块已经完全传输。
- PADDED (0x8):指示帧负载包含填充字节。
7. PING 帧(Type = 0x6)
功能
用于测量连接的往返时间(RTT)或确认连接的可用性。
结构
-
帧头:
- 长度:8 字节
- 类型:0x6
- 标志:可能包含
ACK
(0x1)
- 流标识符:必须为 0
-
帧负载:
- Opaque Data(8 字节):任意 8 字节数据,用于回显。
标志
- ACK (0x1):确认收到的 PING 帧,回应相同的数据。
8. GOAWAY 帧(Type = 0x7)
功能
用于指示连接的终止,通知对方不再接受新的流。
结构
-
帧头:
- 长度:可变(至少 8 字节)
- 类型:0x7
- 标志:无定义
- 流标识符:必须为 0
-
帧负载:
- 最后一个流 ID(31 位):指示发送方希望接收方不再接收的流 ID 之前的所有流均可继续处理。
- 错误码(32 位):指示连接终止的原因(例如
NO_ERROR
、PROTOCOL_ERROR
等)。
- 附加数据(可选):人类可读的错误信息,使用 UTF-8 编码。
标志
9. WINDOW_UPDATE 帧(Type = 0x8)
功能
用于调整流或连接的窗口大小,实现流量控制。
结构
-
帧头:
- 长度:4 字节
- 类型:0x8
- 标志:无定义
- 流标识符:所属的流 ID(0 表示连接级别窗口)
-
帧负载:
- 窗口增量(31 位):表示窗口大小增加的字节数(必须大于 0)。
标志
10. CONTINUATION 帧(Type = 0x9)
功能
用于传输被分割的头部块,当 HEADERS 或 PUSH_PROMISE 帧的头部块过长时使用。
结构
-
帧头:
- 长度:可变
- 类型:0x9
- 标志:可能包含
END_HEADERS
(0x4)、PADDED
(0x8)
- 流标识符:所属的流 ID
-
帧负载:
- 头部块:使用 HPACK 压缩的头部字段的后续部分。
- 可选的填充字节(仅在 PADDED 标志下使用):用于填充数据,长度由 PADDED 标志指定。
标志
- END_HEADERS (0x4):指示头部块已经完全传输。
- PADDED (0x8):指示帧负载包含填充字节。
错误码
数据映射
错误码名称与其对应的 32 位无符号整数 之间存在明确的映射关系。每个错误码名称对应一个唯一的数值,这些数值在协议规范中被预定义。
标准错误码列表
根据 RFC 7540 规范,HTTP/2 定义了以下标准错误码:
错误码名称 |
数值(十六进制) |
数值(十进制) |
描述 |
NO_ERROR |
0x0 |
0 |
正常关闭,无错误发生。 |
PROTOCOL_ERROR |
0x1 |
1 |
一般协议错误,协议未被正确遵循。 |
INTERNAL_ERROR |
0x2 |
2 |
内部错误,服务器遇到意外情况。 |
FLOW_CONTROL_ERROR |
0x3 |
3 |
流量控制错误,流量控制机制被违反。 |
SETTINGS_TIMEOUT |
0x4 |
4 |
设置超时,未能在规定时间内完成设置交换。 |
STREAM_CLOSED |
0x5 |
5 |
流已关闭,尝试操作一个已关闭的流。 |
FRAME_SIZE_ERROR |
0x6 |
6 |
帧大小错误,接收到的帧大小不符合协议要求。 |
REFUSED_STREAM |
0x7 |
7 |
拒绝流,请求被服务器拒绝。 |
CANCEL |
0x8 |
8 |
请求被取消,客户端或服务器主动取消请求。 |
COMPRESSION_ERROR |
0x9 |
9 |
压缩错误,头部压缩(HPACK)出现问题。 |
CONNECT_ERROR |
0xa |
10 |
连接错误,CONNECT 方法失败。 |
ENHANCE_YOUR_CALM |
0xb |
11 |
服务器认为客户端发送请求过于频繁,需要冷静。 |
INADEQUATE_SECURITY |
0xc |
12 |
安全性不足,使用的加密套件不被接受。 |
HTTP_1_1_REQUIRED |
0xd |
13 |
需要使用 HTTP/1.1 协议,HTTP/2 不支持的特性。 |
错误码的扩展与自定义
虽然上述列出了标准的 HTTP/2 错误码,但协议允许实现者定义 自定义错误码。自定义错误码需遵循以下规则:
- 范围限制:自定义错误码的数值应大于
0xd
(13),避免与标准错误码冲突。
- 分配管理:通常由组织或开发者自行管理,确保错误码的唯一性和一致性。
- 文档记录:自定义错误码应在相关文档中详细记录其含义和使用场景,方便双方理解和调试。
错误码的使用示例
在 RST_STREAM 帧中使用错误码
当服务器决定终止某个流时,会发送一个 RST_STREAM 帧,并在帧负载中包含相应的错误码。例如:
RST_STREAM Frame:
- Frame Header:
- Length: 4
- Type: 0x3
- Flags: 0x0
- Stream Identifier: 1
- Frame Payload:
- Error Code: 0x1 (PROTOCOL_ERROR)
在 GOAWAY 帧中使用错误码
当服务器决定终止整个连接时,会发送一个 GOAWAY 帧,并在帧负载中包含最后一个有效的流标识符和错误码。例如:
GOAWAY Frame:
- Frame Header:
- Length: 8
- Type: 0x7
- Flags: 0x0
- Stream Identifier: 0
- Frame Payload:
- Last Stream ID: 100
- Error Code: 0x2 (INTERNAL_ERROR)
总结
HTTP/2 的错误码机制通过 32 位无符号整数 的二进制格式高效传输错误信息,帮助通信双方快速定位和处理问题。标准错误码涵盖了从正常关闭到各种协议和内部错误的广泛场景,同时也支持自定义错误码以满足特定需求。理解和正确使用这些错误码,对于开发和维护稳定、高效的 HTTP/2 应用至关重要。
各帧类型的使用场景与注意事项
数据帧与头部帧
- DATA 和 HEADERS 帧是传输实际 HTTP 数据的主要载体。HEADERS 帧用于发送头部信息,而 DATA 帧用于发送主体内容。
- CONTINUATION 帧用于处理过长的头部块,确保头部信息可以被分割成多个帧传输。
流优先级与控制
- PRIORITY 帧允许客户端和服务器设定流的优先级,优化资源分配和响应时间。
- WINDOW_UPDATE 帧实现流量控制,防止发送方过快地发送数据,导致接收方处理不过来。
连接管理
- SETTINGS 帧用于初始化和更新连接的配置参数,如最大并发流数、初始窗口大小等。
- PING 帧用于检测连接的活跃性和测量延迟。
- GOAWAY 帧用于优雅地终止连接,通知对方不再接受新的流。
错误处理
- RST_STREAM 帧用于中止特定流,通知对方发生了错误或取消请求。
- GOAWAY 帧用于中止整个连接,适用于严重错误或维护需要。
服务推送
- PUSH_PROMISE 帧允许服务器向客户端主动推送资源,减少延迟和提高性能。
总结
HTTP/2 的帧机制极大地提升了网络传输的效率和灵活性。
通过多种帧类型的协同工作,HTTP/2 实现了多路复用、头部压缩、流量控制和优先级管理等先进特性。
这些帧类型的详细理解和正确使用,对于开发高性能、可扩展的网络应用至关重要。