协议学习——HTTP2帧结构总结

    xiaoxiao2021-04-12  73

    前言:

      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错误。
    转载请注明原文地址: https://ju.6miu.com/read-667307.html

    最新回复(0)