聊一聊HTTP/3, QUIC 它們是怎么工作的?
為什么我們需要HTTP/3呢?一個重要原因是解決了“頭阻塞”問題。
HTTP/2中的頭阻塞問題
HTTP/2通過幀和流在HTTP級別解決了頭阻塞問題。但是,在TCP級別問題仍然存在。
在接收來自上層的幀后,TCP會將它們分成段。
如果一切順利,所有段將到達另一端。
然而,互聯(lián)網(wǎng)可能不穩(wěn)定。在這個過程中,一些段可能會丟失。
TCP有一個保證傳遞的功能。它將接收到的段放入緩沖區(qū),并等待丟失的段重新傳輸,從而導致頭阻塞。
為了解決這個問題,我們需要找到TCP的替代品——QUIC和UDP。
更新的協(xié)議棧
從協(xié)議棧中可以看到一個重大的變化:TCP被UDP取代。
不同于TCP,UDP不保證傳遞,段之間沒有依賴關(guān)系。這意味著不再會有頭阻塞問題。
此外,由于UDP是一種無連接的協(xié)議,不需要握手。它運行速度比TCP更快。
在UDP的基礎(chǔ)上,引入了一個新協(xié)議QUIC。它繼承了TCP的一些優(yōu)點,包括連接管理和流控制。此外,QUIC實現(xiàn)了一些功能來保證數(shù)據(jù)傳遞,以彌補UDP的不足。
另一個變化是在QUIC內(nèi)部實現(xiàn)了TLS,同時繼承了其所有安全特性。由于TLS 1.3已經(jīng)投入生產(chǎn),QUIC從這個版本開始。
最后但同樣重要的是,QPACK取代了HPACK,進一步提高了頭部壓縮算法的性能。靜態(tài)表中的條目從61增加到98,并且現(xiàn)在是0索引的。
QUIC數(shù)據(jù)包、幀和流
QUIC由數(shù)據(jù)包和幀組成。一個數(shù)據(jù)包由多個幀組成。
以下是QUIC數(shù)據(jù)包的結(jié)構(gòu)。
在數(shù)據(jù)包頭中,QUIC使用連接ID來標記其目標和源。
瀏覽器和服務(wù)器可以選擇它們的ID。有了它們,我們可以將連接從IP和端口中解耦,實現(xiàn)平穩(wěn)的連接遷移。
以下情況可能每天都會發(fā)生在您身上。
當你離開家時,你的手機會從WiFi切換到4G(很快是5G)。由于IP變化,TCP會重新連接。在重新連接到互聯(lián)網(wǎng)之前,您將在一瞬間失去連接。
使用QUIC,連接ID保持不變,因此連接在概念上保持不變。盡管IP發(fā)生變化,但連接被重用,沒有重新連接的成本。
接下來,讓我們看一個QUIC數(shù)據(jù)包的示例。
QUIC IETF
QUIC連接信息
[數(shù)據(jù)包長度:1350]
1... .... = 頭部形式:長頭部(1)
.1.. .... = 固定位:True
..00 .... = 數(shù)據(jù)包類型:初始(0)
.... 00.. = 保留:0
.... ..00 = 包號長度:1字節(jié)(0)
版本:draft-29(0xff00001d)
目標連接ID長度:8
目標連接ID:45fb5955dfaa8914
源連接ID長度:0
令牌長度:0
長度:1332
數(shù)據(jù)包號碼:1
負載:5a99e5b29413627619ca3b5add4cf8b6ce348355b1c1a2be9874c7961e7996a24aeec860…
TLSv1.3記錄層:握手協(xié)議:客戶端Hello
填充長度:997
從公共標志1100 0000中,我們可以知道它是一個長頭部,其類型是初始的。接下來是QUIC版本:draft-29,然后是目標連接ID及其長度。
接下來,讓我們看QUIC幀結(jié)構(gòu)。
類似于HTTP/2幀,QUIC中有各種幀類型。
例如,STREAM幀用于攜帶流,而ACK幀用于控制。
標題中的字段使用可變長度編碼,最多可達8字節(jié)。
流標識符可以達到2^62,其中兩位保留為標記符。
- 最不顯著的位標記發(fā)送方:0表示客戶端,1表示服務(wù)器。
- 第二位最不顯著的位標記流的方向:0表示雙向流,1表示單向流。
以下是幀的示例。
- TLSv1.3記錄層:握手協(xié)議:客戶端Hello
- 幀類型:CRYPTO(0x0000000000000006)
- 偏移:0
- 長度:314
- 加密數(shù)據(jù)
- 握手協(xié)議:客戶端Hello
- 幀類型是CRYPTO,這是為握手設(shè)計的類型,負載是加密數(shù)據(jù)。
下面是另一個示例,服務(wù)器Hello。
- TLSv1.3記錄層:握手協(xié)議:服務(wù)器Hello
- 幀類型:CRYPTO(0x0000000000000006)
- 偏移:0
- 長度:90
- 加密數(shù)據(jù)
- 握手協(xié)議:服務(wù)器Hello
- 握手類型:服務(wù)器Hello(2)
- 長度:86
- 版本:TLS 1.2(0x0303)
- 隨機數(shù):0f58bdbd934450c7aa98242121447bef2fe0733aa5fc3beffab6513c7177f9a4
- 會話ID長度:0
- 密碼套件:TLS_AES_128_GCM_SHA256(0x1301)
- 壓縮方法:null(0)
- 擴展長度:46
- 擴展:key_share(len=36)
- 擴展:supported_versions(len=2)
除了QUIC幀的新字段外,其余的字段在TLS 1.3握手中都有提到。
HTTP/3協(xié)議和幀
QUIC可以完成很多工作,減輕了HTTP/3的工作負擔。
例如,與HTTP/2不同,HTTP/3利用QUIC流,而不是自己定義和控制流。
在HTTP/2中管理的大多數(shù)幀類型都移到了QUIC,如RST_STREAM幀和WINDOW_UPDATE幀。
由于這一點,HTTP/3的幀結(jié)構(gòu)簡化為僅有2個字段——幀類型和長度。
有一點值得一提,HTTP/3沒有像HTTPS的443端口一樣的指定端口。
瀏覽器首先使用HTTP/2與服務(wù)器建立連接以發(fā)現(xiàn)服務(wù)。服務(wù)器會響應(yīng)帶有Alt-Svc頭部的請求,其中包括HTTP/3的端口,例如Alt-Svc: h3-29=":443"。有了這個信息,瀏覽器異步地連接到該端口。一旦連接建立,將使用HTTP/3進行未來的通信。