HTTP2 协议初识

0.363字数 3113阅读 992

  RFC:https://tools.ietf.org/html/rfc7540

RFC 中英文对照:https://github.com/fex-team/http2-spec/blob/master/HTTP2%E4%B8%AD%E8%8B%B1%E5%AF%B9%E7%85%A7%E7%89%88(06-29).md

O'Reilly :https://hpbn.co/http2/

SPDY:http://www.chromium.org/spdy/

随着Web应用日渐广泛,复杂程度和重要性也在不断的增长,也因此对开发人员和用户带来了负担,HTTP2 支持所有的HTTP/1.1的核心特征,提供了HTTP语义的传输优化,并且在各方面做到更高效。

HTTP 2 基本概念

HTTP2 是运行在TCP或者SSL协议之上,属于应用层的协议。HTTP2.0消息包以二进制帧的形式进行封装。

HTTP/2 binary framing layer

HTTP2引入了一下的三个新概念:

Stream: 已经建立连接的双向字节流,用唯一ID标示,可以传输一个或多个消息

Message:逻辑上的HTTP消息,请求或者响应,可以包含多个 frame

Frame:HTTP2通信的最小单位,二进制头封装,封装HTTP头部或body

HTTP2是把一个HTTP数据包分成多个帧发送,每个帧有一个二进制头,并把HTTP分成多个独立小帧,多个帧组成一个Message在流中发送。不同流的帧有可能交错到达,帧的报文头中标示了属于哪一个流。

HTTP/2 streams, messages, and frames

HTTP2 的帧格式

HTTP2的最小数据单位是帧,所有帧以9字节的帧头并跟着0-16,383字节的数据。

Frame Layout

Length: unsigned 24-bit integer,最大值为 2^24(16384),指的是不包括头部的部分

Type:帧的类型

Flags:为帧类型保留的8字节字段有具体的布尔标识

R:1位的保留字段

Stream Identifier:31字节的流标识符。0是保留的,标明帧是与连接相关作为一个整体而不是一个单独的流。

帧主体的结构和内容完全取决于帧类型。

HTTP2 有以下十种帧类型:

Frame Type Registry

1、DATA :数据帧,Type=0x0,主要用来传递消息体

DATA Frame Payload

Pad Length : 8位,可选,只在设置了PADDED标记时呈现。表示帧填充的字节为单位的长度。

Data : 应用数据。

Padding : 填充字节不包含任何应用语义值。填充字节在发送时设为0,接收时忽略。

DATA 帧定义了以下的 Flags:

END_STREAM (0x1): 位1,表示当前帧是相应流发送的最后一帧。设置时流进入半封闭或关闭状态。

PADDED (0x8): 位4,表示Pad Length 字段启用与否。

2、HEADERS :头部帧,Type=0x1,主要用于传递消息头

HEADERS Frame Payload

Pad Length:8位可选,在设置PADDED 标记时呈现。表示帧填充的字节为单位的长度。

E : 1位可选,在优先级标记设置时呈现。表示用于标识流依赖是否是专用的。

Stream Dependency : 31位可选,在优先级标记设置时呈现。流依赖的流的标识符。

Weight : 8位可选,在优先级标记设置时呈现。流的8位权重标记,1-256的值。

Header Block Fragment : 报头块。

Padding : 填充字节

HEADERS 帧定义了以下的 Flags:

END_STREAM (0x1) : 位1,标识是此发送端对流发送的最后报头区块。设置这标记使流进入半封闭状态。

END_HEADERS (0x4) : 位3,表示帧包含了整个的报头块,且后面没有延续帧。 END_HEADERS为0的报头帧后面必须跟着延续帧。

PADDED (0x8) : 位4,表示Pad Length字段会呈现。

PRIORITY (0x20) : 位6,优先级标记(E), 流依赖及权重字段将会呈现。

3、PRIORITY :优先级帧,Type=0x2,用于设置流的优先级

