前言:

  HTTP2引入了二进制分帧层,将普通的请求/响应,拆解为帧实现请求和响应的并发。HTTP2规定了10中类型的帧。本文将对这10种类型的帧做总结。

帧的结构:
   所有的帧都包含一个9 byte的帧头 + 可边长的正文不同。根据帧的类型不同,正文部分的结构也不一样。
   帧头:
       Length(3
bypte):表示帧的正文部分Payload的长度。初始设为2^14(16384),如果想要发送更大长度的帧,必须收到设置有SETTINGS_MAX_FRAME_SIZE.的
    SETTING frame。帧头的9 byte不算在length的计算范围之内。   Type(1
byte):只有0h-Ah有效,如果收到了type超出范围的,必须忽略这一帧。   Flags(1 bype):标志位,常用的flag
有END_STREAM,标志该帧是数据流的最后一帧。   Stream Identifier(31 bit): 帧的id,表示该帧属于哪个数据流

  DATA 帧:
 

  DATA: 可变长,是应用数据。长度受到Length, Window Size的控制。   Pad Length:
 一个8bit域,表示着Padding域的长度。   Padding
:*padding填充为若干个0x0比特,由padding来控制是否校验。如果需要校验,则对非0的部分回应PROTOCAL_ERROR异常。padding的长度需要计算在flow
controll 之内      DATA frame的flag可以有:   END_STREAM
(0x1):当设置改为之后,stream将转变为half-closed或者closed状态。   PADDED:设置了该位,那么bit 3表明着Pad
Length field并且正文需要填充。 payload = Padding Length + Data + Padding
  异常情况:   1、每一个DATA 帧都必须从属与一个数据流。如果收到一个DATA frame它的stream id
为0x0,那么抛出一个PROTOCAL_ERROR错误。  
2、如果padding的长度超过了payload,那么必须将其视为一个错误,并抛出PROTOCAL_ERROR   
  HEADER 帧 :
  
  Pad Length:1 btye,表明padding 域的长度。在flag中设置了PADDED(00001000) 位的时候有效。   E: 1
bit,表示流依赖是否专用,只有当设置了PRIORITY的时候才有效。   Stream Dependency:
表示该数据流请求的资源依赖于哪个数据流。接收端优先响应被依赖资源的数据流。   Weight: 在没有stream
dependency的时候,或者被依赖的资源已经被响应了的时候。weight越大,越优先响应。   Header Block Fragment:
用于传送差异的头部信息
  header的flag可以有:    END_STREAM:
(0000001B),用于关闭该数据流,如果接收端收到该标志位,则流进入half-close
或者close的状态。但是还是可以继续接受CONTINUATION 帧。因为理论上,CONTIUATION帧属于HEADER的一部分。  
 END_HEADERS:(00000100B),表示header block fragment包含了全部的header
block。之后不会有CONTIUATION frame。    PADDED:(00001000B),表明正文是否需要填充padding*    
PRIORITY: (00010100B),第五位表示E,那么需要stream dependency 和weight位有效。
  异常情况:   1、收到的header 帧没有stream id,那么抛出PROTOCAL_ERROR  
 2、padding域如果超出length的长度,那么会抛出PROTOCAL_ERROR

 PRIORITY 帧
 
   E:表明stream dependency 和weight有效。   Stream Dependency:
表示该数据流请求的资源依赖于哪个数据流。接收端优先响应被依赖资源的数据流。   Weight: 在没有stream
dependency的时候,或者被依赖的资源已经被响应了的时候。weight越大,越优先响应。
  priority 没有标志位。可以在stream idle 或者 closed的时候发送。用于接收端决定接收顺序。但是PRIORITY
帧可能在stream已经被处理的时候被收到。这个时候,这个PRIORITY将无效。      异常情况:   1、PRIORITY
帧必须从属与一个stream,如果没有steam id,那么将抛出PROTOCAL_ERROR   2、PRIORITY 帧如果长度超过5
byte,那么将抛出FRAME_SIZE_ERROR

  RST_STREAM 帧
       
 收到RST_STREAM帧的会立刻关闭stream。并且包含着结束的错误码,错误码中包含着为什么要结束该数据流。接收端在收到该帧之后,不能发送任何stream。但是发送端可以接受还在信道上的帧。因为要保证已经发出去的帧被正确处理。
      异常情况:   1、如果RST_STREAM没有stream id,那么将抛出PROTOCAL_ERROR错误。  
