2020-07-16 HTTP簡介

image.png

當我們打開網站時也許不會去留意網站前面的HTTP是怎么來的而柑。但是它毫無疑問在網絡中有著舉足輕重的地位。本文從起源到發(fā)展,詳說HTTP從1到3的演變风钻。

說在前面

本文不致力于講完 HTTP 的全部內容贬派,事實上短短的篇幅也不可能講完。本文也無意于深挖 HTTP 中的某一點,這是像 《HTTP 權威指南》或者是 RFC 協(xié)議做的事肄渗。

本文目標是幫助讀者理清 HTTP 的演化過程,說說 HTTP 變化的那些事咬最。

HTTP 的起源

HTTP 最初是 Tim BernersLee 1989 年在歐洲核子研究組織(CERN)所發(fā)起的翎嫡。Tim BernersLee 提出了一種能讓遠隔兩地的研究者們共享知識的設想。這個設想的基本理念是:借助多文檔之間相互關聯(lián)形成的超文本(HyperText)永乌,連成可相互參閱的 WWW(World Wide Web钝的,萬維網)。用于傳輸?shù)某谋緜鬏攨f(xié)議(HyperText Transfer Protocol)铆遭,即 HTTP 由此誕生。

WWW 這一名稱沿猜,是 Web 瀏覽器當年用來瀏覽超文本的客戶端應用程序時的名稱∶度伲現(xiàn)在則用來表示這一系列的集合,也可簡稱為 Web啼肩。

HTTP 本身是一個簡單的請求-響應協(xié)議橄妆,它通常運行在 TCP 之上。從整個網絡模型來看祈坠,HTTP 是應用層的一個協(xié)議害碾。在 OSI 七層模型中,HTTP 位于最上層赦拘。它并不涉及數(shù)據(jù)包的傳輸慌随,只是規(guī)定了客戶端和服務器之間的通信格式。定了客戶端可能發(fā)送給服務器什么樣的消息以及得到什么樣的響應躺同。請求和響應消息的頭以 ASCII 碼形式給出阁猜。

HTTP 采用 BS 架構,也就是瀏覽器到服務器的架構蹋艺,客戶端通過瀏覽器發(fā)送 HTTP 請求給服務器剃袍,服務器經過解析響應客戶端的請求。就是這個簡單實用的模型捎谨,使得 HTTP 這個基于 TCP/IP 的協(xié)議迅速推廣民效。

HTTP/0.9 到 HTTP/1.1

HTTP 的演化并不是一蹴而就的。當年 HTTP 的出現(xiàn)主要是為了解決文本傳輸?shù)碾y題涛救。由于協(xié)議本身非常簡單畏邢,于是在此基礎上設想了很多應用方法并投入了實際使用。現(xiàn)在 HTTP 已經超出了 Web 這個框架的局限州叠,被運用到了各種場景里棵红。

HTTP/0.9

HTTP 協(xié)議最早的一個版本是 1990 年發(fā)布的 HTTP/0.9。

前面說到咧栗,HTTP 于 1989 年問世逆甜。那時的 HTTP 并沒有作為正式的標準被建立虱肄。這時的 HTTP 其實含有 HTTP/1.0 之前版本的意思,因此被稱為 HTTP/0.9交煞。這個版本只有一個命令:GET咏窿。通過 GET 可以獲取服務器的資源,比如請求服務器根目錄下的 index.html 文件素征。這個版本的協(xié)議規(guī)定集嵌,服務器只能回應 HTML 格式的字符串,不能回應其它格式御毅,也就是說圖像根欧、視頻等多媒體資源,在 HTTP/0.9 這個版本上是無法進行傳輸?shù)摹?/strong>

HTTP/1.0

