HTTP2协议-二进制帧结构总结

前言:
  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错误
————————————————
版权声明:本文为CSDN博主「lizzvia」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013237862/article/details/70158334

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章