PRIORITY Frame Payload

E : 1位标记,指示流的依赖是专有的。

Stream Dependency : 流所依赖流的31位标识符。

Weight: 流的权重(8位)。1-256的权重值。

4、RST_STREAM :流结束帧,Type=0x3,用于终止异常流

RST_STREAM Frame Payload

RST_STREAM 帧由一个32位整数标记错误码,指明流被终止的原因。

RST_STREAM 帧必须与流相关联,就是说stream id 不能为 0x0

RST_STREAM帧绝对不能在流处于“空闲”状态下发送。

5、SETTINGS :连接配置参数帧,Type=0x4,用于设置参数

Setting Format

载体包含0或多个参数,每个包含一个16位标识以及一个32位的值。

1)设置帧由两个终端在连接开始时发送,连接生存期的任意时间发送。

2)设置帧的参数将替换参数中现有值,不能识别的忽略。

3)设置帧总是应用于连接,而不是一个单独的流。流ID必须为0;

SETTINGS 帧定义了以下的 Flags:

ACK (0x1) : 位1,表示设置帧被接收端接收并应用。如果设置了ACK,设置帧的载体必须为空。

Settings Registry

SETTINGS_HEADER_TABLE_SIZE (0x1) : 发送端通知远端报头压缩表的最大承载量。初始值是4,096个字节。

SETTINGS_ENABLE_PUSH (0x2) : 用来关闭服务器推送。0时不能发送PUSH_PROMISE。1表示可以推送。

SETTINGS_MAX_CONCURRENT_STREAMS (0x3) : 标明发送端允许接收端创建的最大并发流的数量。没有限制(<100) 。 0值阻止新流的创建。

SETTINGS_INITIAL_WINDOW_SIZE (0x4) : 表示发送端流量控制的初始窗口大小(字节)。初始值是65,535。 影响所有流的窗口大小.

SETTINGS_MAX_FRAME_SIZE (0x5): 接收最大帧大小。初始值为2^14 (16,384)字节,最大值为2^24-1 or 16,777,215字节。

SETTINGS_MAX_HEADER_LIST_SIZE (0x6): 可接收的header列表长度(字节),基于非压缩的列表大小。

6、PUSH_PROMISE:推送承诺帧,Type=0x5,Server推送之前告知Client端

PUSH_PROMISE Payload Format

1) 被推送的流并不需要按照顺序使用。

2) 接收端可以给推送端返回一个RST_STREAM拒绝接收。

Pad Length : 8位,只在PADDED标记设置时才呈现。

R : 1bit保留位。Padding : 填充字节。

Promised Stream ID : 31位整数表示终端准备发送的流标记。

Header Block Fragment : 包含请求头字段的报头区块。

PUSH_PROMISE 帧定义了以下的Flags:

END_HEADERS (0x4) : 位3, 表明帧包含了整个报头区块。

PADDED (0x8) : 位4, 表明Pad Length字段是已设置。

7、PING:Type=0x6,发送端测量最小的RTT时间,检测连接是否可用

PING Payload Format

PING 帧定义了以下的Flags:

ACK (0x1) : 位1表示PING帧是一个PING响应。

1)PING帧可以被任何终端任何时刻发送。

2)PING帧必须在载体中包含一个8字节长度的数据。

收到不含ACK的PING帧必须发送一个有ACK的PING响应,带相同的载荷。PING响应应设置比其他帧更高的优先级。流ID为0,不和任何流关联;

8、GOAWAY:超时帧,Type=0x7,通知对端不要在连接上建新流

GOAWAY Payload Format

可以由客户端或服务端发送。发动端将忽略连接上流标示符大于Last-Stream-ID的流。

接收端接收到超时帧后不能在这个连接上打开新流,可以创建新连接。

终端在关闭连接之前总是应当发送一个超时帧;

适用于连接而不是特定的流。流标识符必须是0x0,否则错误处理;