HTTP 正式作為標準被公布是在 1996 年的 5 月端蛆,版本被命名為 HTTP/1.0凤粗,并記載于 RFC1945 [https://www.ietf.org/rfc/rfc1...]。雖說是初期標準今豆,但該協(xié)議標準至今仍被廣泛使用在服務器端嫌拣。

HTTP/1.0 版本發(fā)布,增加了 POST 命令和 HEAD 命令呆躲,豐富了瀏覽器與服務器的互動手段异逐。這個版本的 HTTP 協(xié)議可以發(fā)送任何格式的內容,包括傳輸文字插掂、圖像灰瞻、視頻、文件等燥筷,這為互聯(lián)網的大發(fā)展奠定了基礎箩祥。

HTTP/1.0 除了增加了請求方法以及對發(fā)送文件的支持之外,還增加了格式的改變肆氓。除了數(shù)據(jù)部分袍祖,每次通信都必須包括頭信息(HTTP header),用來描述一些元數(shù)據(jù)谢揪。另外還增加了狀態(tài)碼蕉陋、多字符集支持、多部分發(fā)送(multi-part type)拨扶、權限(authorization)凳鬓、緩存(cache)、內容編碼(content encoding)等等患民。

HTTP/1.1

HTTP/1.0 版也并不是完美的缩举,它的主要缺點是,每一次建立 TCP 連接只能發(fā)送一個請求。發(fā)送數(shù)據(jù)完畢仅孩,連接就關閉托猩,如果還要請求其他資源,就必須再新建一個連接辽慕。如果多次請求京腥,勢必就會對服務器產生較大的資源性能損耗。

1997 年 1 月公布的 HTTP/1.1 是目前主流的 HTTP 協(xié)議版本溅蛉。當初的標準是 RFC2068公浪,之后發(fā)布的修訂版 RFC2616 就是當前的最新版本。

其中最著名的是 1999 年 6 月公布的 RFC 2616 [https://tools.ietf.org/html/r...]船侧,定義了 HTTP 協(xié)議中現(xiàn)今廣泛使用的一個版本——HTTP/1.1欠气。

這個版本最大的變化就是將持久化連接加入了 HTTP 標準,即 TCP 連接默認不關閉镜撩,可以被多個請求復用晃琳。此外,HTTP/1.1 版還新增了許多方法琐鲁,例如:PUT、PATCH人灼、HEAD围段、OPTIONS、DELETE投放。得到進一步完善的HTTP/1.1 版本奈泪,一直沿用至今。

HTTP 協(xié)議簡單介紹

請求

客戶端發(fā)送一個 HTTP 請求到服務器灸芳,請求消息包括以下格式:

請求行(request line)涝桅、請求頭部(header)、空行和請求數(shù)據(jù)四個部分組成烙样。

Get 請求例子

1 > GET / HTTP/1.1
2 > Host: www.baidu.com
3 > User-Agent: curl/7.52.1
4 > Accept: /

第一部分:請求行冯遂,用來說明請求類型,要訪問的資源以及所使用的 HTTP 版本谒获。

第二部分:請求頭部蛤肌,緊接著請求行(即第一行)之后的部分,用來說明服務器要使用的附加信息

從第二行起為請求頭部批狱,HOST 將指出請求的目的地裸准。User-Agent,服務器端和客戶端腳本都能訪問它,它是瀏覽器類型檢測邏輯的重要基礎赔硫。該信息由你的瀏覽器來定義炒俱,并且在每個請求中自動發(fā)送等等。

第三部分:空行,請求頭部后面的空行是必須的

即使第四部分的請求數(shù)據(jù)為空权悟,也必須有空行砸王。

第四部分:請求數(shù)據(jù)也叫主體,可以添加任意的其他數(shù)據(jù)僵芹。

這個例子的請求數(shù)據(jù)為空处硬。

響應消息

一般情況下,服務器接收并處理客戶端發(fā)過來的請求后拇派,會返回一個 HTTP 的響應消息荷辕。

HTTP 響應也由四個部分組成,分別是:狀態(tài)行件豌、消息報頭疮方、空行和響應正文。

例子

1 < HTTP/1.1 200 OK
2 < Accept-Ranges: bytes
3 < Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
4 < Connection: keep-alive
5 < Content-Length: 2381
6 < Content-Type: text/html
7 < Date: Thu, 11 Jun 2020 16:04:33 GMT
8 < Etag: "588604c8-94d"
9 < Last-Modified: Mon, 23 Jan 2017 13:27:36 GMT
10 < Pragma: no-cache
11 < Server: bfe/1.0.8.18
12 < Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
13 <
14 < !DOCTYPE html>
15 < !--STATUS OK--><html> <head><meta HTTP-equiv=content-type content=text/html;charset=utf-8><meta HTTP-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer>... </html>
16

第一部分:狀態(tài)行茧彤,由 HTTP 協(xié)議版本號骡显、狀態(tài)碼、狀態(tài)消息三部分組成曾掂。

第一行為狀態(tài)行惫谤,(HTTP/1.1)表明 HTTP 版本為 1.1 版本,狀態(tài)碼為 200珠洗,狀態(tài)消息為(ok)

第二部分:消息報頭溜歪,用來說明客戶端要使用的一些附加信息

第二行和第三行為消息報頭。

Date:生成響應的日期和時間许蓖;Content-Type:指定了 MIME 類型的 HTML(text/html)蝴猪,編碼類型是 UTF-8

第三部分:空行,消息報頭后面的空行是必須的

第四部分:響應正文膊爪,服務器返回給客戶端的文本信息自阱。

空行后面的 HTML 部分為響應正文。

狀態(tài)碼

狀態(tài)代碼有三位數(shù)字組成米酬,第一個數(shù)字定義了響應的類別沛豌,共分五種類別:

  • 1xx:指示信息–表示請求已接收,繼續(xù)處理
  • 2xx:成功–表示請求已被成功接收赃额、理解琼懊、接受
  • 3xx:重定向–要完成請求必須進行更進一步的操作
  • 4xx:客戶端錯誤–請求有語法錯誤或請求無法實現(xiàn)
  • 5xx:服務器端錯誤–服務器未能實現(xiàn)合法的請求

安全性與 HTTPS

HTTP 的誕生是為了解決信息傳遞和共享的問題,并沒有考慮到互聯(lián)網高速發(fā)展后面臨的安全問題爬早。

一般來說 HTTP 從 TCP 三次握手后哼丈,便開始了數(shù)據(jù)傳輸。由于 HTTP 本身以明文形式來傳輸數(shù)據(jù)筛严,并不具備任何數(shù)據(jù)加密醉旦、身份校驗的機制。同時下層協(xié)議并不對數(shù)據(jù)安全性、保密性提供保證车胡。所以在網絡傳輸?shù)倪^程中檬输,任意節(jié)點的第三方都可以隨意劫持流量、篡改數(shù)據(jù)或竊取信息匈棘。

HTTP 無法確保數(shù)據(jù)的保密性丧慈、完整性和真實性,已經不能適應現(xiàn)代互聯(lián)網應用的安全需求主卫。

隨著 Web 的日益壯大逃默,HTTP 的使用呈巨額增長趨勢,對信息安全的需求也愈來愈迫切簇搅,SSL(Secure SocketsLayer 完域,安全套接層)應運而生。

當對于安全需求瘩将,首先想到的就是對信息進行加密吟税。SSL ,安全套接層姿现,顧名思義是在 TCP 上提供的安全套接字層肠仪。其位于應用層和傳輸層之間,應用層數(shù)據(jù)不再直接傳遞給傳輸層而是傳遞給 SSL 層备典,SSL 層對從應用層收到的數(shù)據(jù)進行加密藤韵,利用數(shù)據(jù)加密、身份驗證和消息完整性驗證機制熊经,為網絡上數(shù)據(jù)的傳輸提供安全性保證。HTTPS 便是指 Hyper Text Transfer Protocol over SecureSocket Layer欲险。

談到具體實施上镐依,業(yè)內通常采用的一般有對稱加密和非對稱加密。采用何種方式進行加密天试?如何判斷服務器未被篡改槐壳?如何傳遞加密密鑰?帶著這樣的問題喜每,我們來看看 HTTPS 的工作流程务唐。

1、客戶端發(fā)起 HTTPS 請求

這個沒什么好說的带兜,就是用戶在瀏覽器里輸入一個 HTTPS 網址枫笛,然后連接到 server 的 443 端口。

2刚照、服務端的配置

采用 HTTPS 協(xié)議的服務器必須要有一套數(shù)字證書刑巧,可以自己制作,也可以向組織申請,區(qū)別就是自己頒發(fā)的證書需要客戶端驗證通過啊楚,才可以繼續(xù)訪問吠冤,而使用受信任的公司申請的證書則不會彈出提示頁面(Let‘s Encrypt 就是個不錯的選擇,免費的 SSL 證書)恭理。

這套證書其實就是一對公鑰和私鑰拯辙,如果對公鑰和私鑰不太理解,可以想象成一把鑰匙和一個鎖頭颜价,只是全世界只有你一個人有這把鑰匙涯保,你可以把鎖頭給別人,別人可以用這個鎖把重要的東西鎖起來,然后發(fā)給你石洗,因為只有你一個人有這把鑰匙抢野,所以只有你才能看到被這把鎖鎖起來的東西。

3撇他、傳送證書

這個證書其實就是公鑰,只是包含了很多信息狈蚤,如證書的頒發(fā)機構困肩,過期時間等等。

4脆侮、客戶端解析證書

這部分工作是有客戶端的 TLS 來完成的锌畸,首先會驗證公鑰是否有效,比如頒發(fā)機構靖避,過期時間等等潭枣,如果發(fā)現(xiàn)異常,則會彈出一個警告框幻捏,提示證書存在問題盆犁。

如果證書沒有問題,那么就生成一個隨機值篡九,然后用證書對該隨機值進行加密谐岁,就好像上面說的,把隨機值用鎖頭鎖起來榛臼,這樣除非有鑰匙伊佃,不然看不到被鎖住的內容。

5沛善、傳送加密信息

這部分傳送的是用證書加密后的隨機值航揉,目的就是讓服務端得到這個隨機值,以后客戶端和服務端的通信就可以通過這個隨機值來進行加密解密了金刁。

6迷捧、服務段解密信息

服務端用私鑰解密后织咧,得到了客戶端傳過來的隨機值(私鑰),然后把內容通過該值進行對稱加密漠秋,所謂對稱加密就是笙蒙,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰庆锦,不然無法獲取內容捅位,而正好客戶端和服務端都知道這個私鑰,所以只要加密算法夠彪悍搂抒,私鑰夠復雜艇搀,數(shù)據(jù)就夠安全。

7求晶、傳輸加密后的信息

這部分信息是服務段用私鑰加密后的信息焰雕,可以在客戶端被還原。

8芳杏、客戶端解密信息

客戶端用之前生成的私鑰解密服務段傳過來的信息矩屁,于是獲取了解密后的內容,整個過程第三方即使監(jiān)聽到了數(shù)據(jù)爵赵,也束手無策吝秕。

簡單說完了 HTTPS 的工作流程。讓我們再將注意力放在 SSL 的演化上空幻。

1994年烁峭,Netscape 創(chuàng)建了 SSL 協(xié)議的原始規(guī)范并逐步發(fā)布協(xié)議改進版本。1995 年發(fā)布 SSL 2.0秕铛。1996年约郁,Netscape 和 Paul Kocher 共同設計發(fā)布 SSL 3.0 協(xié)議,獲得互聯(lián)網廣泛認可和支持但两。因特網工程任務組(IETF)接手負責該協(xié)議鬓梅,并將其重命名為 TLS(傳輸層安全)協(xié)議。

我們看到镜遣,SSL 2.0 規(guī)范是在 1995 年左右發(fā)布的,而 SSL 3.0 是在 1996 年 11 月發(fā)布的士袄。有趣的是悲关,SSL 3.0 是在 RFC 6101 [https://tools.ietf.org/html/r...] 中描述的,該 RFC 于 2011 年 8 月發(fā)布娄柳。它位于歷史類別中寓辱,該類別通常是被考慮和被丟棄的文檔想法,或者是在決定記錄它們時已經具有歷史意義的協(xié)議(根據(jù) IETF [https://www.ietf.org/about/gr...] 說明)赤拒。在這種情況下秫筏,有一個描述 SSL 3.0 的 IETF 文檔是很有必要的诱鞠,因為在其可以被用作規(guī)范參考。

再來看看这敬,SSL 是如何激發(fā) TLS 的發(fā)展的航夺。后者在 1996 年 11 月以 draft-ietf-tls-protocol-00 [https://tools.ietf.org/html/d...] 宣告開始。它經歷了六個草案版本崔涂,并于 1999 年初作為 RFC 2246 [https://tools.ietf.org/html/r...] - TLS 1.0 正式發(fā)布阳掐。

在 1995 和 1999 年間,SSL 和 TLS 協(xié)議用于保護互聯(lián)網上的 HTTP 通信冷蚂。這作為事實上的標準運行良好缭保。直到 1998 年 1 月,隨著 I-D draft-ietf-tls-HTTPs-00 [https://tools.ietf.org/html/d...] 的發(fā)布蝙茶,HTTPS 的正式標準化過程才開始艺骂。該工作于 2000 年 5 月以 RFC 2616 - HTTP 上的 TLS 的發(fā)布結束。

TLS 在 2000 到 2007 年間繼續(xù)發(fā)展隆夯,標準化為 TLS 1.1 和 1.2钳恕。直至七年后,TLS 的下一個版本開始進行吮廉,該版本在 2014 年四月被采納為 draft-ietf-tls-tls13-00 [https://tools.ietf.org/html/d...]苞尝,并在 28 份草稿后,于 2018 年八月出了完成版本 RFC 8446 [https://tools.ietf.org/html/r...] - TLS 1.3宦芦。

改進與 HTTP2

回到 HTTP 本身宙址。在很長一段時間里,HTTP/1.1 已經足夠好了(確實是调卑,現(xiàn)在仍應用最為廣泛)抡砂,但是,Web 不斷變化的需求使得我們需要一個更好更合適的協(xié)議恬涧。

HTTP/1.1 自從 1997 年發(fā)布以來注益,我們已經使用 HTTP/1.x 相當長一段時間了。但隨著互聯(lián)網近十年爆炸式的發(fā)展溯捆,從當初網頁內容以文本為主丑搔,到現(xiàn)在以富媒體(如圖片、聲音提揍、視頻)為主啤月,而且對頁面內容實時性高要求的應用越來越多(比如聊天、視頻直播)劳跃,所以當時協(xié)議規(guī)定的某些特性谎仲,已經逐漸無法滿足現(xiàn)代網絡的需求了。

image.png

如果你有仔細觀察刨仑,那些最流行的網站首頁所需要下載資源的話郑诺,會發(fā)現(xiàn)一個非常明顯的趨勢夹姥。近年來加載網站首頁需要下載的數(shù)據(jù)量在逐漸增加,并已經超過了 2100K辙诞。但在這里我們更關心的是:平均每個頁面為了完成顯示與渲染所需要下載的資源數(shù)也已經超過了 100 個辙售。

基于此,在 2010 年到 2015 年倘要,谷歌通過實踐一個實驗性的 SPDY 協(xié)議圾亏,證明了一個在客戶端和服務器端交換數(shù)據(jù)的另類方式。其收集了瀏覽器和服務器端的開發(fā)者的焦點問題封拧,明確了響應數(shù)量的增加和解決復雜的數(shù)據(jù)傳輸志鹃。在啟動 SPDY 這個項目時預設的目標是:

  • 頁面加載時間 (PLT) 減少 50%。
  • 無需網站作者修改任何內容泽西。
  • 將部署復雜性降至最低曹铃,無需變更網絡基礎設施。
  • 與開源社區(qū)合作開發(fā)這個新協(xié)議捧杉。
  • 收集真實性能數(shù)據(jù)陕见,驗證這個實驗性協(xié)議是否有效。為了達到降低目標味抖,減少頁面加載時間的目標评甜,SPDY 引入了一個新的二進制分幀數(shù)據(jù)層,以實現(xiàn)多向請求和響應仔涩、優(yōu)先次序忍坷、最小化及消除不必要的網絡延遲,目的是更有效地利用底層 TCP 連接熔脂。

HTTP/1.1 有兩個主要的缺點:安全不足和性能不高佩研,由于背負著 HTTP/1.x 龐大的歷史包袱,所以協(xié)議的修改霞揉,兼容性是首要考慮的目標旬薯,否則就會破壞互聯(lián)網上無數(shù)現(xiàn)有的資產。

image.png

而如上圖所示适秩,SPDY 位于 HTTP 之下绊序,TCP 和 SSL 之上,這樣可以輕松兼容老版本的 HTTP 協(xié)議同時可以使用已有的 SSL 功能秽荞。

SPDY 協(xié)議在 Chrome 瀏覽器上證明可行以后骤公,就被當作 HTTP/2 的基礎,主要特性都在 HTTP/2 之中得到繼承蚂会。

于是時間來到 2015 年淋样,HTTP/2.0 問世耗式。

HTTP/2 相比 HTTP/1.1 的修改并不會破壞現(xiàn)有程序的工作胁住,但是新的程序可以借由新特性得到更好的速度趁猴。

HTTP/2 保留了 HTTP/1.1 的大部分語義,例如請求方法彪见、狀態(tài)碼儡司、乃至 URI 和絕大多數(shù) HTTP 頭部字段一致。而 HTTP/2 采用了新的方法來編碼余指、傳輸客戶端和服務器間的數(shù)據(jù)捕犬。

來看看 HTTP/2 的具體特點:

  • 二進制分幀層:在應用層與傳輸層之間增加一個二進制分幀層,以此達到在不改動 HTTP 的語義酵镜,HTTP 方法碉碉、狀態(tài)碼、URI 及首部字段的情況下淮韭,突破 HTTP/1.1 的性能限制垢粮,改進傳輸性能,實現(xiàn)低延遲和高吞吐量靠粪。在二進制分幀層上蜡吧,HTTP/2.0 會將所有傳輸?shù)男畔⒎指顬楦〉南⒑蛶λ鼈儾捎枚M制格式的編碼占键,其中 HTTP1.x 的首部信息會被封裝到 Headers 幀昔善,而我們的 request body 則封裝到 Data 幀里面。
  • 多路復用:對于 HTTP/1.x畔乙,即使開啟了長連接君仆,請求的發(fā)送也是串行發(fā)送的,在帶寬足夠的情況下啸澡,對帶寬的利用率不夠袖订,HTTP/2.0 采用了多路復用的方式,可以并行發(fā)送多個請求嗅虏,提高對帶寬的利用率洛姑。
  • 數(shù)據(jù)流優(yōu)先級:由于請求可以并發(fā)發(fā)送了,那么如果出現(xiàn)了瀏覽器在等待關鍵的 CSS 或者 JS 文件完成對頁面的渲染時皮服,服務器卻在專注的發(fā)送圖片資源的情況怎么辦呢楞艾?HTTP/2.0 對數(shù)據(jù)流可以設置優(yōu)先值,這個優(yōu)先值決定了客戶端和服務端處理不同的流采用不同的優(yōu)先級策略龄广。
  • 服務端推送:在 HTTP/2.0 中硫眯,服務器可以向客戶發(fā)送請求之外的內容,比如正在請求一個頁面時择同,服務器會把頁面相關的 logo两入,CSS 等文件直接推送到客戶端,而不會等到請求來的時候再發(fā)送敲才,因為服務器認為客戶端會用到這些東西裹纳。這相當于在一個 HTML 文檔內集合了所有的資源择葡。
  • 頭部壓縮:使用首部表來跟蹤和存儲之前發(fā)送的鍵值對,對于相同的內容剃氧,不會再每次請求和響應時發(fā)送敏储。
  • HTTP/2.0 支持明文 HTTP 傳輸,而 SPDY 強制使用 HTTPS朋鞍。
  • HTTP/2.0 消息頭的壓縮算法采用 HPACK已添,而非 SPDY 采用的 DEFLATE。

QUIC 和 HTTP3

雖然 HTTP/2 提高了網頁的性能滥酥,但是并不代表它已經是完美的了更舞,HTTP/3 就是為了解決 HTTP/2 所存在的一些問題而被推出來的。隨著時間的演進坎吻,越來越多的流量都往手機端移動疏哗,手機的網絡環(huán)境會遇到的問題像是封包丟失機率較高、較長的 Round Trip Time (RTT)和連接遷移等問題禾怠,都讓主要是為了有線網路設計的HTTP/TCP協(xié)議遇到貧頸返奉。

我們可以看兩個典型的問題。

第一握手帶來的消耗吗氏。HTTP/2 使用 TCP 協(xié)議來傳輸?shù)难科绻褂?HTTPS 的話,還需要使用 TLS 協(xié)議進行安全傳輸弦讽,而使用 TLS 也需要一個握手過程污尉,這樣就需要有兩個握手延遲過程:

  • 在建立 TCP 連接的時候,需要和服務器進行三次握手來確認連接成功往产,也就是說需要在消耗完 1.5 個 RTT 之后才能進行數(shù)據(jù)傳輸被碗。
  • 進行 TLS 連接,TLS 有兩個版本——TLS 1.2 和 TLS 1.3仿村,每個版本建立連接所花的時間不同锐朴,大致是需要1~2個 RTT。

總之蔼囊,在傳輸數(shù)據(jù)之前焚志,我們需要花掉 3~4 個 RTT。

第二畏鼓,TCP 的隊頭阻塞并沒有得到徹底解決酱酬。我們知道,為了實現(xiàn)多路復用云矫,在 HTTP/2 中多個請求是跑在一個 TCP 管道中的膳沽。但當出現(xiàn)了丟包時,HTTP/2 的表現(xiàn)反倒不如 HTTP/1.X 了。因為 TCP 為了保證可靠傳輸挑社,有個特別的丟包重傳機制呵俏,丟失的包必須要等待重新傳輸確認,HTTP/2 出現(xiàn)丟包時滔灶,整個 TCP 都要開始等待重傳,那么就會阻塞該 TCP 連接中的所有請求吼肥。而對于 HTTP/1.1 來說录平,可以開啟多個 TCP 連接,出現(xiàn)這種情況反到只會影響其中一個連接缀皱,剩余的 TCP 連接還可以正常傳輸數(shù)據(jù)斗这。

至此,我們很容易就會想到啤斗,為什么不直接去修改 TCP 協(xié)議表箭?其實這已經是一件不可能完成的任務了。因為 TCP 存在的時間實在太長钮莲,已經充斥在各種設備中免钻,并且這個協(xié)議是由操作系統(tǒng)實現(xiàn)的,更新起來非常麻煩崔拥,不具備顯示操作性极舔。

HTTP/3 乘著 QUIC 來了。

image.png

HTTP3 是基于 QUIC 的協(xié)議链瓦,如上圖拆魏。先說 QUIC,QUIC 協(xié)議是 Google 提出的一套開源協(xié)議慈俯,它基于 UDP 來實現(xiàn)渤刃,直接競爭對手是 TCP 協(xié)議。QUIC 協(xié)議的性能非常好贴膘,甚至在某些場景下可以實現(xiàn) 0-RTT 的加密通信卖子。

在 Google 關于 QUIC [https://docs.google.com/docum...] 的文件中提到,與 HTTP/2 相比刑峡,QUIC 主要具有下列優(yōu)勢:

  • Reduce connection establishment latency (減少連接建立時間)
  • Improved congestion control (改進擁塞控制)
  • Multiplexing without head-of-line blocking (沒有隊頭阻塞的多路復用)
  • Forward error correction (修復之前的錯誤)
  • Connection migration(支持網絡遷移)

多路復用揪胃,避免隊頭阻塞

這句話說起來很容易,但理解起來并不那么顯然氛琢,要想理解 QUIC 協(xié)議到底做了什么以及這么做的必要性喊递,我想還是從最基礎的 HTTP/1.0 聊起比較合適。

Pipiline

根據(jù)谷歌的調查阳似, 現(xiàn)在請求一個網頁骚勘,平均涉及到 80 個資源,30 多個域名∏味铮考慮最原始的情況当宴,每請求一個資源都需要建立一次 TCP 請求,顯然不可接受泽疆。HTTP 協(xié)議規(guī)定了一個字段 Connection户矢,不過默認的值是 close,也就是不開啟殉疼。

早在 1999 年提出的 HTTP 1.1 [https://www.ietf.org/rfc/rfc2...] 協(xié)議 中就把 Connection 的默認值改成了Keep-Alive梯浪,這樣同一個域名下的多個 HTTP 請求就可以復用同一個 TCP 連接。這種做法被稱為 HTTP Pipeline瓢娜,優(yōu)點是顯著的減少了建立連接的次數(shù)挂洛,也就是大幅度減少了 RTT。

以上面的數(shù)據(jù)為例眠砾,如果 80 個資源都要走一次 HTTP 1.0虏劲,那么需要建立 80 個 TCP 連接,握手 80 次褒颈,也就是 80 個 RTT柒巫。如果采用了 HTTP 1.1 的 Pipeline,只需要建立 30 個 TCP 連接谷丸,也就是 30 個 RTT吻育,提高了 62.5% 的效率。

Pipeline 解決了 TCP 連接浪費的問題淤井,但它自己還存在一些不足之處布疼,也就是所有管道模型都難以避免的隊頭阻塞問題。

隊頭阻塞

我們再舉個簡單而且直觀的例子币狠,假設加載一個 HTML 一共要請求 10 個資源游两,那么請求的總時間是每一個資源請求時間的總和。最直觀的體驗就是漩绵,網速越快請求時間越短贱案。然而如果某一個資源的請求被阻塞了(比如 SQL 語句執(zhí)行非常慢)。但對于客戶端來說所有后續(xù)的請求都會因此而被阻塞止吐。

image.png

隊頭阻塞(Head of line blocking宝踪,下文簡稱 HOC)說的是當有多個串行請求執(zhí)行時,如果第一個請求不執(zhí)行完碍扔,后續(xù)的請求也無法執(zhí)行瘩燥。比如上圖中,如果第四個資源的傳輸花了很久不同,后面的資源都得等著厉膀,平白浪費了很多時間溶耘,帶寬資源沒有得到充分利用。

因此服鹅,HTTP 協(xié)議允許客戶端發(fā)起多個并行請求凳兵,比如在筆者的機器上最多支持六個并發(fā)請求。并發(fā)請求主要是用于解決 HOC 問題企软,當有三個并發(fā)請求時庐扫,情況會變成這樣:

image.png

可見雖然第四個資源的請求被阻塞了,但是其他的資源請求并不一定會被阻塞仗哨,這樣總的來說網絡的平均利用率得到了提升形庭。

支持并發(fā)請求是解決 HOC 問題的一種方案,這句話沒有錯藻治。但是我們要理解到:“并發(fā)請求并非是直接解決了 HOC 的問題,而是盡可能減少 HOC 造成的影響“巷挥,以上圖為例桩卵,HOC 的問題依然存在,只是不會太浪費帶寬而已倍宾。

有讀者可能會好奇雏节,為什么不多搞幾個并發(fā)的 HTTP 請求呢?剛剛說過筆者的電腦最多支持 6 個并發(fā)請求高职,谷歌曾經做過實驗钩乍,把 6 改成 10,然后嘗試訪問了三千多個網頁怔锌,發(fā)現(xiàn)平均訪問時間竟然還增加了 5% 左右寥粹。這是因為一次請求涉及的域名有限,再多的并發(fā) HTTP 請求并不能顯著提高帶寬利用率埃元,反而會消耗性能涝涤。

SPDY 的做法

有沒有辦法解決隊頭阻塞呢?

答案是肯定的岛杀。SPDY 協(xié)議的做法很值得借鑒阔拳,它采用了多路復用(Multiplexing)技術,允許多個 HTTP 請求共享同一個 TCP 連接类嗤。我們假設每個資源被分為多個包傳遞糊肠,在 HTTP 1.1 中只有前面一個資源的所有數(shù)據(jù)包傳輸完畢后,后面資源的包才能開始傳遞(HOC 問題)遗锣,而 SPDY 并不這么要求货裹,大家可以一起傳輸。

這么做的代價是數(shù)據(jù)會略微有一些冗余精偿,每一個資源的數(shù)據(jù)包都要帶上標記泪酱,用來指明自己屬于哪個資源,這樣客戶端最后才能把他們正確的拼接起來。不同的標記可以理解為圖中不同的顏色墓阀,每一個小方格可以理解為資源的某一個包毡惜。

TCP 窗口

是不是覺得 SPDY 的多路復用已經夠厲害了,解決了隊頭阻塞問題斯撮?很遺憾的是经伙,并沒有,而且我可以很肯定的說勿锅,只要你還在用 TCP 鏈接帕膜,HOC 就是逃不掉的噩夢,不信我們來看看 TCP 的實現(xiàn)細節(jié)溢十。

我們知道 TCP 協(xié)議會保證數(shù)據(jù)的可達性垮刹,如果發(fā)生了丟包或者錯包,數(shù)據(jù)就會被重傳张弛。于是問題來了荒典,如果一個包丟了,那么后面的包就得停下來等這個包重新傳輸吞鸭,也就是發(fā)生了隊頭阻塞寺董。當然 TCP 協(xié)議的設計者們也不傻,他們發(fā)明了滑動窗口的概念:

image.png

這樣的好處是在第一個數(shù)據(jù)包(1-1000) 發(fā)出后刻剥,不必等到 ACK 返回就可以立刻發(fā)送第二個數(shù)據(jù)包遮咖。可以看出圖中的 TCP 窗口大小是 4造虏,所以第四個包發(fā)送后就會開始等待御吞,直到第一個包的 ACK 返回。這樣窗口可以向后滑動一位漓藕,第五個包被發(fā)送魄藕。

如果第一、二撵术、三個的包都丟失了也沒有關系背率,當發(fā)送方收到第四個包時,它可以確信一定是前三個 ACK 丟了而不是數(shù)據(jù)包丟了嫩与,否則不會收到 4001 的 ACK寝姿,所以發(fā)送方可以大膽的把窗口向后滑動四位。

滑動窗口的概念大幅度提高了 TCP 傳輸數(shù)據(jù)時抗干擾的能力划滋,一般丟失一兩個 ACK 根本沒關系饵筑。但如果是發(fā)送的包丟失,或者出錯处坪,窗口就無法向前滑動根资,出現(xiàn)了隊頭阻塞的現(xiàn)象架专。

QUIC 是如何做的

image.png

QUIC 協(xié)議基于 UDP 實現(xiàn),我們知道 UDP 協(xié)議只負責發(fā)送數(shù)據(jù)玄帕,并不保證數(shù)據(jù)可達性部脚。這一方面為 QUIC 的多路復用提供了基礎,另一方面也要求 QUIC 協(xié)議自己保證數(shù)據(jù)可達性裤纹。

SPDY 為各個數(shù)據(jù)包做好標記委刘,指明他們屬于哪個 HTTP 請求,至于這些包能不能到達客戶端鹰椒,SPDY 并不關心挠他,因為數(shù)據(jù)可達性由 TCP 協(xié)議保證和媳。既然客戶端一定能收到包绷柒,那就只要排序耸别、拼接就行了。QUIC 協(xié)議采用了多路復用的思想奸汇,但同時還得自己保證數(shù)據(jù)的可達性施符。

TCP 協(xié)議的丟包重傳并不是一個好想法,因為一旦有了前后順序茫蛹,隊頭阻塞問題將不可避免操刀。而無序的數(shù)據(jù)發(fā)送給接受者以后烁挟,如何保證不丟包婴洼,不錯包呢?這看起來是個不可能完成的任務撼嗓,不過如果把要求降低成:最多丟一個包柬采,或者錯一個包。事情就簡單多了且警,操作系統(tǒng)中有一種存儲方式叫 RAID 5粉捻,采用的是異或運算加上數(shù)據(jù)冗余的方式來保證前向糾錯(FEC: Forward Error Correcting)。QUIC 協(xié)議也是采用這樣的思想斑芜,這里不再贅述肩刃。

利用冗余數(shù)據(jù)的思想,QUIC 協(xié)議基本上避免了重發(fā)數(shù)據(jù)的情況杏头。當然 QUIC 協(xié)議還是支持重傳的盈包,比如某些非常重要的數(shù)據(jù)或者丟失兩個包的情況。

少 RTT醇王,請求更快速

前面說到呢燥,一次 HTTPS 請求,它的基本流程是三次 TCP 握手外加四次 SSL/TLS 握手寓娩。也就是需要三個 RTT叛氨。但是 QUIC 在某些場景下呼渣,甚至能夠做到 0RTT。

首先介紹下什么是 0RTT寞埠。所謂的 0RTT 就是通信雙方發(fā)起通信連接時屁置,第一個數(shù)據(jù)包便可以攜帶有效的業(yè)務數(shù)據(jù)。而我們知道畸裳,這個使用傳統(tǒng)的TCP是完全不可能的缰犁,除非你使能了 TCP 快速打開特性,而這個很難怖糊,因為幾乎沒人愿意為了這個收益去對操作系統(tǒng)的網絡協(xié)議棧大動手腳帅容。未使能 TCP 快速打開特性的TCP傳輸?shù)谝还P數(shù)據(jù)前,至少要等1個RTT伍伤。

我們這里再說說 HTTP2并徘。對于 HTTP2 來說,本來需要一個額外的 RTT 來進行協(xié)商扰魂,判斷客戶端與服務器是不是都支持 HTTP2麦乞,不過好在它可以和 SSL 握手的請求合并。這也導致了一個現(xiàn)象劝评,就是大多數(shù)主流瀏覽器僅支持 HTTPS2 而不單獨支持 HTTP2姐直。因為 HTTP2 需要一個額外的 RTT,HTTPS2 需要兩個額外的 RTT蒋畜,僅僅是增加一個 RTT 就能獲得數(shù)據(jù)安全性声畏,還是很劃算的。

TCP 快速打開

何謂 TCP 快速打開姻成,即客戶端可以在發(fā)送第一個 SYN 握手包時攜帶數(shù)據(jù)插龄,但是 TCP 協(xié)議的實現(xiàn)者不允許將把這個數(shù)據(jù)包上傳給應用層。這主要是為了防止 TCP 泛洪攻擊 [https://tools.ietf.org/html/r...]科展。

因為如果 SYN 握手的包能被傳輸?shù)綉脤泳危敲船F(xiàn)有的防護措施都無法防御泛洪攻擊,而且服務端也會因為這些攻擊而耗盡內存和 CPU才睹。

當然 TCP 快速打開并不是完全不可行的徘跪。人們設計了 TFO (TCP Fast Open),這是對 TCP 的拓展琅攘,不僅可以在發(fā)送 SYN 時攜帶數(shù)據(jù)垮庐,還可以保證安全性。

TFO 設計了一個 Cookie乎澄,它在第一次握手時由 server 生成突硝,Cookie 主要是用來標識客戶端的身份,以及保存上次會話的配置信息置济。因此在后續(xù)重新建立 TCP 連接時解恰,客戶端會攜帶 SYN + Cookie + 請求數(shù)據(jù)锋八,然后不等 ACK 返回就直接開始發(fā)送數(shù)據(jù)。

image.png

服務端收到 SYN 后會驗證 Cookie 是否有效护盈,如果無效則會退回到三次握手的步驟挟纱,如下圖所示:

image.png

同時,為了安全起見腐宋,服務端為每個端口記錄了一個值 PendingFastOpenRequests紊服,用來表示有多少請求利用了 TFO,如果超過預設上限就不再接受胸竞。

關于 TFO 的優(yōu)化欺嗤,可以總結出三點內容:

  • TFO 設計的 Cookie 思想和 SSL 恢復握手時的 Session Ticket 很像,都是由服務端生成一段 Cookie 交給客戶端保存卫枝,從而避免后續(xù)的握手煎饼,有利于快速恢復。
  • 第一次請求絕對不會觸發(fā) TFO校赤,因為服務器會在接收到 SYN 請求后把 Cookie 和 ACK 一起返回吆玖。后續(xù)客戶端如果要重新連接,才有可能使用這個 Cookie 進行 TFO
  • TFO 并不考慮在 TCP 層過濾重復請求马篮,以前也有類似的提案想要做過濾沾乘,但因為無法保證安全性而被拒絕。所以 TFO 僅僅是避免了泛洪攻擊(類似于 backlog)浑测,但客戶端接收到的翅阵,和 SYN 包一起發(fā)來的數(shù)據(jù),依然有可能重復尽爆。不過也只有可能是 SYN 數(shù)據(jù)重復怎顾,所以 TFO 并不處理這種情況读慎,要求服務端程序自行解決漱贱。這也就是說,不僅僅要操作系統(tǒng)的支持夭委,更要求應用程序(比如 MySQL)也支持 TFO幅狮。

TFO 使得 TCP 協(xié)議有可能變成 0-RTT,核心思想和 Session Ticket 的概念類似: 將當前會話的上下文緩存在客戶端株灸。如果以后需要恢復對話崇摄,只需要將緩存發(fā)給服務器校驗,而不必花費一個 RTT 去等待慌烧。

結合 TFO 和 Session Ticket 技術逐抑,一個本來需要花費 3 個 RTT 才能完成的請求可以被優(yōu)化到一個 RTT。如果使用 QUIC 協(xié)議屹蚊,我們甚至可以更進一步厕氨,將 Session Ticket 也放到 TFO 中一起發(fā)送进每,這樣就實現(xiàn)了 0-RTT 的對話恢復。

QUIC 是怎么做的

讓我們看看 QUIC 是怎么做的命斧。

首先聲明一點田晚,如果一對使用 QUIC 進行加密通信的雙方此前從來沒有通信過,那么 0-RTT 是不可能的国葬,即便是 QUIC 也是不可能的贤徒。

QUIC 握手的過程需要一次數(shù)據(jù)交互,0-RTT 時延即可完成握手過程中的密鑰協(xié)商汇四,比 TLS 相比效率提高了 5 倍接奈,且具有更高的安全性。在握手過程中使用 Diffie-Hellman 算法協(xié)商初始密鑰通孽,初始密鑰依賴于服務器存儲的一組配置參數(shù)鲫趁,該參數(shù)會周期性的更新。初始密鑰協(xié)商成功后利虫,服務器會提供一個臨時隨機數(shù)挨厚,雙方根據(jù)這個數(shù)再生成會話密鑰。

具體握手過程如下:

(1) 客戶端判斷本地是否已有服務器的全部配置參數(shù)糠惫,如果有則直接跳轉到(5)疫剃,否則繼續(xù)

(2) 客戶端向服務器發(fā)送 inchoate client hello(CHLO) 消息,請求服務器傳輸配置參數(shù)

(3) 服務器收到 CHLO硼讽,回復 rejection(REJ) 消息巢价,其中包含服務器的部分配置參數(shù)

(4) 客戶端收到 REJ,提取并存儲服務器配置參數(shù)固阁,跳回到(1)

(5) 客戶端向服務器發(fā)送 full client hello 消息壤躲,開始正式握手,消息中包括客戶端選擇的公開數(shù)备燃。此時客戶端根據(jù)獲取的服務器配置參數(shù)和自己選擇的公開數(shù)碉克,可以計算出初始密鑰。

(6) 服務器收到 full client hello并齐,如果不同意連接就回復 REJ漏麦,同(3);如果同意連接况褪,根據(jù)客戶端的公開數(shù)計算出初始密鑰撕贞,回復 server hello(SHLO)消息,SHLO 用初始密鑰加密测垛,并且其中包含服務器選擇的一個臨時公開數(shù)捏膨。

(7) 客戶端收到服務器的回復,如果是 REJ 則情況同(4)食侮;如果是 SHLO号涯,則嘗試用初始密鑰解密熬北,提取出臨時公開數(shù)

(8) 客戶端和服務器根據(jù)臨時公開數(shù)和初始密鑰,各自基于 SHA-256 算法推導出會話密鑰

(9) 雙方更換為使用會話密鑰通信诚隙,初始密鑰此時已無用讶隐,QUIC 握手過程完畢。之后會話密鑰更新的流程與以上過程類似久又,只是數(shù)據(jù)包中的某些字段略有不同巫延。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市地消,隨后出現(xiàn)的幾起案子炉峰,更是在濱河造成了極大的恐慌,老刑警劉巖脉执,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疼阔,死亡現(xiàn)場離奇詭異,居然都是意外死亡半夷,警方通過查閱死者的電腦和手機婆廊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巫橄,“玉大人淘邻,你說我怎么就攤上這事∠婊唬” “怎么了宾舅?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彩倚。 經常有香客問我筹我,道長,這世上最難降的妖魔是什么帆离? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任蔬蕊,我火速辦了婚禮,結果婚禮上盯质,老公的妹妹穿的比我還像新娘袁串。我一直安慰自己概而,他們只是感情好呼巷,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赎瑰,像睡著了一般王悍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上餐曼,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天压储,我揣著相機與錄音鲜漩,去河邊找鬼。 笑死集惋,一個胖子當著我的面吹牛孕似,可吹牛的內容都是我干的。 我是一名探鬼主播刮刑,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼喉祭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雷绢?” 一聲冷哼從身側響起泛烙,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翘紊,沒想到半個月后蔽氨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡帆疟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年鹉究,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踪宠。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡坊饶,死狀恐怖,靈堂內的尸體忽然破棺而出殴蓬,到底是詐尸還是另有隱情匿级,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布染厅,位于F島的核電站痘绎,受9級特大地震影響,放射性物質發(fā)生泄漏肖粮。R本人自食惡果不足惜孤页,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涩馆。 院中可真熱鬧行施,春花似錦、人聲如沸魂那。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涯雅。三九已至鲜结,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背精刷。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工拗胜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怒允。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓埂软,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纫事。 傳聞我的和親對象是個殘疾皇子仰美,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354