26 HTTP/2 内核

Posted by CodingWithAlice on April 30, 2021

26 HTTP/2 内核

一篇还不错的博文:深入理解http2.0协议,看这篇就够了!

  • 总结

    1、HTTP/2 必须先发送一个 连接前言 字符串,然后才能建立正式连接

    2、HTTP/2 废除了起始行,统一使用头字段,在两端维护字段 Key-Value 的索引表,使用HPACK算法压缩头部 3、HTTP/2 把报文切分为多种类型的 二进制帧,报头里最重要的字段是 流标识符,标记帧属于哪个流

    4、流是 HTTP/2 虚拟的概念,流是帧的双向传输序列,相当于 HTTP/1 里的一次 请求 - 应答

    5、在一个 HTTP/2 连接上可以并发多个流,也就是多个 请求 - 响应 报文,这就是 多路复用

连接前言

  • 作用

    TLS 握手成功之后,客户端必须要发送一个 连接前言 Magic(connection preface),用来确认建立 HTTP/2 连接

  • 内容

    HTTP/1 请求报文,使用纯文本的 ASCII 码格式,请求方法是特别注册的一个关键字 PRI,全文只有 24 个字节:PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

头部压缩

在请求发送前,必须要用 HPACK 算法来压缩头部数据

  • HPACK 算法核心
需要客户端和服务器各自维护一份 **索引表**,压缩和解压缩就是 **查表和更新表** 的操作
  • 伪头字段

    HTTP/2 废除了原有的起始行概念,把起始行里面的 请求方法、URI、状态码 等统一转换成了伪头字段(比真正的头字段在名字多前加一个:

    • :authority :method :status,分别表示的是 域名、请求方法和状态码
  • 静态表

    HTTP 报文头:Key-Value 形式的字段

    HTTP/2 为一些最常用的头字段定义了一个只读的静态表

    :method=GET 使用索引值 2 表示,:path=/index.html 使用索引值 5 表示

    image-20210430100035153

  • 动态表

    和静态表结构相同,但会在 编码解码的时候随时更新

    比如说,第一次发送请求时的 user-agent 字段长是一百多个字节,用哈夫曼压缩编码发送之后,客户端和服务器都更新自己的动态表,添加一个新的索引号 65。那么下一次发送的时候就不用再重复发那么多字节了,只要用一个字节发送编号就好

报文拆解成二进制帧

帧结构:只有 9 字节

image-20210430100142897

image-20210430100214085

解析细节:

1、帧长度:3 个字节

–> 抓包示例中为 00010a,表示数据长度是 266 字节

2、帧类型:分成 数据帧控制帧 两类,可扩展,例如,HEADERS 帧和 DATA 帧属于数据帧,存放的是 HTTP 报文,而 SETTINGSPINGPRIORITY 等则是用来管理流的控制帧

–> 抓包示例中为 01,表示 HEADERS 帧,负载(payload)里面存放的是被 HPACK 算法压缩的头部信息

3、标志位:可以保存 8 个标志位,携带简单的控制信息,例如,END_HEADERS 表示头数据结束,END_STREAM 表示单方向数据发送结束

–> 抓包示例中为25,转换成二进制 11001 有 3 个位被置 1,PRIORITY 表示设置了流的优先级,END_HEADERS 表示这一个帧就是完整的头数据,END_STREAM 表示单方向数据发送结束,后续再不会有数据帧

4、流标识符:代表了 帧所属的流,接收方使用它就可以从乱序的帧里识别出具有相同流 ID 的帧序列,按顺序组装起来就实现了虚拟的流

–> 抓包示例中为 01,表示这是客户端发起的第一个流,后面的响应数据帧也会是这个 ID,也就是说在 stream[1] 里完成这个请求响应

## 流与多路复用 流是二进制帧的双向传输序列,如下是三个流 0、1、3

image-20210430100355483

image-20210430100422700

流有哪些特点?

1、流是 可并发 的,一个 HTTP/2 连接上可以同时发出多个流传输数据,也就是并发多请求,实现 多路复用

2、客户端和服务器都可以创建流,双方互不干扰

3、流是双向的,一个流里面客户端和服务器都可以发送或接收数据帧服务端推送(遵守同源策略)

4、流之间没有固定关系,流之间彼此独立,但流内部的帧是有严格顺序的

5、流可以设置优先级,让服务器优先处理,比如先传 HTML -> CSS -> js -> 传图片,优化用户体验

6、流 ID 不能重用,只能顺序递增,客户端 发起的 ID 是 奇数服务器端 发起的 ID 是偶数

7、在流上发送 RST_STREAM 帧可以 随时终止流,取消接收或发送

8、第 0 号流 比较特殊,不能关闭,也不能发送数据帧,只能发送 控制帧,用于流量控制

注意,根据上述特性可以推论:

1、HTTP/2 在一个连接上使用多个流收发数据,那么它本身 默认就会是长连接

2、下载大文件的时候想取消接收,在 HTTP/1 里只能断开 TCP 连接,重新三次握手,成本很高,而在 HTTP/2 里就可以简单地发送一个 RST_STREAM 中断流,而 长连接会继续保持

3、因为客户端和服务器两端都可以创建流,而流 ID 有奇数偶数和上限的区分,所以大多数的流 ID 都会是奇数

4、ID 用完的话,客户端可以发送控制帧 GOAWAY真正关闭 TCP 连接

流状态转换

根据 帧的标志位 实现 流状态转换

下一次再发请求就要 开一个新流(而不是新连接),流 ID 不断增加,直到到达上限,发送 GOAWAY 帧开一个新的 TCP 连接,流 ID 就又可以重头计数。

image-20210430100605211