018: HTTP/2 中的二进制帧是如何设计的?
帧结构
HTTP/2 中传输的帧结构如下图所示:
每个帧分为帧头
和帧体
。先是三个字节的帧长度,这个长度表示的是帧体
的长度。
然后是帧类型,大概可以分为数据帧和控制帧两种。数据帧用来存放 HTTP 报文,控制帧用来管理流
的传输。
接下来的一个字节是帧标志,里面一共有 8 个标志位,常用的有 END_HEADERS表示头数据结束,END_STREAM表示单方向数据发送结束。
后 4 个字节是Stream ID
, 也就是流标识符
,有了它,接收方就能从乱序的二进制帧中选择出 ID 相同的帧,按顺序组装成请求/响应报文。
流的状态变化
从前面可以知道,在 HTTP/2 中,所谓的流
,其实就是二进制帧的双向传输的序列。那么在 HTTP/2 请求和响应的过程中,流的状态是如何变化的呢?
HTTP/2 其实也是借鉴了 TCP 状态变化的思想,根据帧的标志位来实现具体的状态改变。这里我们以一个普通的请求-响应
过程为例来说明:
最开始两者都是空闲状态,当客户端发送Headers帧
后,开始分配Stream ID
, 此时客户端的流
打开, 服务端接收之后服务端的流
也打开,两端的流
都打开之后,就可以互相传递数据帧和控制帧了。
当客户端要关闭时,向服务端发送END_STREAM
帧,进入半关闭状态
, 这个时候客户端只能接收数据,而不能发送数据。
服务端收到这个END_STREAM
帧后也进入半关闭状态
,不过此时服务端的情况是只能发送数据,而不能接收数据。随后服务端也向客户端发送END_STREAM
帧,表示数据发送完毕,双方进入关闭状态
。
如果下次要开启新的流
,流 ID 需要自增,直到上限为止,到达上限后开一个新的 TCP 连接重头开始计数。由于流 ID 字段长度为 4 个字节,最高位又被保留,因此范围是 0 ~ 2的 31 次方,大约 21 亿个。
流的特性
刚刚谈到了流的状态变化过程,这里顺便就来总结一下流
传输的特性:
- 并发性。一个 HTTP/2 连接上可以同时发多个帧,这一点和 HTTP/1 不同。这也是实现多路复用的基础。
- 自增性。流 ID 是不可重用的,而是会按顺序递增,达到上限之后又新开 TCP 连接从头开始。
- 双向性。客户端和服务端都可以创建流,互不干扰,双方都可以作为
发送方
或者接收方
。 - 可设置优先级。可以设置数据帧的优先级,让服务端先处理重要资源,优化用户体验。
以上就是对 HTTP/2 中二进制帧的介绍,希望对你有所启发。