半小時搞懂 HTTP、HTTPS和HTTP2

HTTP 概述

HTTP 超文本傳輸​​協議是位於 TCP/IP 體系結構中的應用層協議,它是萬維網的數據通信的基礎。

當我們訪問一個網站時,需要通過統一資源定位符 URL 來定位服務器並獲取資源。

<協議>://<域名>:<端口>/<路徑>

一個 URL 的一般形式通常如上所示(http://test.com/index.html ),現在最常用的協議就是 HTTP,HTTP 的默認端口是 80,通常可以省略。
在這裏插入圖片描述

HTTP/1.1

HTTP/1.1 是目前使用最廣泛的版本,一般沒有特別標明版本都是指 HTTP/1.1。

HTTP 連接建立過程

我們來看一下在瀏覽器輸入 URL 後獲取 HTML 頁面的過程。

  1. 先通過 DNS 查詢將域名轉換爲 IP 地址。即將 test.com 轉換爲 221.239.100.30 這一過程。
  2. 通過三次握手(詳情見文末)建立 TCP 連接。
  3. 發起 HTTP 請求。
  4. 目標服務器接收到 HTTP 請求並處理。
  5. 目標服務器往瀏覽器發回 HTTP 響應。
  6. 瀏覽器解析並渲染頁面。

下圖中的 RTT 爲往返時延。
在這裏插入圖片描述

HTTP 連接拆除過程

所有 HTTP 客戶端(瀏覽器)、服務器都可在任意時刻關閉 TCP 連接。通常會在一條報文結束時關閉連接,但出錯的時候,也可能在首部行的中間或其他任意位置關閉連接。

由於 HTTP 是基於 TCP 的,所以在經歷 TCP 四次揮手(詳情見文末)過程後,連接就正常關閉了。

HTTP 報文格式

HTTP 報文由請求行、首部、實體主體組成,它們之間由 CRLF(回車換行符) 分隔開。

注意:實體包括首部(也稱爲實體首部)和實體主體,sp 即是空格 space
在這裏插入圖片描述
請求行和首部是由 ASCII 文本組成的,實體主體是可選的,可以爲空也可以是任意二進制數據。

請求報文和響應報文的格式基本相同。

請求報文格式

<method> <request-URL> <version>
<headers>
<entity-body>

響應報文格式

<version> <status> <reason-phrase>
<headers>
<entity-body>

一個請求或響應報文由以下字段組成

  1. 請求方法,客戶端希望服務器對資源執行的動作。
  2. 請求 URL,命名了所請求的資源。
  3. 協議版本,報文所使用的 HTTP 版本。
  4. 狀態碼,這三位數字描述了請求過程中所發生的情況。
  5. 原因短語,數字狀態碼的可讀版本(例如上面的響應示例跟在 200 後面的 OK,一般按規範寫最好)。
  6. 首部,可以有零或多個首部。
  7. 實體的主體部分,可以爲空也可以包含任意二進制數據。

一個 HTTP 請求示例

GET /2.app.js HTTP/1.1
Host: 118.190.217.8:3389
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Accept: */*
Referer: http://118.190.217.8:3389/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

一個 HTTP 響應示例

HTTP/1.1 200 OK
X-Powered-By: Express
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 07 Mar 2020 03:52:30 GMT
ETag: W/"253e-170b31f7de7"
Content-Type: application/javascript; charset=UTF-8
Vary: Accept-Encoding
Content-Encoding: gzip
Date: Fri, 15 May 2020 05:38:05 GMT
Connection: keep-alive
Transfer-Encoding: chunked

方法

方法 描述
GET 從服務器獲取一份文檔
HEAD 只從服務器獲取文檔的頭部
POST 向服務器發送需要處理的數據
PUT 將請求的數據部分存儲在服務器上
TRACE 對可能經過代理服務器傳送到服務器上去的報文進行追蹤
OPTIONS 決定可以在服務器上執行哪些方法
DELETE 從服務器上刪除一份文檔
GET 和 HEAD

其中 GET 和 HEAD 被稱爲安全方法,因爲它們是冪等的(如果一個請求不管執行多少次,其結果都是一樣的,這個請求就是冪等的),類似於 POST 就不是冪等的。

HEAD 方法和 GET 方法很類似,但服務器在響應中只返回首部。這就允許客戶端在未獲取實際資源的情況下,對資源的首部進行檢查。使用 HEAD,可以:

  1. 在不獲取資源的情況下了解資源的情況。
  2. 通過查看響應狀態碼,看看某個對象是否存在。
  3. 通過查看首部,瞭解測試資源是否被修改了。

服務器開發者必須確保返回的首部與 GET 請求所返回的首部完全相同。遵循 HTTP/1.1 規範,就必須實現 HEAD 方法。

PUT

與 GET 方法從服務器讀取文檔相反,PUT 方法會向服務器寫入文檔。PUT 方法的語義就是讓服務器用請求的主體部分來創建一個由所請求的 URL 命名的新文檔。 如果那個文檔已存在,就覆蓋它。因爲 PUT 允許用戶對內容進行修改,所以服務器要求在執行 PUT 之前,要用密碼登錄。

POST

POST 方法通常用來向服務器發送表單數據。

TRACE

客戶端發起一個請求時,這個請求可能要穿過路由器、防火牆、代理、網關等。每個中間節點都可能會修改原始的 HTTP 請求,TRACE 方法允許客戶端在最終發起請求時,看看它變成了什麼樣子。

TRACE 請求會在目的服務器端發起一個“環回”診斷。行程最後一站的服務器會彈回一條 TRACE 響應,並在響應主體中攜帶它收到的原始請求報文。 這樣客戶端就可以查看在所有中間 HTTP 應用程序組成的請求/響應鏈上,原始報文是否被毀壞或修改過。
在這裏插入圖片描述
TRACE 方法主要用於診斷,用於驗證請求是否如願穿過了請求/響應鏈。它也是一種工具,用來查看代理和其他應用程序對用戶請求所產生的效果。 TRACE 請求中不能帶有實體的主體部分。TRACE 響應的實體主體部分包含了響應服務器收到的請求的精確副本。

OPTIONS

OPTIONS 方法請求 Web 服務器告知其支持的各種功能。

DELETE

DELETE 方法就是讓服務器刪除請求 URL 所指定的資源。

狀態碼

整體範圍 已定義範圍 分類
100~199 100~101 信息提示
200~299 200~206 成功
300~399 300~305 重定向
400~499 400~415 客戶端錯誤
500~599 500~505 服務器錯誤
300~399 重定向狀態碼

重定向狀態碼要麼告訴客戶端使用替代位置來訪問他們感興趣的資源,要麼提供一個替代的響應而不是資源的內容。 如果資源已被移動,可以發送一個重定向狀態碼和一個可選的 Location 首部來告知客戶端資源已被移走,以及現在在哪裏可以找到它。這樣,瀏覽器可以在不打擾使用者的情況下,透明地轉入新的位置。

400~499 客戶端錯誤狀態碼

有時客戶端會發送一些服務器無法處理的東西,例如格式錯誤的請求報文、一個不存在的 URL。

500~599 服務器錯誤狀態碼

有時客戶端發送了一條有效請求,服務器自身卻出錯了。

首部

首部和方法共同配合工作,決定了客戶端和服務器能做什麼事情。

首部分類

  1. 通用首部,可以出現在請求或響應報文中。
  2. 請求首部,提供更多有關請求的信息。
  3. 響應首部,提供更多有關響應的信息。
  4. 實體首部,描述主體的長度和內容,或者資源自身。
  5. 擴展首部,規範中沒有定義的新首部。
通用首部

有些首部提供了與報文相關的最基本信息,它們被稱爲通用首部。以下是一些常見的通用首部:
在這裏插入圖片描述

請求首部

請求首部是隻在請求報文中有意義的首部,用於說明請求的詳情。以下是一些常見的請求首部:
在這裏插入圖片描述

響應首部

響應首部讓服務器爲客戶端提供了一些額外的信息。

實體首部

實體首部提供了有關實體及其內容的大量信息,從有關對象類型的信息,到能夠對資源使用的各種有效的請求方法。
例如內容首部,提供了與實體內容有關的特定信息,說明了其類型、尺寸以及處理它所需的其他有用信息。
另外,通用的緩存首部說明了如何或什麼時候進行緩存。實體的緩存首部提供了與被緩存實體有關的信息。
在這裏插入圖片描述

性能優化

1. 減少 HTTP 請求

每發起一個 HTTP 請求,都得經歷三次握手建立 TCP 連接,如果連接只用來交換少量數據,這個過程就會嚴重降低 HTTP 性能。所以我們可以將多個小文件合成一個大文件,從而減少 HTTP 請求次數。

其實由於持久連接(重用 TCP 連接,以消除連接及關閉時延;HTTP/1.1 默認開啓持久連接)的存在,每個新請求不一定都需要建立一個新的 TCP 連接。但是,瀏覽器處理完一個 HTTP 請求才能發起下一個,所以在 TCP 連接數沒達到瀏覽器規定的上限時,還是會建立新的 TCP 連接。從這點來看,減少 HTTP 請求仍然是有必要的。

2. 靜態資源使用 CDN

內容分發網絡(CDN)是一組分佈在多個不同地理位置的 Web 服務器。我們都知道,當服務器離用戶越遠時,延遲越高。CDN 就是爲了解決這一問題,在多個位置部署服務器,讓用戶離服務器更近,從而縮短請求時間。

3. 善用緩存

爲了避免用戶每次訪問網站都得請求文件,我們可以通過添加 Expires 頭來控制這一行爲。Expires 設置了一個時間,只要在這個時間之前,瀏覽器都不會請求文件,而是直接使用緩存。

不過這樣會產生一個問題,當文件更新了怎麼辦?怎麼通知瀏覽器重新請求文件?

可以通過更新頁面中引用的資源鏈接地址,讓瀏覽器主動放棄緩存,加載新資源。

具體做法是把資源地址 URL 的修改與文件內容關聯起來,也就是說,只有文件內容變化,纔會導致相應 URL 的變更,從而實現文件級別的精確緩存控制。什麼東西與文件內容相關呢?我們會很自然的聯想到利用數據摘要要算法對文件求摘要信息,摘要信息與文件內容一一對應,就有了一種可以精確到單個文件粒度的緩存控制依據了。

參考資料:

4. 壓縮文件

壓縮文件可以減少文件下載時間,讓用戶體驗性更好。

gzip 是目前最流行和最有效的壓縮方法。可以通過向 HTTP 請求頭中的 Accept-Encoding 頭添加 gzip 標識來開啓這一功能。當然,服務器也得支持這一功能。

舉個例子,我用 Vue 開發的項目構建後生成的 app.js 文件大小爲 1.4MB,使用 gzip 壓縮後只有 573KB,體積減少了將近 60%。

5. 域名拆分

由於瀏覽器對同一域名有併發 TCP 連接數量的限制,所以將網頁的資源分配到不同的域名下,可以突破瀏覽器的限制,從而建立更多的 TCP 連接。

HTTPS

HTTPS 是最流行的 HTTP 安全形式,由網景公司首創,所有主要的瀏覽器和服務器都支持此協議。 使用 HTTPS 時,所有的 HTTP 請求和響應數據在發送之前,都要進行加密。加密可以使用 SSL 或 TLS。
在這裏插入圖片描述
SSL/TLS 協議作用在 HTTP 協議之下,對於上層應用來說,原來的發送/接收數據流程不變,這就很好地兼容了老的 HTTP 協議。由於 SSL/TLS 差別不大,下面統一使用 SSL。

要想了解 HTTPS 爲何安全,還得繼續瞭解一下這些概念:加密算法摘要算法數字簽名數字證書

加密算法

對稱密鑰密碼體制

對稱密鑰密碼體制,即加密密鑰和解密密鑰是使用相同的密碼體制。對稱密鑰加密技術的缺點之一就是發送者和接收者在對話之前,一定要有一個共享的密鑰,所以不太安全。

公鑰密碼體制

公鑰密碼體制使用不同的加密密鑰與解密密鑰。公鑰密碼體制產生的主要原因有兩個:一是對稱密鑰密碼體制的密鑰分配問題,二是對數字簽名的需求。

在公鑰密碼體制中,加密密鑰是公開的,解密密鑰是需要保密的,加密算法和解密算法也是公開的。

公鑰密碼體制的加密和解密有如下特點:

  1. 密鑰對產生器產生出接收者 B 的一對密鑰,即加密密鑰 PK 和解密密鑰 SK。
  2. 發送者 A 用 B 的公鑰 PK 作爲加密密鑰來加密信息,B 接收後用解密密鑰 SK 解密。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uWP5p7So-1589532545350)(../imgs/is3.png)]

使用對稱密鑰時,由於雙方使用同樣的密鑰,因此在通信信道上可以進行一對一的雙向保密通信,雙方都可以用同一個密鑰加密解密。

使用公開密鑰時,在通信信道上可以是多對一的單向保密信道。即可以有多人持有 B 的公鑰,但只有 B 才能解密。

摘要算法

摘要算法的主要特徵是加密過程不需要密鑰,並且經過加密的數據無法被解密,目前可以被解密逆向的只有CRC32算法,只有輸入相同的明文數據經過相同的消息摘要算法才能得到相同的密文。

數字簽名

用加密系統對報文進行簽名,以說明是誰編寫的報文,同時證明報文未被篡改過,這種技術稱爲數字簽名

數字簽名是附加在報文上的特殊加密校驗碼。使用數字簽名的好處有:

  1. 簽名可以證明是作者編寫了這條報文。只有作者纔會有最機密的私有密鑰,因此,只有作者才能計算出這些校驗和。
  2. 簽名可以防止報文被篡改,如果有人在報文傳輸過程中對其進行了修改,校驗和就不再匹配了。

數字簽名通常是用非對稱公開密鑰技術產生的。
在這裏插入圖片描述
看上圖,任何人都能用 A 的公鑰 PK 對密文進行 E 運算後得到 A 發送的明文。可見這種通信並非爲了保密,而是爲了進行簽名和核實簽名,即確認此信息是 A 發送的。 但上述過程僅對報文進行了簽名,對報文 X 本身卻未保密,所以要採用下圖的方法,同時實現祕密通信和數字簽名。
在這裏插入圖片描述

數字證書

假如你想訪問一個網站,怎麼確保對方給你的公鑰是你想訪問的網站的公鑰,而不是被中間人篡改過的?

數字證書的出現就是爲了解決這個問題,它是由數字證書認證機構頒發的,用來證明公鑰擁有者的身份。換句話說,數字證書的作用就相當於人的身份證,身份證證明了張三就是張三,而不是別人。

數字證書一般包含以下內容

  1. 對象的名稱(人、服務器、組織等);
  2. 過期時間;
  3. 證書發佈者(由誰爲證書擔保);
  4. 來自證書發佈者的數字簽名;
  5. 對象的公鑰;
  6. 對象和所用簽名算法的描述性信息。

任何人都可以創建一個數字證書,但由誰來擔保纔是重點。

數字證書的數字簽名計算過程

  1. 用摘要算法對數字證書的內容計算出摘要;
  2. 用數字證書的私鑰對摘要進行加密得到數字簽名。
    在這裏插入圖片描述

瀏覽器收到證書時,會對簽名頒發機構進行驗證,如果頒發機構是個很有權威的公共簽名機構,瀏覽器可能就知道其公開密鑰了(瀏覽器會預裝很多簽名頒發機構的證書)。如果對簽名頒發機構一無所知,瀏覽器通常會向用戶顯示一個對話框,看看他是否相信這個簽名發佈者。

因爲數字證書的公鑰是公開的,任何人都可以用公鑰解密出數字證書的數字簽名的摘要,然後再用同樣的摘要算法對證書內容進行摘要計算,將得出的摘要和解密後的摘要作對比,如果內容一致則說明這個證書沒有被篡改過,可以信任。

這個過程是建立在被大家所認可的證書機構之上得到的公鑰,所以這是一種安全的方式。
在這裏插入圖片描述

HTTPS 連接建立過程

HTTPS 連接建立過程和 HTTP 差不多,區別在於 HTTP(默認端口 80) 請求只要在 TCP 連接建立後就可以發起,而 HTTPS(默認端口 443) 在 TCP 連接建立後,還需要經歷 SSL 協議握手,成功後才能發起請求。
在這裏插入圖片描述
在這裏插入圖片描述

HTTP/2

HTTP/2 是 HTTP/1.x 的擴展,而非替代。所以 HTTP 的語義不變,提供的功能不變,HTTP 方法、狀態碼、URL 和首部字段等這些核心概念也不變。

之所以要遞增一個大版本到 2.0,主要是因爲它改變了客戶端與服務器之間交換數據的方式。HTTP 2.0 增加了新的二進制分幀數據層,而這一層並不兼容之前的 HTTP 1.x 服務器及客戶端——是謂 2.0。

HTTP/2 連接建立過程

現在的主流瀏覽器 HTTP/2 的實現都是基於 SSL/TLS 的,也就是說使用 HTTP/2 的網站都是 HTTPS 協議的,所以本文只討論基於 SSL/TLS 的 HTTP/2 連接建立過程。

基於 SSL/TLS 的 HTTP/2 連接建立過程和 HTTPS 差不多。在 SSL/TLS 握手協商過程中,客戶端在 ClientHello 消息中設置 ALPN(應用層協議協商)擴展來表明期望使用 HTTP/2 協議,服務器用同樣的方式回覆。通過這種方式,HTTP/2 在 SSL/TLS 握手協商過程中就建立起來了。

HTTP/1.1 的問題

1. 隊頭阻塞

在 HTTP 請求應答過程中,如果出現了某種情況,導致響應一直未能完成,那後面所有的請求就會一直阻塞着,這種情況叫隊頭阻塞。

2. 低效的 TCP 利用

由於 TCP 慢啓動機制,導致每個 TCP 連接在一開始的時候傳輸速率都不高,在處理多個請求後,纔會慢慢達到“合適”的速率。對於請求數據量很小的 HTTP 請求來說,這種情況就是種災難。

3. 臃腫的消息首部

HTTP/1.1 的首部無法壓縮,再加上 cookie 的存在,經常會出現首部大小比請求數據大小還大的情況。

4. 受限的優先級設置

HTTP/1.1 無法爲重要的資源指定優先級,每個 HTTP 請求都是一視同仁。

在繼續討論 HTTP/2 的新功能之前,先把 HTTP/1.1 的問題列出來是有意義的。因爲 HTTP/2 的某些新功能就是爲了解決上述某些問題而產生的。

二進制分幀層

HTTP/2 是基於幀的協議。採用分幀是爲了將重要信息封裝起來,讓協議的解析方可以輕鬆閱讀、解析並還原信息。

而 HTTP/1.1 是以文本分隔的。解析 HTTP/1.1 不需要什麼高科技,但往往速度慢且容易出錯。你需要不斷地讀入字節,直到遇到分隔符 CRLF 爲止,同時還要考慮不守規矩的客戶端,它只會發送 LF。

解析 HTTP/1.1 的請求或響應還會遇到以下問題:

  1. 一次只能處理一個請求或響應,完成之前不能停止解析。
  2. 無法預判解析需要多少內存。

HTTP/2 有了幀,處理協議的程序就能預先知道會收到什麼,並且 HTTP/2 有表示幀長度的字段。
在這裏插入圖片描述

幀結構

 +-----------------------------------------------+
 |                 Length (24)                   |
 +---------------+---------------+---------------+
 |   Type (8)    |   Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 Stream Identifier (31)                      |
 +=+=============================================================+
 |                   Frame Payload (0...)                      ...
 +---------------------------------------------------------------+
名稱 長度 描述
Length 3 字節 表示幀負載的長度,取值範圍爲 (2 的 14 次方)至 (2 的 24 次方 - 1)。(2 的 14 次方) 16384 字節是默認的最大幀大小,如果需要更大的幀,必須在 SETTINGS 幀中設置
Type 1 字節 當前幀類型(見下表)
Flags 1 字節 具體幀類型的標識
R 1 位 保留位,不要設置,否則可能會帶來嚴重的後果
Stream Identifier 31 位 每個流的唯一 ID
Frame Payload 長度可變 真實的幀內容,長度是在 Length 字段中設置的

由於 HTTP/2 是分幀的,請求和響應都可以多路複用,有助於解決類似類似隊頭阻塞的問題。

幀類型

名稱 ID 描述
DATA 0x0 傳輸流的核心內容
HEADERS 0x1 包含 HTTP 首部,和可選的優先級參數
PRIORITY 0x2 指示或更改流的優先級和依賴
RST_STREAM 0x3 允許一端停止流(通常由於錯誤導致的)
SETTINGS 0x4 協商連接級參數
PUSH_PROMISE 0x5 提示客戶端,服務器要推送些東西
PING 0x6 測試連接可用性和往返時延(RTT)
GOAWAY 0x7 告訴另一端,當前的端已結束
WINDOW_UPDATE 0x8 協商一端將要接收多少字節(用於流量控制)
CONTINUATION 0x9 用以擴展 HEADERS 模塊

多路複用

在 HTTP/1.1 中,如果客戶端想發送多個並行的請求,那麼必須使用多個 TCP 連接。

而 HTTP/2 的二進制分幀層突破了這一限制,所有的請求和響應都在同一個 TCP 連接上發送:客戶端和服務器把 HTTP 消息分解成多個幀,然後亂序發送,最後在另一端再根據流 ID 重新組合起來。

這個機制爲 HTTP 帶來了巨大的性能提升,因爲:

  • 可以並行交錯地發送請求,請求之間互不影響;
  • 可以並行交錯地發送響應,響應之間互不干擾;
  • 只使用一個連接即可並行發送多個請求和響應;
  • 消除不必要的延遲,從而減少頁面加載的時間;
  • 不必再爲繞過 HTTP 1.x 限制而多做很多工作;

在這裏插入圖片描述

HTTP/2 規範對流的定義是:HTTP/2 連接上獨立的、雙向的幀序列交換。如果客戶端想要發出請求,它會開啓一個新流,然後服務器在這個流上回復。 由於有分幀,所以多個請求和響應可以交錯,而不會互相阻塞。流 ID 用來標識幀所屬的流。

客戶端到服務器的 HTTP/2 連接建立後,通過發送 HEADERS 幀來啓動新的流。如果首部需要跨多個幀,可能還會發送 CONTINUATION 幀。該 HEADERS 幀可能來自請求或響應。 後續流啓動的時候,會發送一個帶有遞增流 ID 的新 HEADERS 幀。

消息

HTTP 消息泛指 HTTP 請求或響應,消息由一或多個幀組成,這些幀可以亂序發送,然後再根據每個幀首部的流 ID 重新組裝。

一個消息至少由 HEADERS 幀(它初始化流)組成,並且可以另外包含 CONTINUATION 和 DATA 幀,以及其他的 HEADERS 幀。
在這裏插入圖片描述
HTTP/1.1 的請求和響應部分都分成消息首部和消息體兩部分;HTTP/2 的請求和響應分成 HEADERS 幀和 DATA 幀。

優先級

把 HTTP 消息分解爲很多獨立的幀之後,就可以通過優化這些幀的交錯和傳輸順序,進一步提升性能。

通過 HEADERS 幀和 PRIORITY 幀,客戶端可以明確地和服務器溝通它需要什麼,以及它需要這些資源的順序。具體來講,服務器可以根據流的優先級,控制資源分配(CPU、內存、帶寬),而在響應數據準備好之後,優先將最高優先級的幀發送給客戶端。

流量控制

在同一個 TCP 連接上傳輸多個數據流,就意味着要共享帶寬。標定數據流的優先級有助於按序交付,但只有優先級還不足以確定多個數據流或多個連接間的資源分配。

爲解決這個問題,HTTP/2 爲數據流和連接的流量控制提供了一個簡單的機制:

  • 流量控制基於每一跳進行,而非端到端的控制;
  • 流量控制基於 WINDOW_UPDATE 幀進行,即接收方廣播自己準備接收某個數據流的多少字節,以及對整個連接要接收多少字節;
  • 流量控制窗口大小通過 WINDOW_UPDATE 幀更新,這個字段指定了流 ID 和窗口大小遞增值;
  • 流量控制有方向性,即接收方可能根據自己的情況爲每個流乃至整個連接設置任意窗口大小;
  • 流量控制可以由接收方禁用,包括針對個別的流和針對整個連接。

HTTP/2 連接建立之後,客戶端與服務器交換 SETTINGS 幀,目的是設置雙向的流量控制窗口大小。除此之外,任何一端都可以選擇禁用個別流或整個連接的流量控制。

服務器推送

HTTP/2 新增的一個強大的新功能,就是服務器可以對一個客戶端請求發送多個響應。換句話說,除了對最初請求的響應外,服務器還可以額外向客戶端推送資源,而無需客戶端明確地請求。
在這裏插入圖片描述
爲什麼需要這樣一個機制呢?通常的 Web 應用都由幾十個資源組成,客戶端需要分析服務器提供的文檔才能逐個找到它們。那爲什麼不讓服務器提前就把這些資源推送給客戶端,從而減少額外的時間延遲呢?服務器已經知道客戶端下一步要請求什麼資源了,這時候服務器推送即可派上用場。

另外,客戶端也可以拒絕服務器的推送。

首部壓縮

HTTP/1.1 存在的一個問題就是臃腫的首部,HTTP/2 對這一問題進行了改進,可以對首部進行壓縮。
在一個 Web 頁面中,一般都會包含大量的請求,而其中有很多請求的首部往往有很多重複的部分。

例如有如下兩個請求:

:authority: unpkg.zhimg.com
:method: GET
:path: /[email protected]/dist/zap.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
:authority: zz.bdstatic.com
:method: GET
:path: /linksubmit/push.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36

從上面兩個請求可以看出來,有很多數據都是重複的。如果可以把相同的首部存儲起來,僅發送它們之間不同的部分,就可以節省不少的流量,加快請求的時間。

HTTP/2 在客戶端和服務器端使用“首部表”來跟蹤和存儲之前發送的鍵-值對,對於相同的數據,不再通過每次請求和響應發送。

下面再來看一個簡化的例子,假設客戶端按順序發送如下請求首部:

Header1:foo
Header2:bar
Header3:bat

當客戶端發送請求時,它會根據首部值創建一張表:

索引 首部名稱
62 Header1 foo
63 Header2 bar
64 Header3 bat

如果服務器收到了請求,它會照樣創建一張表。
當客戶端發送下一個請求的時候,如果首部相同,它可以直接發送這樣的首部塊:

62 63 64

服務器會查找先前建立的表格,並把這些數字還原成索引對應的完整首部。

性能優化

使用 HTTP/2 代替 HTTP/1.1,本身就是一種巨大的性能提升。
這小節要聊的是在 HTTP/1.1 中的某些優化手段,在 HTTP/2 中是不必要的,可以取消的。

取消合併資源

在 HTTP/1.1 中要把多個小資源合併成一個大資源,從而減少請求。而在 HTTP/2 就不需要了,因爲 HTTP/2 所有的請求都可以在一個 TCP 連接發送。

取消域名拆分

取消域名拆分的理由同上,再多的 HTTP 請求都可以在一個 TCP 連接上發送,所以不需要採取多個域名來突破瀏覽器 TCP 連接數限制這一規則了。

TCP 三次握手和四次揮手

由於 HTTP 是基於 TCP 的,所以打算在文末補充一下 TCP 連接建立和拆除的過程。

在繼續講解之前,需要了解一些 TCP 的字段和標誌位:

  1. 32 比特的序號字段和確認號字段,TCP 字節流每一個字節都按順序編號。確認號是接收方期望從發送方收到的下一字節的序號。
  2. ACK 標誌位,用於指示確認字段中的值是有效的 ACK=1 有效,ACK=0 無效。
  3. SYN 標誌位,用於連接建立,SYN 爲 1 時,表明這是一個請求建立連接報文。
  4. FIN 標誌位,用於連接拆除,FIN 爲 1 時,表明發送方數據已發送完畢,並要求釋放連接。

在這裏插入圖片描述

TCP 三次握手建立連接

TCP 標準規定,ACK 報文段可以攜帶數據,但不攜帶數據就不用消耗序號。

  1. 客戶端發送一個不包含應用層數據的 TCP 報文段,首部的 SYN 置爲 1,隨機選擇一個初始序號(一般爲 0)放在 TCP 報文段的序號字段中。(SYN 爲 1 的時候,不能攜帶數據,但要消耗掉一個序號)
  2. TCP 報文段到達服務器主機後,提取報文段,爲該 TCP 連接分配緩存和變量,並向客戶端發送允許連接的 TCP 報文段(不包含應用層數據)。這個報文段的首部包含 3 個信息:SYN 置爲 1;確認號字段置爲客戶端的序號 + 1;隨機選擇自己的初始序號(一般爲 0)。
  3. 收到服務器的 TCP 響應報文段後,客戶端也要爲該 TCP 連接分配緩存和變量,並向服務器發送一個報文段。這個報文段將服務器端的序號 + 1 放置在確認號字段中,用來對服務器允許連接的報文段進行響應,因爲連接已經建立,所以 SYN 置爲 0。最後一個階段,報文段可以攜帶客戶到服務器的數據。並且以後的每一個報文段,SYN 都置爲 0。

下圖是一個具體的示例:
在這裏插入圖片描述

TCP 四次揮手拆除連接

FIN 報文段即使不攜帶數據,也要消耗序號。

  1. 客戶端發送一個 FIN 置爲 1 的報文段。
  2. 服務器回送一個確認報文段。
  3. 服務器發送 FIN 置爲 1 的報文段。
  4. 客戶端回送一個確認報文段。

參考資料

更多文章,敬請關注

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