博客 / 詳情

返回

HTTP/2.0的二進制是什麼?

這篇純粹滿足自己的好奇心

我好像是一個在海邊玩耍的孩子,不時為拾到比通常更光滑的石子或更美麗的 貝殼而歡欣鼓舞,而展現在我面前的是完全未探明的真理之海。牛頓

寫本文的時候,想起高中物理課本的一句話:

我好像是一個在海邊玩耍的孩子,不時為拾到比通常更光滑的石子或更美麗的貝殼而歡欣鼓舞,而展現在我面前的是完全未探明的真理之海。

那個時候不懂這句話,忙於刷分,如今純粹是為了自己的好奇心而探究一些問題,腦海中又開始復現這句話。本文的問題來自於前面的一篇文章:《HTTP學習筆記(三) HTTP/2》, 這篇文章裏我們提到了HTTP/2的幾個特點:

  1. is binary, instead of textual
二進制代替了文本
  1. is fully multiplexed, instead of ordered and blocking
多路複用
  1. can therefore use one connection for parallelism
並行請求
  1. uses header compression to reduce overhead
壓縮請求頭,減少消耗
  1. allows servers to “push” responses proactively into client caches
允許服務器主動推送響應進入客户端的緩存中

其實對於1我是不理解的,畢竟在計算機的世界都是“二進制”嘛,當時我的想法是難道是跟JDK處理String一樣的操作,在JDK8之前,String本身是藉助於char來存儲的:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}

到了JDK 8之後, JDK藉助byte來存儲字符串:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    @Stable
    private final byte[] value;
}    

畢竟一個char佔兩個字節, 一個byte只佔一個字節,因為我之前用程序連接過充電樁,接收充電樁的報文,給的報文都是byte類型的,byte更小,像String就帶了一些額外的信息,所以我猜想,是這個意義上的二進制,但是這只是猜想,我想過用抓包工具去驗證我的猜想,但是發現抓包工具我用的並部署,再加上HTTP/2.0都是加密報文,抓包挺麻煩的,我也想過看HTTP Client的源碼,但是這兩個見效都太慢了,最近偶然翻看MongDB的文檔,翻到了這方面的説明,這個問題就有了答案。其實HTTP也對上面的二進制進行了解釋:

Why is HTTP/2 binary?

Binary protocols are more efficient to parse, more compact “on the wire”, and most importantly, they are much less error-prone, compared to textual protocols like HTTP/1.x, because they often have a number of affordances to “help” with things like whitespace handling, capitalization, line endings, blank lines and so on.

二進制協議相對於文本協議,比如HTTP/1.x ,解析效率、傳輸效率更高,有更好的容錯性。還提供了一些機制可以幫助處理空白字符、大小寫、空行等等。

For example, HTTP/1.1 defines four different ways to parse a message; in HTTP/2, there’s just one code path.

例如,HTTP/1.1定義了四種解析數據的方式,但是在HTTP/2, 只有一種代碼路徑。

It’s true that HTTP/2 isn’t usable through telnet, but we already have some tool support, such as a Wireshark plugin.

雖然HTTP/2已經不能再使用Telnet了,但是我們也有其他工具的支持,比如 Wireshark plugin。

所以HTTP説自己是二進制的,潛台詞是HTTP/2的數據包採取了高度結構化的格式,因為在計算機中,最終一切都是二進制形式存在。在HTTP/2中傳輸的數據會被格式化為幀(frame), 每個幀都會被分配一個流。HTTP/2的幀具備特定的格式,如下圖所示:

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

在HTTP/2中,每個幀都由兩部分組成: 幀頭(9個字節),幀頭是固定長度的,佔據9個字節,包含了關於該幀的一些信息,比如長度、類型等。幀頭後面是有效載荷,長度可變,取決於幀的類型和內容。這有點類似於TCP數據包,讀取HTTP/2幀可以遵循定義好的過程(先讀取數據長度,然後幀的類型)。相比之下,HTTP/1.1是由一個ASCII編碼的文本行組成的非結構化格式,雖然這些文本最終將以二進制形式傳輸,但是基本上它是一串字符的流,而不是明確地被分為獨立的幀。

From a user-, script-, or server- generated event, an HTTP/1.x msg is generated, and if HTTP/2 is in use, it is binary framed into an HTTP/2 stream, then sent.

HTTP/1.1的消息通過逐個字符地讀取字符來解析,直到達到換行字符為止,這種方式有點混亂,但是因為無法知道每行的長度,所以必須逐個字符進行處理。對於HTTP正文的長度可以提前知道,我們可以在HTTP頭裏獲知到這個信息。

這讓我想起了MongDB的BSON,我想BSON中的binary的語義應當和HTTP/2的binary語義是對等的,在BSON規範的官網可以看到我們的猜想是正確的:

BSON is a binary format in which zero or more ordered key/value pairs are stored as a single entity. We call this entity a document.

BSON是一種二進制格式,其中有零個或多個有序的鍵/值對被存儲為一個單一的實體,我們將這個實體稱之為文檔。

下面是一個JSON和其對應的BSON格式的示例:

{"hello": "world"} →
\x16\x00\x00\x00           // total document size 總的文檔大小 算上大小字段本身
\x02                       // 0x02 = type String 
hello\x00                  // field name 字段值
\x06\x00\x00\x00world\x00  // field value 字段值
\x00                       // 0x00 = type EOO ('end of object')

總結一下,在計算機中最終一切都是二進制格式,當我們在數據格式中看到二進制時,我們可以理解為這種存儲結構是高度結構化的 , 讀取效率更高,更為緊湊,將數據重新進行佈局。

user avatar edagarli 頭像 skychx 頭像 biubiubiu_5ea3ee0e6b5fd 頭像 async_wait 頭像 showonne 頭像
5 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.