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

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