2、不能再流idle的时候接受RST_STREAM,否则将抛出PROTOCAL_ERROR错误。  
3、如果RST_STREAM的长度超过4byte,那么将抛出FRAME_SIZE_ERROR错误。

  SETTINGS 帧
       该帧传递着配置的信息,这些信息将影响着该数据流的通信过程。一般在HEADERS frame发送之前先发送SETTINGS
帧,但是也允许在通信过程中发送SETTINGS帧。可以发送多个SETTINGS帧,这些帧将按序处理,后面设置的值将覆盖前面的值。  
由于SETTINGS帧是配置信息,具有高优先级,所以需要接收方发送ack保证每个SETTINGS 帧正确到达。  
很多时候SETTINGS帧是面向整个连接,而非单个流的。在这种情况下,Stream id设置为0.
  Identifier-value:设置参数-设置值。有一下这些情况:(每一个setting帧都设置一组值)  
SETTINGS_HEADER_TABLE_SIZE(0x1):允许发送者通知接收方header的最大长度。初始值为4096 byte  
SETTINGS_ENABLE_PUSH(0x2):允许服务端主动推送消息给客户端。如果设置了disable,那么服务端不能发送PUSH_PROMISE
frame。0是允许,1是不允许  
SETTINGS_MAX_CONCURRENT_STREAM(0x3):表明当前connection可以同时建立stream的的最大数目。初始为100  
SETTINGS_INITIAL_WINDOW_SIZE(0x4):表明发送者初始的窗口大小,初始值为2^16 -1  
SETTINGS_MAX_FRAME_SIZE(0x5):最大帧长。设置DATA帧允许的最大长度,最大不能超过2^24-1  
SETTINGS_MAX_HEADER_LIST_SIZE(0x6):表示发送方想要接受的header的最大长度。并且这个长度是没有压缩之前的。包括name、value
+ 每个头部域的32byte。       flags:   ACK:
SETTINGS的处理顺序必须按照发送的顺序。一旦所有的value都被处理完了,接受者必须发送一个SETTINGS,包含一个ack。之后发送方才能继续发送。  
 异常情况:  1、SETTINGS是针对全局connection的,所以stream id必须是0,否则将抛出PROTOCAL_ERROR错误。
 2、SETTINGS会影响connection 状态,一个未完成的,或者有错的SETTINGS必须被视为error,抛出PROTOCAL_ERROR
 3、SETTINGS 长度如果超过了6 byte,那么将抛出一个FRAME_SIZE_ERROR
 4、如果SETTINGS_ENABLE_PUSH设置为1,但是服务器发送了PUSH_PROMISE,那么将抛出PROTOCAL_ERROR
 5、如果SETTINGS_INITIAL_WINDOW_SIZE: 超过了2^31-1,那么将抛出FLOW_CONTROL_ERROR
 6、如果发送了SETTINGS,一直没有收到ack,那么将抛出SETTINGS_TIMEOUT

PUSH_PROMISE 帧
     PUSH_PROMISE帧是服务端主动向客户端发送数据的时候,用于打开这一个stream的请求帧。作用类似于HEADERS
frame。只有当SETTING_ENABLE_PUSH设置为0的时候,可以发送PUSH_PROMISE。      Pad Length:padding的长度
 R: 保留位   Promise Stream Id:打开这一组stream 时的stream id。这个时候帧头的stream
id指的是这一组主动推送是给哪一组请求(数据流)主动发消息的。   Header Block Fragment:请求头部域的差值部分   Padding:
填充位      flags:   END_HEADERS:(0x4) 当设置了该flag,那么第二个bit表示Header Block
Fragment包含了全部的请求头。   PADDED:(0x8),第三bit表示Pad Length 和Padding 有效
 
PUSH_PROMISE必须在stream已经打开或者半关闭的状态时才能发送。如果接收端想要拒绝PUSH_PROMISE,可以发送RST_STREAM帧。一个PUSH_PROMISE帧会以两种方式改变stream。第一种是PUSH_PROMISE
的header block fragment会改变header
压缩。第二种是发送PUSH_PROMISE会创建一个新流。发送端必须在确认该流已经建立之后,才能发送之后的DATA frame。
  异常状态:   1、PUSH_PROMISE必须从属于某一个数据流,所以如果stream id为0,那么必须报PROTOCAL_ERROR错误  
2、只有在SETTINGS_ENABLE_PUSH设置为0的时候,才能允许发送PUSH_PROMISE,否则会报PROTOCAL_ERROR错误  
3、如果被依赖的流不是open
或者half-closed状态,但是接受到了PUSH_PROMISE帧,那么必须将该帧视为错误,抛出PROTOCAL_ERROR错误。但是发出RST_STREAM的一端必须先处理接受到的PUSH_PROMISE,因为它可能实在发送RST之前发出的。
  4、如果PUSH_PROMISE的stream id所指向的流是非法的,那么必须报PROTOCAL_ERROR错误。

  PING 帧
         