连接关闭前小于或等于标识符上的流没有完全关闭的,重试请求;

小于或等于最后流标识符的流可能仍然能成功完成,保持连接在打开状态直到正在处理的流全部处理完成。

在发送超时帧后,发送端能丢弃流标识符大于最终流标识的流的帧。但任何修改流状态的帧不能被忽略。

超时帧包含一个32位错误码,包含关闭连接的原因。

9、WINDOW_UPDATE:Type=0x8,实现流量控制

WINDOW_UPDATE Payload Format

1) 可以作用单独的流(ID!=0) 或 整个连接(ID==0)

2) 所有类型的流量控制都是逐跳的(hop-by-hop), 中介端不会转发

3) 流量控制只适用于Data帧

4) 一个保留字节,一个31位整数( 发送端被允许传输的字节数,它的大小是接收端的缓存能力的衡量)。

5) 流和连接的初始值都是65535;流的窗口大小可以用SETTING帧设置大小SETTINGS_INITIAL_WINDOW_SIZE;

6) 通过设置窗口大小,可能导致窗口大小为负数(当前有10字节数据,设置为5字节,则剩余-5字节的长度)。

10、CONTINUATION:延续帧,Type=0x9,延续一个报头区块

CONTINUATION Frame Payload

只要流上前一帧是不带END_HEADERS的HEADERS帧、PUSH_PROMISE帧或者不带有END_HEADERS标记的CONTINUATION帧,可以发送任意多个延续帧。

CONTINUATION 帧定义了以下的Flags:

END_HEADERS (0x4) : 位3,指示帧是否是报头区块的终止。 如果END_HEADERS位没有被设置,这个帧必须跟着另一个延续帧。

HTTP2 错误码

Error Code Registry

HTTP2 连接过程

http2 的版本标识:

h2:基于TLS之上构建的HTTP/2,作为ALPN的标识符,两个字节表示,0x68, 0x32,即https

h2c:直接在TCP之上构建的HTTP/2,缺乏安全保证,即http

HTTP版本的请求过程:

在不知道服务器是否支持http2的情况下,可以利用http的升级机制发送试探包

1、客户端发起请求

2、服务器不支持 http2,直接按照 http/1.1响应

3、服务器支持 http2,通知客户端切换到http2

4、服务器发送的第一个http2帧,必须为SETTINGS帧做为连接序言

5、客户端接收到101响应后,也必须发送一个序言作为响应,其逻辑结构:

6、客户端可以马上发送请求帧或其它帧过去,不用等待来自服务器端的SETTINGS帧

7、任一端接收到SETTINGS帧之后,都需要返回一个包含确认标志位SETTIGN作为确认

8、其它帧的正常传输

HTTP Upgrade

HTTPS 版本的建立连接:

1、客户端和服务器端TLS层协商

2、客户端发送连接序言(同上表示,PRI + SETTINGS)

3、接收到客户端连接序言之后,服务器端发送连接序言

4、双方各自确认SETTINGS帧

5、其它帧的正常传输

ALPN

HTTP/2的直接连接:

1、客户端必须首先发送一个连接序言,其逻辑结构:

2、发送完毕序言之后,客户端可以不用等待来自服务器端响应,马上发送HTTP/2其它帧

3、服务器端接收到客户端的连接序言之后,需要发送一个SETTINGS帧作为连接序言

4、任一端接收到SETTINGS帧之后,都需要返回一个包含确认标志位SETTIGN作为确认

5、其它帧的正常传输

Starting HTTP/2

对比明文版的HTTP/1.1和HTTP/2完成一次请求-响应:

1、HTTP/1.1在建立建立之后,只需要发送请求报文数据

2、HTTP/2客户端需要在连接建立之初马上发送一个连接序言过去,然后才是正常请求

3、两端(客户端+服务器端)的两次完整的连接序言+确认的交互流程,多了两次往返过程

推荐阅读更多精彩内容