HTTP 協議的初印象:
是基於 TCP/IP 協議的應用層協議,不涉及數據包的傳輸,主要規定了客戶端和服務器之間的通信格式,默認使用 80 端口。
1 HTTP 協議 0.9 版(1991 年)
是個弱智協議,客戶端發起請求以后,服務器只能返回 HTML 格式的字符串,不能回應別的格式。
只有一個 GET 命令:
GET /index.html
上面命令表示,TCP 連接(connection)建立后,客戶端向服務器請求(request)網頁 index.html。
服務器發送完畢,就關閉 TCP 連接。
2 HTTP 協議 1.0 版(1996 年)
與 0.9 版本相比,有以下幾點變化:
- 這個版本允許發送的內容變多了,不僅可以傳輸文字,還能傳輸圖像、視頻、二進制文件。
- 除了 GET,POST、HEAD 這兩個命令都可以用,瀏覽器和服務器的互動手段增加了。
- HTTP 請求和回應的格式也變了。每次通信都必須包括頭信息,用來描述一些元數據。
新增的功能還有:
- 狀態碼
- 多字符集支持
- 多部分發送
- 權限
- 緩存
- 內容編碼
2.1 請求形式:
<!-- 請求命令, 必須在尾部添加協議版本(HTTP/1.0)-->
GET /HTTP/1.0
<!-- 多行頭信息,描述了客戶端的情況 -->
User-Agent: Mozilla/5.0(Macintosh: Intel Mac PS X 10_10_5)
<!-- 客戶端表示自己可以接受任何格式的數據 -->
Accept:*/*
2.2 回應格式
<!-- 頭信息 -->
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 137582
Expires:Thu, 05 Dec 1997 16:00:00 GMT
Server: Apache 0.84
<!-- 數據 -->
<html>
<body>Hello World</body>
</html>
其中,頭信息的第一行是 “協議的版本 + 狀態碼(status code) + 狀態描述”
2.3 Content-Type 字段
Content-Type 字段的作用是告訴客戶端從服務端返回的數據是什么格式。
常見的 ContentType:
- text/plain
- text/html
- text/css
- image/jpeg
- image/png
- image/svg+xml
- audio/mp4
- video/mp4
- application/javascript
- application/pdf
- application/zip
- application/atom+xml
數據類型的構成包括一級類型和二級類型,中間用斜杠隔開。這些類型都被稱為 MIME type。
MIME type 不僅用於 HTTP 請求,也可以用於別的地方,比如 HTML 網頁。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 等同於 -->
<meta charset="utf-8" />
2.4 數據壓縮
客戶端在請求的時候,用 Accept - Encoding 字段說明自己可以接受哪些壓縮方法。
Accept-Encoding: gzip, deflate
而服務器使用 Content-Encoding 字段說明數據的壓縮方法。
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
2.5 HTTP/1.0 協議的缺點
每個 TCP 連接只能發送一個請求,數據發送完畢連接就關閉了,如果要請求別的資源,必須再新建一個連接。
但是 TCP 連接的成本很高,因為客戶端和服務器的三次握手,並且啟動發送的速率較慢。
有一些瀏覽器為了解決 TCP 會關閉的問題,采用了非標准的 Connection 字段。
瀏覽器請求和服務器回應都帶有這個字段:
Connection: keep-alive
這樣的話,一個可以復用的連接建立了,直到客戶端或者服務器主動關閉連接。但是不同的瀏覽器實現不同,這不是一個好的解決辦法。
3 HTTP 協議 1.1 版(1997 年)
3.1 持久連接
相比 1.0 版,1.1 版最大的變化是保持連接,即默認 TCP 連接不關閉,可以被多個請求復用,不用再聲明 Connection: keep-alive。
那怎么關閉呢?如果客戶端或者服務器發現對方一段時間內沒有活動,就可以主動關閉連接。不過最好還是客戶端在發送最后一個請求的時候,發送一個 Connection:close,明確要求服務器關閉 TCP 連接。
另外,對於同一個域名,大多數瀏覽器允許同時建立 6 個持久連接。
3.2 管道機制
管道機制: 在同一個 TCP 連接里,客戶端可以同時發送多個請求。
以前的做法是客戶端先發送 A 請求,然后再發送 B 請求。管道機制是允許瀏覽器同時發送 A 請求和 B 請求,但是服務器還是按照順序進行的,先回應 A 請求再回應 B 請求。
3.3 區分數據包
一個 TCP 連接現在可以傳送多個回應,勢必就要有一種機制,區分數據包是屬於哪一個回應的。
Content-length 字段的作用,聲明本次回應的數據長度。
Content-Length: 3495
上面的字段告訴客戶端,本次回應的長度是 3495 個字節,后面的字節就屬於下一個回應了。
所以,使用 Content-Length 字段的前提條件是,服務器發送回應之前,必須知道回應的數據長度。
而在 1.0 版本中,這個字段可以存在,也可以不存在,因為服務端關閉了 TCP 連接說明數據包已經全了。
3.4 其他功能
1.1 版新增了很多動詞方法:PUT、PATCH、HEAD、OPTIONS、DELETE。
客戶端的請求頭信息增加了 Host 字段,用來指定服務器的域名。
這個字段可以將請求發往同一台服務器的不同網站。
如:
Host: www.example.com
3.5 缺點
"隊頭堵塞": 在 1.1 版協議中,雖然可以在一個 TCP 中發送多個請求,服務器只有處理完一個回應,才會進行下一個回應。要是前面的回應特別慢,后面就會有許多請求排隊等着。
為了避免這個問題,只有兩種方法:一是減少請求數,二是同時多開持久連接。
所以產生了很多網頁優化技巧,比如合並腳本和樣式表,將圖片嵌入 CSS 代碼,域名分片等,但是 HTTP 協議設計得更好的話,這些工作完全沒有必要做的。
4 SPDY 協議 (2009 年)
2009 年,谷歌公開了自己搞的 SPDY 協議,主要為了解決 HTTP/1.1 效率不高的問題。
這個協議最終被當作了 HTTP/2 的基礎。
5 HTTP 協議 2 版
HTTP/2 協議不叫 HTTP/2.0,這是因為標准委員會不在打算發布子版本,下一個新的版本將會是 HTTP/3。
5.1 二進制協議
HTTP 協議的頭肯定是文本(ASCII 碼),數據體可以是文本,也可以是二進制。
但 HTTP/2 協議頭和數據體都是二進制,是一個徹徹底底的二進制協議。
這里的頭和數據體都換了一個身份叫做 “幀”:頭信息幀和數據幀。
二進制協議的好處就是可以定義額外的幀,HTTP/2 有近十種幀,為將來的高級應用打好了基礎,因為二進制解析比文本解析更方便。
5.2 多工
HTTP/2 協議復用 TCP 連接,也就是說客戶端和服務端可以同時發送或者回應多個請求。
比如說,在一個 TCP 里,客戶端給服務端發送了 A 和 B 兩個請求,按道理說應該先處理 A 請求,但是服務端發現 A 請求有點費勁兒,就先把 A 請求中處理好的發出去,接着回應 B 請求,完事兒以后再發送 A 請求中剩下的部分。
像這樣雙向,實時的通信,就叫做多工(Multiplexing)。
5.3 數據流
HTTP/2 的數據包不是按照順序發送的,同一個連接里的數據包可能屬於多個請求。所以需要一個編號,這個編號指明數據包屬於哪一個請求或者回應。
這里有一個概念叫做數據流,我們把一個請求或者回應的所有數據包合在一起稱為一個數據流。每個數據流都有一個獨一無二的編號,數據包發送的時候都必須標定數據流編號。
客戶端請求的數據流編號一律為基數,而服務端回應的數據流編號為偶數。
在 HTTP/1.1 中終止請求的方式只能是關閉 TCP 連接,而現在可以發送一個信號:(RST_STREAM 幀),取消這個數據流。
也就是說 HTTP/2 可以取消某一次請求,同時能保證 TCP 連接打開,可以被其他請求所使用。
此外,客戶端可以指定數據流的優先級,優先級高的服務器越早響應。
5.4 頭信息壓縮
因為協議不帶有狀態,每次請求都必須附上所有信息。有一些信息是重復的,這樣會影響數據傳輸速度,浪費帶寬。
HTTP/2 采用頭信息壓縮機制可以減少不利影響。首先是將信息壓縮后再發送(gzip 或者 compress),其次是客戶端 和服務器同時維護一張頭信息表,這個表很神奇的地方在於可以利用索引進行管理,只要我們發送索引就可以表示我們傳輸的信息,這樣就能夠提高速度。
5.5 主動推送
HTTP/2 允許服務器在沒有經過允許的情況下,可以主動向客戶端推送資源 。這個過程叫做服務器推送(server push)。
舉一個例子,別發送郵件給我們的時候,郵箱會冒個彈框出來告訴我們有人往郵箱里投遞了一封郵件。注意,此時我們並沒有刷新網頁。
服務器推送還可能用到消息提醒,靜態資源自動推送到服務端等場景。