作者:小林coding
圖解計算機基礎網站:https://xiaolincoding.com
大家好,我是小林,我最開始寫的第一篇圖解文章就是這篇:
那時候我也就不到 100 讀者,如今這篇閱讀都快 2 萬了。
當時這篇有些地方沒有解釋到位,然後我周末抽時間把一些沒解釋清楚的地方重寫了,而且還增加 HTTP 緩存技術 方面的面試題,文章的内容相比以前多了 5000 +字和 10 +張圖。
不多說了,發車發車!
提綱
HTTP 基本概念
HTTP 是什麼?
HTTP 是超文本傳輸協定,也就是HyperText Transfer Protocol。
能否詳細解釋「超文本傳輸協定」?
HTTP的名字「超文本協定傳輸」,它可以拆成三個部分:
- 超文本
- 傳輸
- 協定
三個部分
1. 「協定」
在生活中,我們也能随處可見「協定」,例如:
- 剛畢業時會簽一個「三方協定」;
- 找房子時會簽一個「租房協定」;
生活中的協定,本質上與計算機中的協定是相同的,協定的特點:
- 「協」字,代表的意思是必須有兩個以上的參與者。例如三方協定裡的參與者有三個:你、公司、學校三個;租房協定裡的參與者有兩個:你和房東。
- 「議」字,代表的意思是對參與者的一種行為約定和規範。例如三方協定裡規定試用期期限、毀約金等;租房協定裡規定租期期限、每月租金金額、違約如何處理等。
針對 HTTP 協定,我們可以這麼了解。
HTTP 是一個用在計算機世界裡的協定。它使用計算機能夠了解的語言确立了一種計算機之間交流通信的規範(兩個以上的參與者),以及相關的各種控制和錯誤處理方式(行為約定和規範)。
2. 「傳輸」
所謂的「傳輸」,很好了解,就是把一堆東西從 A 點搬到 B 點,或者從 B 點 搬到 A 點。
别輕視了這個簡單的動作,它至少包含兩項重要的資訊。
HTTP 協定是一個雙向協定。
我們在上網沖浪時,浏覽器是請求方 A ,百度網站就是應答方 B。雙方約定用 HTTP 協定來通信,于是浏覽器把請求資料發送給網站,網站再把一些資料傳回給浏覽器,最後由浏覽器渲染在螢幕,就可以看到圖檔、視訊了。
請求 - 應答
資料雖然是在 A 和 B 之間傳輸,但允許中間有中轉或接力。
就好像第一排的同學想傳遞紙條給最後一排的同學,那麼傳遞的過程中就需要經過好多個同學(中間人),這樣的傳輸方式就從「A < --- > B」,變成了「A <-> N <-> M <-> B」。
而在 HTTP 裡,需要中間人遵從 HTTP 協定,隻要不打擾基本的資料傳輸,就可以添加任意額外的東西。
針對傳輸,我們可以進一步了解了 HTTP。
HTTP 是一個在計算機世界裡專門用來在兩點之間傳輸資料的約定和規範。
3. 「超文本」
HTTP 傳輸的内容是「超文本」。
我們先來了解「文本」,在網際網路早期的時候隻是簡單的字元文字,但現在「文本」的涵義已經可以擴充為圖檔、視訊、壓縮包等,在 HTTP 眼裡這些都算作「文本」。
再來了解「超文本」,它就是超越了普通文本的文本,它是文字、圖檔、視訊等的混合體,最關鍵有超連結,能從一個超文本跳轉到另外一個超文本。
HTML 就是最常見的超文本了,它本身隻是純文字檔案,但内部用很多标簽定義了圖檔、視訊等的連結,再經過浏覽器的解釋,呈現給我們的就是一個文字、有畫面的網頁了。
OK,經過了對 HTTP 裡這三個名詞的詳細解釋,就可以給出比「超文本傳輸協定」這七個字更準确更有技術含量的答案:
HTTP 是一個在計算機世界裡專門在「兩點」之間「傳輸」文字、圖檔、音頻、視訊等「超文本」資料的「約定和規範」。
那「HTTP 是用于從網際網路伺服器傳輸超文本到本地浏覽器的協定 ,這種說法正确嗎?
這種說法是不正确的。因為也可以是「伺服器< -- >伺服器」,是以采用兩點之間的描述會更準确。
HTTP 常見的狀态碼有哪些?
五大類 HTTP 狀态碼
1xx
類狀态碼屬于提示資訊,是協定進行中的一種中間狀态,實際用到的比較少。
2xx
類狀态碼表示伺服器成功處理了用戶端的請求,也是我們最願意看到的狀态。
- 「200 OK」是最常見的成功狀态碼,表示一切正常。如果是非
請求,伺服器傳回的響應頭都會有 body 資料。HEAD
- 「204 No Content」也是常見的成功狀态碼,與 200 OK 基本相同,但響應頭沒有 body 資料。
- 「206 Partial Content」是應用于 HTTP 分塊下載下傳或斷點續傳,表示響應傳回的 body 資料并不是資源的全部,而是其中的一部分,也是伺服器處理成功的狀态。
3xx
類狀态碼表示用戶端請求的資源發送了變動,需要用戶端用新的 URL 重新發送請求擷取資源,也就是重定向。
- 「301 Moved Permanently」表示永久重定向,說明請求的資源已經不存在了,需改用新的 URL 再次通路。
- 「302 Found」表示臨時重定向,說明請求的資源還在,但暫時需要用另一個 URL 來通路。
301 和 302 都會在響應頭裡使用字段
Location
,指明後續要跳轉的 URL,浏覽器會自動重定向新的 URL。
- 「304 Not Modified」不具有跳轉的含義,表示資源未修改,重定向已存在的緩沖檔案,也稱緩存重定向,也就是告訴用戶端可以繼續使用緩存資源,用于緩存控制。
4xx
類狀态碼表示用戶端發送的封包有誤,伺服器無法處理,也就是錯誤碼的含義。
- 「400 Bad Request」表示用戶端請求的封包有錯誤,但隻是個籠統的錯誤。
- 「403 Forbidden」表示伺服器禁止通路資源,并不是用戶端的請求出錯。
- 「404 Not Found」表示請求的資源在伺服器上不存在或未找到,是以無法提供給用戶端。
5xx
類狀态碼表示用戶端請求封包正确,但是伺服器處理時内部發生了錯誤,屬于伺服器端的錯誤碼。
- 「500 Internal Server Error」與 400 類型,是個籠統通用的錯誤碼,伺服器發生了什麼錯誤,我們并不知道。
- 「501 Not Implemented」表示用戶端請求的功能還不支援,類似“即将開業,敬請期待”的意思。
- 「502 Bad Gateway」通常是伺服器作為網關或代理時傳回的錯誤碼,表示伺服器自身工作正常,通路後端伺服器發生了錯誤。
- 「503 Service Unavailable」表示伺服器目前很忙,暫時無法響應伺服器,類似“網絡服務正忙,請稍後重試”的意思。
HTTP 常見字段有哪些?
Host 字段
用戶端發送請求時,用來指定伺服器的域名。
Host: www.A.com
有了
Host
字段,就可以将請求發往「同一台」伺服器上的不同網站。
Content-Length 字段
伺服器在傳回資料時,會有
Content-Length
字段,表明本次回應的資料長度。
Content-Length: 1000
如上面則是告訴浏覽器,本次伺服器回應的資料長度是 1000 個位元組,後面的位元組就屬于下一個回應了。
Connection 字段
Connection
字段最常用于用戶端要求伺服器使用 TCP 持久連接配接,以便其他請求複用。
HTTP/1.1 版本的預設連接配接都是持久連接配接,但為了相容老版本的 HTTP,需要指定
Connection
首部字段的值為
Keep-Alive
。
Connection: keep-alive
一個可以複用的 TCP 連接配接就建立了,直到用戶端或伺服器主動關閉連接配接。但是,這不是标準字段。
Content-Type 字段
Content-Type
字段用于伺服器回應時,告訴用戶端,本次資料是什麼格式。
在這裡插入圖檔描述
Content-Type: text/html; charset=utf-8
上面的類型表明,發送的是網頁,而且編碼是UTF-8。
用戶端請求的時候,可以使用
Accept
字段聲明自己可以接受哪些資料格式。
Accept: */*
上面代碼中,用戶端聲明自己可以接受任何格式的資料。
Content-Encoding 字段
Content-Encoding
字段說明資料的壓縮方法。表示伺服器傳回的資料使用了什麼壓縮格式
Content-Encoding: gzip
上面表示伺服器傳回的資料采用了 gzip 方式壓縮,告知用戶端需要用此方式解壓。
用戶端在請求時,用
Accept-Encoding
字段說明自己可以接受哪些壓縮方法。
Accept-Encoding: gzip, deflate
GET 與 POST
GET 和 POST 有什麼差別?
根據 RFC 規範,GET 的語義是從伺服器擷取指定的資源,這個資源可以是靜态的文本、頁面、圖檔視訊等。GET 請求的參數位置一般是寫在 URL 中,URL 規定隻能支援 ASCII,是以 GET 請求的參數隻允許 ASCII 字元 ,而且浏覽器會對 URL 的長度有限制(HTTP協定本身對 URL長度并沒有做任何規定)。
比如,你打開我的文章,浏覽器就會發送 GET 請求給伺服器,伺服器就會傳回文章的所有文字及資源。
GET 請求
根據 RFC 規範,POST 的語義是根據請求負荷(封包body)對指定的資源做出處理,具體的處理方式視資源類型而不同。POST 請求攜帶資料的位置一般是寫在封包 body 中, body 中的資料可以是任意格式的資料,隻要用戶端與服務端協商好即可,而且浏覽器不會對 body 大小做限制。
比如,你在我文章底部,敲入了留言後點選「送出」(暗示你們留言),浏覽器就會執行一次 POST 請求,把你的留言文字放進了封包 body 裡,然後拼接好 POST 請求頭,通過 TCP 協定發送給伺服器。
POST 請求
GET 和 POST 方法都是安全和幂等的嗎?
先說明下安全和幂等的概念:
- 在 HTTP 協定裡,所謂的「安全」是指請求方法不會「破壞」伺服器上的資源。
- 所謂的「幂等」,意思是多次執行相同的操作,結果都是「相同」的。
如果從 RFC 規範定義的語義來看:
- GET 方法就是安全且幂等的,因為它是「隻讀」操作,無論操作多少次,伺服器上的資料都是安全的,且每次的結果都是相同的。是以,可以對 GET 請求的資料做緩存,這個緩存可以做到浏覽器本身上(徹底避免浏覽器發請求),也可以做到代理上(如nginx),而且在浏覽器中 GET 請求可以儲存位書簽。
- POST 因為是「新增或送出資料」的操作,會修改伺服器上的資源,是以是不安全的,且多次送出資料就會建立多個資源,是以不是幂等的。是以,浏覽器一般不會緩存 POST 請求,也不能把 POST 請求儲存為書簽。
做個簡要的小結。
GET 的語義是請求擷取指定的資源。GET 方法是安全、幂等、可被緩存的。
POST 的語義是根據請求負荷(封包主體)對指定的資源做出處理,具體的處理方式視資源類型而不同。POST 不安全,不幂等,(大部分實作)不可緩存。
注意, 上面是從 RFC 規範定義的語義來分析的。
但是實際過程中,開發者不一定會按照 RFC 規範定義的語義來實作 GET 和 POST 方法。比如:
- 可以用 GET 方法實作新增或删除資料的請求,這樣實作的 GET 方法自然就不是安全和幂等。
- 可以用 POST 方法實作查詢資料的請求,這樣實作的 POST 方法自然就是安全和幂等。
曾經有個笑話,有人寫了個部落格,删除部落格用的是GET請求,他覺得沒人通路就連鑒權都沒做。然後Google伺服器爬蟲爬了一遍,他所有博文就沒了。。。
如果「安全」放入概念是指資訊是否會被洩漏的話,雖然 POST 用 body 傳輸資料,而 GET 用 URL 傳輸,這樣資料會在浏覽器位址攔容易看到,但是并不能說 GET 不如 POST 安全的。
因為 HTTP 傳輸的内容都是明文的,雖然在浏覽器位址攔看不到 POST 送出的 body 資料,但是隻要抓個包就都能看到了。
是以,要避免傳輸過程中資料被竊取,就要使用 HTTPS 協定,這樣所有 HTTP 的資料都會被加密傳輸。
GET 請求可以帶 body 嗎?
RFC 規範并沒有規定 GET 請求不能帶 body 的。理論上,任何請求都可以帶 body 的。隻是因為 RFC 規範定義的 GET 請求是擷取資源,是以根據這個語義不需要用到 body。
另外,URL 中的查詢參數也不是 GET 所獨有的,POST 請求的 URL 中也可以有參數的。
HTTP 緩存技術
HTTP 緩存有哪些實作方式?
對于一些具有重複性的 HTTP 請求,比如每次請求得到的資料都一樣的,我們可以把這對「請求-響應」的資料都緩存在本地,那麼下次就直接讀取本地的資料,不必在通過網絡擷取伺服器的響應了,這樣的話 HTTP/1.1 的性能肯定肉眼可見的提升。
是以,避免發送 HTTP 請求的方法就是通過緩存技術,HTTP 設計者早在之前就考慮到了這點,是以 HTTP 協定的頭部有不少是針對緩存的字段。
HTTP 緩存有兩種實作方式,分别是強制緩存和協商緩存。
什麼是強制緩存?
強緩存指的是隻要浏覽器判斷緩存沒有過期,則直接使用浏覽器的本地緩存,決定是否使用緩存的主動性在于浏覽器這邊。
如下圖中,傳回的是 200 狀态碼,但在 size 項中辨別的是 from disk cache,就是使用了強制緩存。
強緩存是利用下面這兩個 HTTP 響應頭部(Response Header)字段實作的,它們都用來表示資源在用戶端緩存的有效期:
-
, 是一個相對時間;Cache-Control
-
,是一個絕對時間;Expires
如果 HTTP 響應頭部同時有 Cache-Control 和 Expires 字段的話,Cache-Control的優先級高于 Expires 。
Cache-control 選項更多一些,設定更加精細,是以建議使用 Cache-Control 來實作強緩存。具體的實作流程如下:
- 當浏覽器第一次請求通路伺服器資源時,伺服器會在傳回這個資源的同時,在 Response 頭部加上 Cache-Control,Cache-Control 中設定了過期時間大小;
- 浏覽器再次請求通路伺服器中的該資源時,會先通過請求資源的時間與 Cache-Control 中設定的過期時間大小,來計算出該資源是否過期,如果沒有,則使用該緩存,否則重新請求伺服器;
- 伺服器再次收到請求後,會再次更新 Response 頭部的 Cache-Control。
什麼是協商緩存?
當我們在浏覽器使用開發者工具的時候,你可能會看到過某些請求的響應碼是
304
,這個是告訴浏覽器可以使用本地緩存的資源,通常這種通過服務端告知用戶端是否可以使用緩存的方式被稱為協商緩存。
上圖就是一個協商緩存的過程,是以協商緩存就是與服務端協商之後,通過協商結果來判斷是否使用本地緩存。
協商緩存可以基于兩種頭部來實作。
第一種:請求頭部中的
If-Modified-Since
字段與響應頭部中的
Last-Modified
字段實作,這兩個字段的意思是:
- 響應頭部中的
:标示這個響應資源的最後修改時間;Last-Modified
- 請求頭部中的
:當資源過期了,發現響應頭中具有 Last-Modified 聲明,則再次發起請求的時候帶上 Last-Modified 的時間,伺服器收到請求後發現有 If-Modified-Since 則與被請求資源的最後修改時間進行對比(Last-Modified),如果最後修改時間較新(大),說明資源又被改過,則傳回最新資源,HTTP 200 OK;如果最後修改時間較舊(小),說明資源無新修改,響應 HTTP 304 走緩存。If-Modified-Since
第二種:請求頭部中的
If-None-Match
字段與響應頭部中的
ETag
字段,這兩個字段的意思是:
- 響應頭部中
:唯一辨別響應資源;Etag
- 請求頭部中的
:當資源過期時,浏覽器發現響應頭裡有 Etag,則再次向伺服器發起請求時,會将請求頭If-None-Match 值設定為 Etag 的值。伺服器收到請求後進行比對,如果資源沒有變化傳回 304,如果資源變化了傳回 200。If-None-Match
第一種實作方式是基于時間實作的,第二種實作方式是基于一個唯一辨別實作的,相對來說後者可以更加準确地判斷檔案内容是否被修改,避免由于時間篡改導緻的不可靠問題。
如果 HTTP 響應頭部同時有 Etag 和 Last-Modified 字段的時候, Etag 的優先級更高,也就是先會判斷 Etag 是否變化了,如果 Etag 沒有變化,然後再看 Last-Modified。
注意,協商緩存這兩個字段都需要配合強制緩存中 Cache-control 字段來使用,隻有在未能命中強制緩存的時候,才能發起帶有協商緩存字段的請求。
使用 ETag 字段實作的協商緩存的過程如下;
- 當浏覽器第一次請求通路伺服器資源時,伺服器會在傳回這個資源的同時,在 Response 頭部加上 ETag 唯一辨別,這個唯一辨別的值是根據目前請求的資源生成的;
- 當浏覽器再次請求通路伺服器中的該資源時,首先會先檢查強制緩存是否過期,如果沒有過期,則直接使用本地緩存;如果緩存過期了,會在 Request 頭部加上 If-None-Match 字段,該字段的值就是 ETag 唯一辨別;
- 伺服器再次收到請求後,會根據請求中的 If-None-Match 值與目前請求的資源生成的唯一辨別進行比較:
- 如果值相等,則傳回 304 Not Modified,不會傳回資源;
- 如果不相等,則傳回 200 狀态碼和傳回資源,并在 Response 頭部加上新的 ETag 唯一辨別;
- 如果浏覽器收到 304 的請求響應狀态碼,則會從本地緩存中加載資源,否則更新資源。
HTTP 特性
HTTP(1.1) 的優點有哪些?
HTTP 最凸出的優點是「簡單、靈活和易于擴充、應用廣泛和跨平台」。
1. 簡單
HTTP 基本的封包格式就是
header + body
,頭部資訊也是
key-value
簡單文本的形式,易于了解,降低了學習和使用的門檻。
2. 靈活和易于擴充
HTTP協定裡的各類請求方法、URI/URL、狀态碼、頭字段等每個組成要求都沒有被固定死,都允許開發人員自定義和擴充。
同時 HTTP 由于是工作在應用層(
OSI
第七層),則它下層可以随意變化。
HTTPS 也就是在 HTTP 與 TCP 層之間增加了 SSL/TLS 安全傳輸層,HTTP/3 甚至把 TCP 層換成了基于 UDP 的 QUIC。
3. 應用廣泛和跨平台
網際網路發展至今,HTTP 的應用範圍非常的廣泛,從桌上型電腦的浏覽器到手機上的各種 APP,從看新聞、刷貼吧到購物、理财、吃雞,HTTP 的應用遍地開花,同時天然具有跨平台的優越性。
HTTP(1.1) 的缺點有哪些?
HTTP 協定裡有優缺點一體的雙刃劍,分别是「無狀态、明文傳輸」,同時還有一大缺點「不安全」。
1. 無狀态雙刃劍
無狀态的好處,因為伺服器不會去記憶 HTTP 的狀态,是以不需要額外的資源來記錄狀态資訊,這能減輕伺服器的負擔,能夠把更多的 CPU 和記憶體用來對外提供服務。
無狀态的壞處,既然伺服器沒有記憶能力,它在完成有關聯性的操作時會非常麻煩。
例如登入->添加購物車->下單->結算->支付,這系列操作都要知道使用者的身份才行。但伺服器不知道這些請求是有關聯的,每次都要問一遍身份資訊。
這樣每操作一次,都要驗證資訊,這樣的購物體驗還能愉快嗎?别問,問就是酸爽!
對于無狀态的問題,解法方案有很多種,其中比較簡單的方式用 Cookie 技術。
Cookie
通過在請求和響應封包中寫入 Cookie 資訊來控制用戶端的狀态。
相當于,在用戶端第一次請求後,伺服器會下發一個裝有客戶資訊的「小貼紙」,後續用戶端請求伺服器的時候,帶上「小貼紙」,伺服器就能認得了了,
Cookie 技術
2. 明文傳輸雙刃劍
明文意味着在傳輸過程中的資訊,是可友善閱讀的,通過浏覽器的 F12 控制台或 Wireshark 抓包都可以直接肉眼檢視,為我們調試工作帶了極大的便利性。
但是這正是這樣,HTTP 的所有資訊都暴露在了光天化日下,相當于資訊裸奔。在傳輸的漫長的過程中,資訊的内容都毫無隐私可言,很容易就能被竊取,如果裡面有你的賬号密碼資訊,那你号沒了。
3. 不安全
HTTP 比較嚴重的缺點就是不安全:
- 通信使用明文(不加密),内容可能會被竊聽。比如,賬号資訊容易洩漏,那你号沒了。
- 不驗證通信方的身份,是以有可能遭遇僞裝。比如,通路假的淘寶、拼多多,那你錢沒了。
- 無法證明封包的完整性,是以有可能已遭篡改。比如,網頁上植入垃圾廣告,視覺污染,眼沒了。
HTTP 的安全問題,可以用 HTTPS 的方式解決,也就是通過引入 SSL/TLS 層,使得在安全上達到了極緻。
HTTP/1.1 的性能如何?
HTTP 協定是基于 TCP/IP,并且使用了「請求 - 應答」的通信模式,是以性能的關鍵就在這兩點裡。
1. 長連接配接
早期 HTTP/1.0 性能上的一個很大的問題,那就是每發起一個請求,都要建立一次 TCP 連接配接(三次握手),而且是串行請求,做了無謂的 TCP 連接配接建立和斷開,增加了通信開銷。
為了解決上述 TCP 連接配接問題,HTTP/1.1 提出了長連接配接的通信方式,也叫持久連接配接。這種方式的好處在于減少了 TCP 連接配接的重複建立和斷開所造成的額外開銷,減輕了伺服器端的負載。
持久連接配接的特點是,隻要任意一端沒有明确提出斷開連接配接,則保持 TCP 連接配接狀态。
短連接配接與長連接配接
當然,如果某個 HTTP 長連接配接超過一定時間沒有任何資料互動,服務端就會主動斷開這個連接配接。
2. 管道網絡傳輸
HTTP/1.1 采用了長連接配接的方式,這使得管道(pipeline)網絡傳輸成為了可能。
即可在同一個 TCP 連接配接裡面,用戶端可以發起多個請求,隻要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。
舉例來說,用戶端需要請求兩個資源。以前的做法是,在同一個 TCP 連接配接裡面,先發送 A 請求,然後等待伺服器做出回應,收到後再發出 B 請求。那麼,管道機制則是允許浏覽器同時發出 A 請求和 B 請求,如下圖:
管道網絡傳輸
但是伺服器必須按照接收請求的順序發送對這些管道化請求的響應。
注意,是按照服務端收到的請求順序響應,并不管哪個請求是先發送的,假設用戶端先發送 A 請求,後發送 B 請求,如果服務端先收到 B 請求,就先響應 B 請求,然後再響應 A 請求,但是假設處理 B 請求的時候,耗時比較長,那麼請求 A 的響應就會被阻塞,這稱為「隊頭堵塞」。
是以,HTTP/1.1 管道解決了請求的隊頭阻塞,但是沒有解決響應的隊頭阻塞。
3. 隊頭阻塞
「請求 - 應答」的模式加劇了 HTTP 的性能問題。
因為當順序發送的請求序列中的一個請求因為某種原因被阻塞時,在後面排隊的所有請求也一同被阻塞了,會招緻用戶端一直請求不到資料,這也就是「隊頭阻塞」,好比上班的路上塞車。
隊頭阻塞
總之 HTTP/1.1 的性能一般,後續的 HTTP/2 和 HTTP/3 就是在優化 HTTP 的性能。
HTTP 與 HTTPS
HTTP 與 HTTPS 有哪些差別?
- HTTP 是超文本傳輸協定,資訊是明文傳輸,存在安全風險的問題。HTTPS 則解決 HTTP 不安全的缺陷,在 TCP 和 HTTP 網絡層之間加入了 SSL/TLS 安全協定,使得封包能夠加密傳輸。
- HTTP 連接配接建立相對簡單, TCP 三次握手之後便可進行 HTTP 的封包傳輸。而 HTTPS 在 TCP 三次握手之後,還需進行 SSL/TLS 的握手過程,才可進入加密封包傳輸。
- HTTP 的端口号是 80,HTTPS 的端口号是 443。
- HTTPS 協定需要向 CA(證書權威機構)申請數字證書,來保證伺服器的身份是可信的。
HTTPS 解決了 HTTP 的哪些問題?
HTTP 由于是明文傳輸,是以安全上存在以下三個風險:
- 竊聽風險,比如通信鍊路上可以擷取通信内容,使用者号容易沒。
- 篡改風險,比如強制植入垃圾廣告,視覺污染,使用者眼容易瞎。
- 冒充風險,比如冒充淘寶網站,使用者錢容易沒。
HTTP 與 HTTPS 網絡層
HTTPS 在 HTTP 與 TCP 層之間加入了
SSL/TLS
協定,可以很好的解決了上述的風險:
- 資訊加密:互動資訊無法被竊取,但你的号會因為「自身忘記」賬号而沒。
- 校驗機制:無法篡改通信内容,篡改了就不能正常顯示,但百度「競價排名」依然可以搜尋垃圾廣告。
- 身份證書:證明淘寶是真的淘寶網,但你的錢還是會因為「剁手」而沒。
可見,隻要自身不做「惡」,SSL/TLS 協定是能保證通信是安全的。
HTTPS 是如何解決上面的三個風險的?
- 混合加密的方式實作資訊的機密性,解決了竊聽的風險。
- 摘要算法的方式來實作完整性,它能夠為資料生成獨一無二的「指紋」,指紋用于校驗資料的完整性,解決了篡改的風險。
- 将伺服器公鑰放入到數字證書中,解決了冒充的風險。
1. 混合加密
通過混合加密的方式可以保證資訊的機密性,解決了竊聽的風險。
HTTPS 采用的是對稱加密和非對稱加密結合的「混合加密」方式:
- 在通信建立前采用非對稱加密的方式交換「會話秘鑰」,後續就不再使用非對稱加密。
- 在通信過程中全部使用對稱加密的「會話秘鑰」的方式加密明文資料。
采用「混合加密」的方式的原因:
- 對稱加密隻使用一個密鑰,運算速度快,密鑰必須保密,無法做到安全的密鑰交換。
- 非對稱加密使用兩個密鑰:公鑰和私鑰,公鑰可以任意分發而私鑰保密,解決了密鑰交換問題但速度慢。
2. 摘要算法
摘要算法用來實作完整性,能夠為資料生成獨一無二的「指紋」,用于校驗資料的完整性,解決了篡改的風險。
校驗完整性
用戶端在發送明文之前會通過摘要算法算出明文的「指紋」,發送的時候把「指紋 + 明文」一同加密成密文後,發送給伺服器,伺服器解密後,用相同的摘要算法算出發送過來的明文,通過比較用戶端攜帶的「指紋」和目前算出的「指紋」做比較,若「指紋」相同,說明資料是完整的。
3. 數字證書
用戶端先向伺服器端索要公鑰,然後用公鑰加密資訊,伺服器收到密文後,用自己的私鑰解密。
這就存在些問題,如何保證公鑰不被篡改和信任度?
是以這裡就需要借助第三方權威機構
CA
(數字證書認證機構),将伺服器公鑰放在數字證書(由數字證書認證機構頒發)中,隻要證書是可信的,公鑰就是可信的。
數子證書工作流程
通過數字證書的方式保證伺服器公鑰的身份,解決冒充的風險。
HTTPS 是如何建立連接配接的?其間互動了什麼?
SSL/TLS 協定基本流程:
- 用戶端向伺服器索要并驗證伺服器的公鑰。
- 雙方協商生産「會話秘鑰」。
- 雙方采用「會話秘鑰」進行加密通信。
前兩步也就是 SSL/TLS 的建立過程,也就是 TLS 握手階段。
SSL/TLS 的「握手階段」涉及四次通信,可見下圖:
SSL/TLS 協定建立的詳細流程:
1. ClientHello
首先,由用戶端向伺服器發起加密通信請求,也就是
ClientHello
請求。
在這一步,用戶端主要向伺服器發送以下資訊:
(1)用戶端支援的 SSL/TLS 協定版本,如 TLS 1.2 版本。
(2)用戶端生産的随機數(
Client Random
),後面用于生成「會話秘鑰」條件之一。
(3)用戶端支援的密碼套件清單,如 RSA 加密算法。
2. SeverHello
伺服器收到用戶端請求後,向用戶端發出響應,也就是
SeverHello
。伺服器回應的内容有如下内容:
(1)确認 SSL/ TLS 協定版本,如果浏覽器不支援,則關閉加密通信。
(2)伺服器生産的随機數(
Server Random
),也是後面用于生産「會話秘鑰」條件之一。
(3)确認的密碼套件清單,如 RSA 加密算法。
(4)伺服器的數字證書。
3.用戶端回應
用戶端收到伺服器的回應之後,首先通過浏覽器或者作業系統中的 CA 公鑰,确認伺服器的數字證書的真實性。
如果證書沒有問題,用戶端會從數字證書中取出伺服器的公鑰,然後使用它加密封包,向伺服器發送如下資訊:
(1)一個随機數(
pre-master key
)。該随機數會被伺服器公鑰加密。
(2)加密通信算法改變通知,表示随後的資訊都将用「會話秘鑰」加密通信。
(3)用戶端握手結束通知,表示用戶端的握手階段已經結束。這一項同時把之前所有内容的發生的資料做個摘要,用來供服務端校驗。
上面第一項的随機數是整個握手階段的第三個随機數,會發給服務端,是以這個随機數用戶端和服務端都是一樣的。
伺服器和用戶端有了這三個随機數(Client Random、Server Random、pre-master key),接着就用雙方協商的加密算法,各自生成本次通信的「會話秘鑰」。
4. 伺服器的最後回應
伺服器收到用戶端的第三個随機數(
pre-master key
)之後,通過協商的加密算法,計算出本次通信的「會話秘鑰」。
然後,向用戶端發送最後的資訊:
(1)加密通信算法改變通知,表示随後的資訊都将用「會話秘鑰」加密通信。
(2)伺服器握手結束通知,表示伺服器的握手階段已經結束。這一項同時把之前所有内容的發生的資料做個摘要,用來供用戶端校驗。
至此,整個 SSL/TLS 的握手階段全部結束。接下來,用戶端與伺服器進入加密通信,就完全是使用普通的 HTTP 協定,隻不過用「會話秘鑰」加密内容。
HTTP/1.1、HTTP/2、HTTP/3 演變
HTTP/1.1 相比 HTTP/1.0 提高了什麼性能?
HTTP/1.1 相比 HTTP/1.0 性能上的改進:
- 使用 TCP 長連接配接的方式改善了 HTTP/1.0 短連接配接造成的性能開銷。
- 支援管道(pipeline)網絡傳輸,隻要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。
但 HTTP/1.1 還是有性能瓶頸:
- 請求 / 響應頭部(Header)未經壓縮就發送,首部資訊越多延遲越大。隻能壓縮
的部分;Body
- 發送冗長的首部。每次互相發送相同的首部造成的浪費較多;
- 伺服器是按請求的順序響應的,如果伺服器響應慢,會招緻用戶端一直請求不到資料,也就是隊頭阻塞;
- 沒有請求優先級控制;
- 請求隻能從用戶端開始,伺服器隻能被動響應。
HTTP/2 做了什麼優化?
HTTP/2 協定是基于 HTTPS 的,是以 HTTP/2 的安全性也是有保障的。
HTT/1 ~ HTTP/2
那 HTTP/2 相比 HTTP/1.1 性能上的改進:
1. 頭部壓縮
HTTP/2 會壓縮頭(Header)如果你同時發出多個請求,他們的頭是一樣的或是相似的,那麼,協定會幫你消除重複的部分。
這就是所謂的
HPACK
算法:在用戶端和伺服器同時維護一張頭資訊表,所有字段都會存入這個表,生成一個索引号,以後就不發送同樣字段了,隻發送索引号,這樣就提高速度了。
2. 二進制格式
HTTP/2 不再像 HTTP/1.1 裡的純文字形式的封包,而是全面采用了二進制格式,頭資訊和資料體都是二進制,并且統稱為幀(frame):頭資訊幀(Headers Frame)和資料幀(Data Frame)。
HTTP/1 與 HTTP/2
這樣雖然對人不友好,但是對計算機非常友好,因為計算機隻懂二進制,那麼收到封包後,無需再将明文的封包轉成二進制,而是直接解析二進制封包,這增加了資料傳輸的效率。
比如狀态碼 200 ,在 HTTP/1.1 是用 '2''0''0' 三個字元來表示(二進制:110010 110000 110000),如圖:
在 HTTP/2 是用數字 200 表示(二進制:11001000),如圖:
3. 資料流
HTTP/2 的資料包不是按順序發送的,同一個連接配接裡面連續的資料包,可能屬于不同的回應。是以,必須要對資料包做标記,指出它屬于哪個回應。
在 HTTP/2 中每個請求或相應的所有資料包,稱為一個資料流(
Stream
)。每個資料流都标記着一個獨一無二的編号(Stream ID),不同 Stream 的幀是可以亂序發送的(是以可以并發不同的 Stream ),因為每個幀的頭部會攜帶 Stream ID 資訊,是以接收端可以通過 Stream ID 有序組裝成 HTTP 消息
用戶端和伺服器雙方都可以建立 Stream, Stream ID 也是有差別的,用戶端建立的 Stream 必須是奇數号,而伺服器建立的 Stream 必須是偶數号。
用戶端還可以指定資料流的優先級。優先級高的請求,伺服器就先響應該請求。
4. 多路複用
HTTP/2 是可以在一個連接配接中并發多個請求或回應,而不用按照順序一一對應。
移除了 HTTP/1.1 中的串行請求,不需要排隊等待,也就不會再出現「隊頭阻塞」問題,降低了延遲,大幅度提高了連接配接的使用率。
舉例來說,在一個 TCP 連接配接裡,伺服器收到了用戶端 A 和 B 的兩個請求,如果發現 A 處理過程非常耗時,于是就回應 A 請求已經處理好的部分,接着回應 B 請求,完成後,再回應 A 請求剩下的部分。
多路複用
5. 伺服器推送
HTTP/2 還在一定程度上改善了傳統的「請求 - 應答」工作模式,服務不再是被動地響應,也可以主動向用戶端發送消息。
比如,用戶端通過 HTTP/1.1 請求從伺服器那擷取到了 HTML 檔案,而 HTML 可能還需要依賴 CSS 來渲染頁面,這時用戶端還要再發起擷取 CSS 檔案的請求,需要兩次消息往返,如下圖左邊部分:
img
如上圖右邊部分,在 HTTP/2 中,用戶端在通路 HTML 時,伺服器可以直接主動推送 CSS 檔案,減少了消息傳遞的次數。
HTTP/2 有什麼缺陷?
HTTP/2 通過 Stream 的并發能力,解決了 HTTP/1 隊頭阻塞的問題,看似很完美了,但是 HTTP/2 還是存在“隊頭阻塞”的問題,隻不過問題不是在 HTTP 這一層面,而是在 TCP 這一層。
HTTP/2 是基于 TCP 協定來傳輸資料的,TCP 是位元組流協定,TCP 層必須保證收到的位元組資料是完整且連續的,這樣核心才會将緩沖區裡的資料傳回給 HTTP 應用,那麼當「前 1 個位元組資料」沒有到達時,後收到的位元組資料隻能存放在核心緩沖區裡,隻有等到這 1 個位元組資料到達時,HTTP/2 應用層才能從核心中拿到資料,這就是 HTTP/2 隊頭阻塞問題。
舉個例子,如下圖:
img
圖中發送方發送了很多個 packet,每個 packet 都有自己的序号,你可以認為是 TCP 的序列号,其中 packet 3 在網絡中丢失了,即使 packet 4-6 被接收方收到後,由于核心中的 TCP 資料不是連續的,于是接收方的應用層就無法從核心中讀取到,隻有等到 packet 3 重傳後,接收方的應用層才可以從核心中讀取到資料,這就是 HTTP/2 的隊頭阻塞問題,是在 TCP 層面發生的。
是以,一旦發生了丢包現象,就會觸發 TCP 的重傳機制,這樣在一個 TCP 連接配接中的所有的 HTTP 請求都必須等待這個丢了的包被重傳回來。
HTTP/3 做了哪些優化?
前面我們知道了 HTTP/1.1 和 HTTP/2 都有隊頭阻塞的問題:
- HTTP/1.1 中的管道( pipeline)雖然解決了請求的隊頭阻塞,但是沒有解決響應的隊頭阻塞,因為服務端需要按順序響應收到的請求,如果服務端處理某個請求消耗的時間比較長,那麼隻能等相應完這個請求後, 才能處理下一個請求,這屬于 HTTP 層隊頭阻塞。
- HTTP/2 雖然通過多個請求複用一個 TCP 連接配接解決了 HTTP 的隊頭阻塞 ,但是一旦發生丢包,就會阻塞住所有的 HTTP 請求,這屬于 TCP 層隊頭阻塞。
HTTP/2 隊頭阻塞的問題是因為 TCP,是以 HTTP/3 把 HTTP 下層的 TCP 協定改成了 UDP!
HTTP/1 ~ HTTP/3
UDP 發生是不管順序,也不管丢包的,是以不會出現像 HTTP/2 隊頭阻塞的問題
大家都知道 UDP 是不可靠傳輸的,但基于 UDP 的 QUIC 協定 可以實作類似 TCP 的可靠性傳輸。
QUIC 有以下 3 個特點。
1、無隊頭阻塞
QUIC 協定也有類似 HTTP/2 Stream 與多路複用的概念,也是可以在同一條連接配接上并發傳輸多個 Stream,Stream 可以認為就是一條 HTTP 請求。
QUIC 有自己的一套機制可以保證傳輸的可靠性的。當某個流發生丢包時,隻會阻塞這個流,其他流不會受到影響,是以不存在隊頭阻塞問題。這與 HTTP/2 不同,HTTP/2 隻要某個流中的資料包丢失了,其他流也會是以受影響。
是以,QUIC 連接配接上的多個 Stream 之間并沒有依賴,都是獨立的,某個流發生丢包了,隻會影響該流,其他流不受影響。
2、更快的連接配接建立
對于 HTTP/1 和 HTTP/2 協定,TCP 和 TLS 是分層的,分别屬于核心實作的傳輸層、openssl 庫實作的表示層,是以它們難以合并在一起,需要分批次來握手,先 TCP 握手,再 TLS 握手。
HTTP/3 在傳輸資料前雖然需要 QUIC 協定握手,這個握手過程隻需要 1 RTT,握手的目的是為确認雙方的「連接配接 ID」,連接配接遷移就是基于連接配接 ID 實作的。
但是 HTTP/3 的 QUIC 協定并不是與 TLS 分層,而是QUIC 内部包含了 TLS,它在自己的幀會攜帶 TLS 裡的“記錄”,再加上 QUIC 使用的是 TLS/1.3,是以僅需 1 個 RTT 就可以「同時」完成建立連接配接與密鑰協商,如下圖:
TCP HTTPS(TLS/1.3) 和 QUIC HTTPS
甚至,在第二次連接配接的時候,應用資料包可以和 QUIC 握手資訊(連接配接資訊 + TLS 資訊)一起發送,達到 0-RTT 的效果。
3、連接配接遷移
基于 TCP 傳輸協定的 HTTP 協定,由于是通過四元組(源 IP、源端口、目的 IP、目的端口)确定一條 TCP 連接配接,那麼當移動裝置的網絡從 4G 切換到 WIFI 時,意味着 IP 位址變化了,那麼就必須要斷開連接配接,然後重建立立連接配接。而建立連接配接的過程包含 TCP 三次握手和 TLS 四次握手的時延,以及 TCP 慢啟動的減速過程,給使用者的感覺就是網絡突然卡頓了一下,是以連接配接的遷移成本是很高的。
而 QUIC 協定沒有用四元組的方式來“綁定”連接配接,而是通過連接配接 ID來标記通信的兩個端點,用戶端和伺服器可以各自選擇一組 ID 來标記自己,是以即使移動裝置的網絡變化後,導緻 IP 位址變化了,隻要仍保有上下文資訊(比如連接配接 ID、TLS 密鑰等),就可以“無縫”地複用原連接配接,消除重連的成本,沒有絲毫卡頓感,達到了連接配接遷移的功能。
是以, QUIC 是一個在 UDP 之上的僞 TCP + TLS + HTTP/2 的多路複用的協定。
QUIC 是新協定,對于很多網絡裝置,根本不知道什麼是 QUIC,隻會當做 UDP,這樣會出現新的問題,因為有的網絡裝置是會丢掉 UDP 包的,而 QUIC 是基于UDP 實作的,那麼如果網絡裝置無法識别這個是 QUIC 包,那麼就會當作 UDP包,然後被丢棄。
是以,HTTP/3 現在普及的進度非常的緩慢,不知道未來 UDP 是否能夠逆襲 TCP。
參考資料:
[1] 上野 宣.圖解HTTP.人民郵電出版社.
[2] 羅劍鋒.透視HTTP協定.極客時間.
[3] 陳皓.HTTP的前世今.酷殼CoolShell.https://coolshell.cn/articles/19840.html
[4] 阮一峰.HTTP 協定入門.阮一峰的網絡日志.http://www.ruanyifeng.com/blog/2016/08/http.html
讀者問答
讀者問:“https和http相比,就是傳輸的内容多了對稱加密,可以這麼了解嗎?”
- 建立連接配接時候:https 比 http多了 TLS 的握手過程;
- 傳輸内容的時候:https 會把資料進行加密,通常是對稱加密資料;
讀者問:“ 我看文中 TLS 和 SSL 沒有做區分,這兩個需要區分嗎?”
這兩實際上是一個東西。
SSL 是洋文 “Secure Sockets Layer 的縮寫,中文叫做「安全套接層」。它是在上世紀 90 年代中期,由網景公司設計的。
到了1999年,SSL 因為應用廣泛,已經成為網際網路上的事實标準。IETF 就在那年把 SSL 标準化。标準化之後的名稱改為 TLS(是 “Transport Layer Security” 的縮寫),中文叫做 「傳輸層安全協定」。
很多相關的文章都把這兩者并列稱呼(SSL/TLS),因為這兩者可以視作同一個東西的不同階段。
讀者問:“為啥 ssl 的握手是 4 次?”
SSL/TLS 1.2 需要 4 握手,需要 2 個 RTT 的時延,我文中的圖是把每個互動分開畫了,實際上把他們合在一起發送,就是 4 次握手:
另外, SSL/TLS 1.3 優化了過程,隻需要 1 個 RTT 往返時延,也就是隻需要 3 次握手:
小林是專為大家圖解的工具人,Goodbye,我們下次見!
微信搜尋公衆号:「小林coding」 ,回複「圖解」即可免費獲得「圖解網絡、圖解系統、圖解MySQL、圖解Redis」PDF 電子書