HTTP/2 frame format

以往的HTTP,咱們習慣了和head /body 打交道。而在HTTP2,取而代之的是幀(Frame)。它將會成爲協議中的最小通信單位——全部的數據,head,body都會打包到Frame內發送。Frame 有不少類型,好比 header frame, data frame (之後...,不本文就會繼續講到)。node

經過Frame 好處多多(還有後面要提到的流),一塊兒能夠完成多播和次序交錯的通信——很大的新特性。git

開門見山,看看幀格式(頭 9 字節是幀頭部,後面的都是有效載荷):github

+-----------------------------------------------+
 |                 Length (24)                   |
 +---------------+---------------+---------------+
 |   Type (8)    |   Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 Stream Identifier (31)                      |
 +=+=============================================================+
 |                   Frame Payload (0...)                      ...
 +---------------------------------------------------------------+

Frame 構成定義:ui

- **Length** :幀主體長度。24 bits unsigned int
 - **Type** : 類型(8 bits)。
 - **Flags** : 特定的Type,有一組特定的flag,以便對type作更多約定
 - **R** : 保留(1bit)。語義未設置而且必須在發送的時候設置爲 0 
 - **Stream Identifier** : 流標識符(31 bytes)。

每次看到報文格式定義中‘保留’字樣我就樂不可支。我太喜歡標準定義者的做派了。編碼

由於,‘保留’就是,能夠不看不瞭解它。因此這樣看起來,其實Frame Head就3條有意義的字段,分別指明: Frame的類型、Frame的內容長度、Frame 所屬的流。世界清淨了。至於‘長度’,固然重要,但是不寫程序碼以前,也能夠不看。spa

細化類型Type

差很少就是照抄下規範了 :)code

Frame Type      Code    
DATA            0x0 
HEADERS         0x1 
PRIORITY        0x2 
RST_STREAM      0x3 
SETTINGS        0x4 
PUSH_PROMISE    0x5 
PING            0x6 
GOAWAY          0x7 
WINDOW_UPDATE   0x8 
CONTINUATION    0x9

細化length

Length是指有效載荷(Payload)的長度。不包括Frame Header的長度(是啊,沒有必要嘛)。好比咱們要發送一個16進制的‘12345678’ 給對方,Length 就是 4 ,不是 8+ 4 = 12 。blog

細化Stream

爲何說須要標示流呢? 由於HTTP2場景下,TCP Connection再也不只有一對請求+響應了——能夠有多個響應。這些響應打包到多個Frame,在一個 Connection 上混合交錯的發。接收方必須知道每一個Frame屬於那個Response,這就是流所標示的了。圖片

有點不清不楚,不過,我會上代碼的。語言中止之處,代碼發生。get

案例

咱們要發送 0x12345678,流編號爲 10 ,類型爲DATA,那麼這個Frame的16進製表達就是:

'000004' + '00' + '00' + '0000000A' +   '12345678'

Testcase

以下爲go語言的代碼testcase,對一個數據Frame作編碼和解碼,雙向驗證一個 frame的生成過程。代碼來自:

func TestWriteData(t *testing.T) {
    fr, buf := testFramer()
    var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4
    data := []byte("ABC")
    fr.WriteData(streamID, true, data)
    const wantEnc = "\x00\x00\x03\x00\x01\x01\x02\x03\x04ABC"
    if buf.String() != wantEnc {
        t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
    }
    f, err := fr.ReadFrame()
    if err != nil {
        t.Fatal(err)
    }
    df, ok := f.(*DataFrame)
    if !ok {
        t.Fatalf("got %T; want *DataFrame", f)
    }
    if !bytes.Equal(df.Data(), data) {
        t.Errorf("got %q; want %q", df.Data(), data)
    }
    if f.Header().Flags&1 == 0 {
        t.Errorf("didn't see END_STREAM flag")
    }
}

細化Flag

上面提到了特定的Type,有不一樣的Flag,到底有什麼?(TODO: 看了代碼來補充細節:)

圖片描述
圖來自於:http://search.cpan.org/~crux/Protocol-HTTP2-0.14/lib/Protocol/HTTP2/Frame.pm

Ref:

  1. 一個github的repo,內有豐富的Frame種類和編碼後的字節結果
  2. go 寫的http2庫,做者說之後會變成標準庫。因此,特別值得看,比1還容易懂

疑問(已解決,文字已經更改)

標準提到的Frame Header是 8字節。可是 4 代碼中,header長度爲 9 ;node-http2 內的lib/framer.js 內func commonHeader也是長度9的header。看起來和標準是矛盾的?我哪裏沒有弄明白呢。

相關文章
相關標籤/搜索