目錄
http2和http3的區別
傳輸層協議
QUIC協議
介紹
連接建立與握手
建立安全連接的過程
RTT
建連為什么需要兩個過程
原因
解決
QUIC協議的1-RTT 建連
必要性
連接過程
第一次握手(Client Hello)
版本號
其他
第二次握手
介紹
Server Hello
身份驗證
生成對稱密鑰
QUIC協議的0-RTT 建連
介紹
連接過程
隊頭阻塞
如何解決隊頭阻塞問題
安全性
性能
連接遷移
引入
介紹
連接 ID
過程
擁塞控制算法
介紹
TCP計算RTT
QUIC計算RTT
熱插拔特性
QUIC 的兩級流量控制
http2和http3的區別
傳輸層協議
HTTP/2:
基于 TCP(傳輸控制協議)實現
HTTP/3:
在 HTTP/3 中,棄用 TCP 協議,改為使用基于 UDP 協議的 QUIC 協議實現
- QUIC(Quick UDP Internet Connections)協議(傳輸層協議)
QUIC協議
介紹
QUIC 協議實現在用戶態,建立在內核態的 UDP 的基礎之上,集成了 TCP 的可靠傳輸特性+ TLS1.3 協議,保證了用戶數據傳輸的安全
- 具有建連快,連接遷移的優秀特性
- 解決了tcp的隊頭阻塞問題
- 實現了更好的擁塞/流量控制算法
連接建立與握手
HTTP/2:
需要進行 TCP 三次握手,然后再進行 TLS(傳輸層安全)握手
- 總共可能需要多次往返才能建立安全連接
建立安全連接的過程
首先,當客戶端使用域名訪問服務器時,需要借助DNS服務器來獲取ip地址
- DNS服務介紹(hosts文件,域名分級,主域名和子域名),域名解析過程(解析過程,dig命令), 面試題(輸入url后會發生什么)_hosts文件中的子域名-CSDN博客
傳統http的建連需要TCP和TLS兩個過程
- 三次握手需要1 RTT
- 交換秘鑰需要2 RTT -- 客戶端發起握手,服務端確認TLS版本+發送證書,客戶端內部進行驗證+生成對稱密鑰+加密發送,服務器確認
對于一個小請求(用戶數據量較小)而言,傳輸數據只需要 1 個 RTT
RTT
RTT --?(Round-Trip Time,往返時延)
- 從發送方發送一個數據包開始,到接收到接收方返回的應答包為止的總時間
建連為什么需要兩個過程
原因
因為TCP 和 TLS 沒辦法合并
TCP 是內核態協議:傳統的 TCP 協議實現是在內核中的,屬于操作系統內核的一部分,應用層無法修改其行為
TLS 是用戶態協議:TLS 是應用層協議,運行在用戶空間,比如瀏覽器或應用程序內
二者在物理上就不同(內核態/用戶態),所以無法合并
解決
如果把tcp挪到用戶態呢?
- 不可行,沒法棄用內核里的 TCP
所有操作系統和網絡設備(防火墻、路由器等)都已經高度適配和優化了 TCP 協議,內核級別實現是幾十年演進的結果
想在用戶態重寫 TCP,意味著你得重新實現擁塞控制、重傳機制、滑動窗口、頭部格式、與 OS 協同等等,成本極高,而且還不一定跑得過內核版 TCP
更關鍵的是,沒法讓全球其他服務器配合你一起改內核,這不現實
既然干不掉,那就自定義一個傳輸層協議放在用戶態.如何呢?
- 當然可行,既然 TCP 合并不了 TLS,那就自己造個輪子
- 在用戶態實現后,再結合 TLS.就可以把兩個建連過程合二為一了
- 這就是 QUIC協議 的實現思路
HTTP/3:
QUIC 協議將握手和加密過程合并,初次建連只需一次往返即可建立安全連接,減少了連接建立的延遲
- 并且,后續再次建連可以使用 0-RTT 特性
QUIC協議的1-RTT 建連
必要性
如果客戶端與服務端初次建連(之前從未進行通信過),或者長時間沒有通信過(0-RTT 過期了),只能進行 1-RTT 建連
- 只有先進行一次完整的 1-RTT 建連,后續一段時間內的通信才可以進行 0-RTT 建連
連接過程
整個握手過程需要 2 次握手(第三次是帶了數據的)
- 所以整個握手過程只需要 1-RTT(RTT 是指數據包在網絡上的一個來回)的時間
- 分成兩個部分 -- Client Hello(發送QUIC 連接信息) + Server Hello(TLS1.3 握手部分)
第一次握手(Client Hello)
QUIC連接 : 協商 QUIC 版本號、協商 quic 傳輸參數、生成連接 ID、確定 Packet Number 等信息
- 類似于 TCP 的 SYN 報文
版本號
Client Hello 在擴展字段里標明了支持的 TLS 版本(Supported Version:TLS1.3)
- 雖然這里寫的是TLS1.3,但實際上ClientHello 報文中的Version 字段寫的是
0x0303
(TLS 1.2)為什么要這樣設計?
- 為了兼容老舊的中間設備
- 很多網絡設備(比如負載均衡器、NAT、防火墻)會“窺探”TLS握手包內容,會根據報頭中的Version字段決定是否通過
- 但這些老舊設備 只認識 TLS 1.0、1.1、1.2 的標號,如果寫入1.3到Version字段,可能會導致拒絕連接/攔截握手,甚至丟包或斷鏈,所以只能寫入1.2
- 并且設計了一個擴展字段Supported Version來明確告訴服務器:我其實支持 TLS1.3
key_share
ClientHello 中包含了非常重要的 key_share 擴展
- 客戶端在發送之前,會自己根據 DHE 算法生成一個公私鑰對
- 發送 Client Hello 報文的時候會把這個公鑰(存在于 key_share 中)發過去,key_share 還包含了客戶端所選擇的曲線 X25519
- 總之,key_share 是客戶端提前生成好的公鑰信息
其他
Client Hello 里還包括了:客戶端支持的算法套、客戶端所支持的橢圓曲線以及簽名算法、psk 的模式等等,一起發給服務端。?
第二次握手
介紹
QUIC協議中,服務器會在第二次握手中一次性并發完成連接建立 + TLS 加密協商
- 會發送多個?QUIC 報文 :?Server Hello報文+用于服務端的身份認證和握手確認的報文
Server Hello
- 服務端自己根據 DHE 算法也生成了一個公私鑰對,同樣的,Key_share 擴展信息中也包含了 服務端的公鑰信息
- 服務端通過 ServerHello 報文將這些信息發送給客戶端
身份驗證
生成對稱密鑰
至此為止,雙方(客戶端服務端)都拿到了對方的公鑰信息,然后結合自己的私鑰信息,生成 pre-master key
- 在這里官方的叫法是(client_handshake_traffic_secret 和 server_handshake_traffic_secret)
- pre-master key屬于是一個中間結果,用于生成最終對稱加密密鑰的原材料
- 然后密鑰導出函數得到 key 和 iv(初始化向量),使用 key 和 iv 對 Server Hello 之后所有的握手消息進行加密
QUIC協議的0-RTT 建連
介紹
基于會話復用功能實現
- 在握手完成之后,服務端會發送一個 New Session Ticket 報文(0-RTT 實現的基礎)給客戶端
作用:
- QUIC 協議的 0-RTT 特性是指,在 首次建立連接后,后續連接可以利用以前的握手信息,跳過部分握手過程,直接開始數據傳輸,減少延遲
什么時候會再次建連呢?
- 連接超時(QUIC 會保存一定時間的連接信息,如 0-RTT 密鑰,允許客戶端重新發起連接時跳過傳統的 1-RTT 握手) 或 關閉(連接因某些原因被關閉或丟失(比如網絡切換)后的重新連接
- 連接斷開后的重試(網絡切換/設備休眠/客戶端IP地址改變)
- 客戶端重新連接到同一服務器(一些場景需要客戶端在之后的請求中繼續與同一個服務器建立連接)
連接過程
雖然client 和 server 在建連時,仍然需要兩次握手,仍然需要 1 個 rtt
- 但是client 在發送第一個包 client hello 時,就帶上了數據(HTTP 請求) --?從何時開始發送數據這個角度上來看,的確是 0-RTT
- 這個請求的數據并不會跟 Initial 報文(內含 Client Hello)一起發送,而是單獨一個數據包(0-RTT 包)進行發送,它和 Initial 包同時發送
隊頭阻塞
HTTP/2:
在 TCP 層面,如果發生數據包丟失,必須等待丟失的數據包被重新傳輸,可能導致隊頭阻塞,影響多路復用的效率
HTTP/3:
QUIC 協議在應用層實現了多路復用,即使某個數據包丟失,只會影響對應的流,其他流可以繼續傳輸,減少了隊頭阻塞的影響
如何解決隊頭阻塞問題
這里有兩個請求同時發送
- 紅色的是請求 1,藍色的是請求 2,這兩個請求在兩條不同的流中進行傳輸
假設在傳輸過程中,請求 1 的某個數據包丟了
- 如果是 TCP,即使請求 2 的所有數據包都收到了,但是也只能阻塞在內核緩沖區中,無法交給應用層 --?所有數據(無論屬于哪個請求)在傳輸時,都會進入一條順序一致的隊列中,一旦某個中間的數據包丟失,后面的數據即使已到達,也不能提交給應用層,因為順序被打亂了
- 但是 QUIC 就不一樣了,請求 1 的數據包丟了只會阻塞請求 1,請求 2 不會受到阻塞 --?QUIC 在應用層實現多路復用的獨立流,每個流都有自己的數據編號和確認機制
為什么只有 QUIC 才能解決呢?
- 雖然HTTP2中也有流的概念 --?HTTP/2 在用戶態實現了多路復用,用“幀”把多個請求的數據包混在一起發
- 但這些幀最后都變成一串字節交給 TCP 發送,TCP 的實現在內核態,而流的實現在用戶態,對于TCP來說,它是看不到“流”的
- 所以在 TCP 中,它不知道這個數據包是請求 1 還是請求 2 的,只會根據 seq number 來判斷包的先后順序,所以無法避免隊頭阻塞
舉個例子來說明兩個協議的不同:
安全性
HTTP/2:
通常與 TLS 結合使用,但在某些情況下也可以不使用加密
HTTP/3:
設計上強制使用加密,確保數據傳輸的安全性
性能
HTTP/3在弱網/高延遲/高丟包的場景下性能提升非常明顯
- 相較于 HTTP/2 平均能提升 20%~30%
- 并且可以在 WiFi 和蜂窩數據切換時,網絡完全不斷開、直播不卡頓、視頻不緩沖
為什么會有這么高的性能提升呢?
- HTTP3擁有更短的建連過程,還實現了連接遷移功能,并且優化了擁塞/流量控制
但不是說HTTP3就完爆HTTP2了,它在穩定高速網絡下,提升不一定明顯,甚至開銷可能略高
- 在高性能機器、內網等場景下,HTTP/2 的性能也很優秀
?
連接遷移
引入
我們經常需要在 WiFi 和 4G 之間進行切換,比如:
- 在家里時使用 WiFi,出門在路上,切換到流量
- 出了門,又可以連上其他場所的 WiFi
- 所以我們的日常生活中需要經常性的切換網絡
而每一次的切換網絡,都變化我們的 IP 地址
介紹
傳統的 TCP 協議是以四元組(源 IP 地址、源端口號、目的 ID 地址、目的端口號)來標識一條連接
- 一旦四元組的任何一個元素發生了改變,這條連接就會斷掉 -> 這條連接中正在傳輸的數據就會斷掉
- 切換到新的網絡后可能需要重新去建立連接,然后重新發送數據 -- 在用戶看來,就是他的網絡會“卡”一下
但是,QUIC 不再以四元組作為唯一標識,QUIC 使用連接 ID 來標識一條連接
- 無論網絡如何切換,只要連接 ID 不變,那么這條連接就不會斷,這就叫連接遷移
連接 ID
每條連接擁有一組連接標識符,也就是連接 ID
- 連接 ID 是由一端獨立選擇的,每個端(客戶端和服務端統稱為端)選擇連接 ID 供對端使用
- 也就是說,客戶端生成的連接 ID 供服務端使用(服務端發送數據時使用客戶端生成的連接 ID 作為目的連接 ID),反過來同理
連接 ID 的主要功能:
- 解決連接遷移時的識別問題,確保連接不會因 IP地址 /?端口號變化而中斷
過程
QUIC 限制連接遷移僅客戶端可以發起 (由客戶端負責發起所有遷移)
- 如果客戶端接收到了一個未知的服務器發來的數據包,客戶端必須丟棄這些數據包,而不會去識別 -- 可以避免偽造包/欺騙連接的風險
連接遷移過程總共需要四個步驟:
連接遷移之前,客戶端使用 IP1 和服務端進行通信
當客戶端 IP 變成 IP2,并且使用 IP2 發送非探測幀給服務端啟動路徑驗證(雙方都需要互相驗證,所以圖中是兩個過程)
通過 PATH_CHANLLENGE 幀和 PATH_RESPONSE 幀進行驗證
驗證通過后,后續就可以使用 IP2 進行通信
擁塞控制算法
介紹
擁塞控制算法中最重要的一個參數是 RTT,RTT 的準確性決定了擁塞控制算法的準確性
- RTT 是數據從發送端到接收端再返回所需的總時間,可以反映網絡時延變化,作為動態調整發送速率的依據
然而,TCP 的 RTT 測量往往不準確,QUIC 的 RTT 測量是準確的
TCP計算RTT
TCP協議中的原包和重傳包的序號是一樣的
- 那么擁塞控制算法進行計算 RTT 的時候,無法區別是初始包還是重傳包
- 也就導致 RTT 的計算值要么偏大,要么偏小
QUIC計算RTT
- QUIC 通過 Packet Number 來標識包的序號 (用來代替tcp的序號)
- 它規定 Packet Number 只能單調遞增,這也就解決了初始包和重傳包的二義性
- 從而保證 RTT 的值是準確的
熱插拔特性
不同于實現在內核態的TCP,實現在用戶態QUIC 的擁塞控制算法是可插拔的
- 它可以根據不同的業務 / 連接靈活選擇使用不同的擁塞控制算法
- Reno、New Reno、Cubic、BBR 等算法都有自己適合的場景
QUIC 的兩級流量控制
QUIC 是雙級流控 -- 連接級別 + 流級別的流控
每個流都有自己的可用窗口,可用窗口的大小取決于(最大窗口數 - 發送出去的最大偏移數)
- 跟中間已經發送出去的數據包,是否按順序收到了對端的 ACK 無關
這與TCP中的機制在本質上不同:
- TCP 的窗口滑動是依賴于“按序 ACK”,如果中間一個包沒 ACK,后面的都不能滑動 -- 也就是基于ACK滑動流控窗口
而 QUIC 是“基于 offset 的流控”,只關心你已經發送到了哪個 offset,不管你中間的 ACK 來沒來 (這一點和http2類似,它在發送時可以將一個包分成多個幀,每個幀帶上偏移量,接收方根據偏移量拼接報文,而不要求按序到達) -- ACK只是用來進行丟包檢測和重傳,不作為窗口滑動的依據
參考 --?一文讀懂QUIC協議:更快、更穩、更高效的網絡通信_后端_李龍彥_InfoQ精選文章
(寫的超級好啊o( ̄▽ ̄)d)