PING帧用于测试一个空闲的connection是否还有效的帧。可以由任何一端发出。长度为8byte。接收端如果收到没有ACK的PING帧,必须发送一个带有ACK的PING
返回给发送端。并且PING帧应该有更高的优先级         flags:     1、ACK(0x1):bit 0 表明PING frame是一个PING
response。接收端必须在接受到PING帧之后,返回一个设置有ack的PING帧
   异常情况:    1、PING帧不能依赖于任何的流,如果PING真有stream id,那么必须报错。Protocal_ERROR  
 2、如果PING帧的长度超过了9 byte,那么必须报FRAME_SIZE_ERROR错误

  GOAWAY帧 
  
     
GOAWAY帧的作用是针对于整个connection而非单个stream的。它作用于通知双方关闭一个connection,收到该帧之后,不能再建立新的数据流,但是要把已经建立的数据流处理完。由于存在一种情况,是一端在收到GOAWAY之前,又发送了一个建立stream的header
frame,于是在GOAWAY帧上设计了一个Last-Stream-ID域,作用是标识这是该connection上最大的frame,如果一方在接收到有更高stream
id的frame时,将忽略该帧。接收到GOAWAY帧的一端不能再在该conncetion上建立新的stream,但是可以建立新的connection。    
 终端在关闭connection的时候,应该发送GOAWAY帧,这样对方可以知道哪个stream及其以前的数据流正在被处理。但是如果遇到错误了,那么终端会选择不发GOAWAY就建立一个connection。
    一个终端可以回发送多个GOAWAY
帧,如果环境变化了。比如说,一个终端想要关闭链接,但是没有遇到任何错误。它可能先发送一个NO_ERROR的frame。之后,又遇到错误了,需要立即shutdown该帧,这个时候,会发送一个带有错误码的GOAWAY
帧。     Last-Stream-ID:  标识该connection上能处理的最后的stream。如果再接收到stream
id更高的frame,将忽略它。     Error Code:如果因为错误而关闭该链接的话,把错误码放到该位置上。         flags
:没有flags位          异常情况:     1、必须指定Last-Stream-ID: 如果该位为0,需要报PROTOCOL_ERROR    
2、GOAWAY帧自己的stream id必须为0,否则报PROTOCOL_ERROR
  WINDOW_UPDATE帧
          这个帧的作用在于告诉对方,还能接受多少byte的数据。作用于DATA
frame。如果一端在接受到滑动窗口,发现能发的字节数变成负数了,那么必须等到接受到一个WINDOW_UPDATE
帧,使得窗口大小为正了之后,才能继续发送DATA
frame。这个帧还可以作为ack,有一个delter位,标识刚才确认了多少byte的数据,一端在发送DATA
frame之后,要等到收到WINDOW_UPDATE,确认之前的数据已经接收到了,才能发送下一个DATA frame             
Window Size Increment:表示当前还能接受的字节大小         异常情况:    
 1、如果接收端不能接受任何帧,在收到该帧的时候,要响应FLOW_CONTROL_ERROR      2、如果接收端收到window size
increment 为0,那么必须报PROTOCOL_ERROR错误      3、如果WINDOW_UPDATE
的长度超过4type,那么必须报FRAME_SIZE_ERROR 错误

  CONTINUATION 帧
       该帧从属一个HEADER ,PUSH_PROMISE帧。用于发送在HEADER PUSH_PROMISE中放不下的request
header域。长度没有限制。
    flags:     END_HEADERS: (00000010B),表示stream id对应的数据流,request header
已经发送完了。         异常情况:    1、如果stream id不是当前的stream id或者是0,那么必须报PROTOCOL_ERROR错误
   2、发送CONTUATION帧之前必须先发送HEADERS, PUSH_PROMISE帧,并且前面这些帧没有设置END_HEADERS
标志位,否则会报PROTOCOL_ERROR错误。

技术
©2019-2020 Toolsou All rights reserved,
一个名叫“讨火车”的小村子 终于把火车讨来了c++内存 指针越界检测机制_CrtMemBlockHeade错误总结-myBatis plus 分页记一次EventBus内存泄露导致的项目问题服务器价格有什么差异?Android中获取当前正在显示的Activity实例创建数据mysql库流程在Vue中使用Web Worker最优化方法总结:公式解、数值优化、求解思想使用easyPOI导入Excel数据