過度到這篇文章可以先看HTTP/2淺析
雖然HTTP/2標(biāo)準(zhǔn)在2015年5月就以RFC 7540正式發(fā)表了调违,并且多數(shù)瀏覽器在2015年底就支持了穿撮。
但是签赃,真正被廣泛使用起來要到2018年左右忽妒,但是也是在2018年,11月IETF給出了官方批準(zhǔn)姜挺,認(rèn)可HTTP-over-QUIC成為HTTP/3齿税。
HTTP/2之所以"被棄用"彼硫,是因為他使用的傳輸層協(xié)議仍然是TCP炊豪,所以HTTP/3首要解決的問題就是繞開TCP
研發(fā)一種新的協(xié)議,同樣還是會因為受到中間設(shè)備僵化的影響拧篮,導(dǎo)致無法被大規(guī)模應(yīng)用词渤。所以,研發(fā)人員們想到了一種基于UDP實現(xiàn)的方式串绩。
我們現(xiàn)在所提到的HTTP/3缺虐,其實就是HTTP over QUIC,即基于QUIC協(xié)議實現(xiàn)的HTTP礁凡。
QUIC協(xié)議有以下特點
基于UDP的傳輸層協(xié)議:它使用UDP端口號來識別指定機器上的特定服務(wù)器高氮。
可靠性:雖然UDP是不可靠傳輸協(xié)議,但是QUIC在UDP的基礎(chǔ)上做了些改造顷牌,使得他提供了和TCP類似的可靠性剪芍。它提供了數(shù)據(jù)包重傳、擁塞控制窟蓝、調(diào)整傳輸節(jié)奏以及其他一些TCP中存在的特性罪裹。
實現(xiàn)了無序、并發(fā)字節(jié)流:QUIC的單個數(shù)據(jù)流可以保證有序交付运挫,但多個數(shù)據(jù)流之間可能亂序状共,這意味著單個數(shù)據(jù)流的傳輸是按序的,但是多個數(shù)據(jù)流中接收方收到的順序可能與發(fā)送方的發(fā)送順序不同谁帕!
快速握手:QUIC提供0-RTT和1-RTT的連接建立
使用TLS 1.3傳輸層安全協(xié)議:與更早的TLS版本相比峡继,TLS 1.3有著很多優(yōu)點,但使用它的最主要原因是其握手所花費的往返次數(shù)更低匈挖,從而能降低協(xié)議的延遲鬓椭。
QUIC協(xié)議 簡介
QUIC是基于UDP實現(xiàn)的,并且是HTTP/3的所依賴的協(xié)議关划,那么小染,按照TCP/IP的分層來講,他是屬于傳輸層的贮折,也就是和TCP裤翩、UDP屬于同一層。
因為QUIC不僅僅承擔(dān)了傳輸層協(xié)議的職責(zé),還具備了TLS的安全性相關(guān)能力踊赠,所以呵扛,可以通過下圖來理解QUIC在HTTP/3的實現(xiàn)中所處的位置。
QUIC協(xié)議 建立連接
TCP這種可靠傳輸協(xié)議需要進行三次握手筐带,也正是因為三次握手今穿,所以需要額外消耗1.5 RTT,而如果再加上TLS的話伦籍,則需要消耗3-4個 RTT連接蓝晒。
QUIC提出一種新的連接建立機制,基于這種連接接機制帖鸦,實現(xiàn)了快速握手功能芝薇,一次QUIC連接建立可以實現(xiàn)使用 0-RTT 或者 1-RTT 來建立連接。
圖片作儿。洛二。。
QUIC在握手過程中使用Diffie-Hellman算法來保證數(shù)據(jù)交互的安全性并合并了它的加密和握手過程來減小連接建立過程中的往返次數(shù)攻锰。
Diffie–Hellman (以下簡稱DH)密鑰交換是一個特殊的交換密鑰的方法晾嘶。它是密碼學(xué)領(lǐng)域內(nèi)最早付諸實踐的密鑰交換方法之一。 DH可以讓雙方在完全缺乏對方(私有)信息的前提條件下通過不安全的信道達成一個共享的密鑰娶吞。此密鑰用于對后續(xù)信息交換進行對稱加密垒迂。
QUIC 連接的建立整體流程大致為:QUIC在握手過程中使用Diffie-Hellman算法協(xié)商初始密鑰,初始密鑰依賴于服務(wù)器存儲的一組配置參數(shù)寝志,該參數(shù)會周期性的更新娇斑。初始密鑰協(xié)商成功后,服務(wù)器會提供一個臨時隨機數(shù)材部,雙方根據(jù)這個數(shù)再生成會話密鑰毫缆。客戶端和服務(wù)器會使用新生的的密鑰進行數(shù)據(jù)加解密乐导。
以上過程主要分為兩個步驟:初始握手(Initial handshake)苦丁、最終(與重復(fù))握手(Final (and repeat) handshake),分別介紹下這兩個過程
初始握手
在連接開始建立時物臂,客戶端會向服務(wù)端發(fā)送一個打招呼信息旺拉,(inchoate client hello (CHLO)),因為是初次建立棵磷,所以蛾狗,服務(wù)端會返回一個拒絕消息(REJ),表明握手未建立或者密鑰已過期仪媒。
但是沉桌,這個拒絕消息中還會包含更多的信息(配置參數(shù)),主要有:
- Server Config:一個服務(wù)器配置,包括服務(wù)器端的Diffie-Hellman算法的長期公鑰(long term Diffie-Hellman public value)
- Certificate Chain:用來對服務(wù)器進行認(rèn)證的信任鏈
- Signature of the Server Config:將Server Config使用信任鏈的葉子證書的public key加密后的簽名
- Source-Address Token:一個經(jīng)過身份驗證的加密塊留凭,包含客戶端公開可見的IP地址和服務(wù)器的時間戳佃扼。
在客戶端接收到拒絕消息(REJ)之后,客戶端會進行數(shù)據(jù)解析蔼夜,簽名驗證等操作兼耀,之后會將必要的配置緩存下來。
同時求冷,在接收到REJ之后瘤运,客戶端會為這次連接隨機產(chǎn)生一對自己的短期密鑰(ephemeral Diffie-Hellman private value) 和 短期公鑰(ephemeral Diffie-Hellman public value)。
之后遵倦,客戶端會將自己剛剛產(chǎn)生的短期公鑰打包一個Complete CHLO的消息包中尽超,發(fā)送給服務(wù)端官撼。這個請求的目的是將自己的短期密鑰傳輸給服務(wù)端梧躺,方便做前向保密
在發(fā)送了Complete CHLO消息給到服務(wù)器之后,為了減少RTT傲绣,客戶端并不會等到服務(wù)器的響應(yīng)掠哥,而是立刻會進行數(shù)據(jù)傳輸。
為了保證數(shù)據(jù)的安全性秃诵,客戶端會自己的短期密鑰和服務(wù)器返回的長期公鑰進行運算续搀,得到一個初始密鑰(initial keys)。
有了這個初識密鑰之后菠净,客戶端就可以用這個密鑰禁舷,將想要傳輸?shù)男畔⑦M行加密,然后把他們安全的傳輸給服務(wù)端了毅往。
接收到Complete CHLO請求的服務(wù)器牵咙,解析請求之后,就同時擁有了客戶端的短期公鑰和自己保存的長期密鑰攀唯。這樣通過運算洁桌,服務(wù)端就能得到一份和客戶端一模一樣的初始密鑰(initial keys)。
接下來他接收到客戶端使用初始密鑰加密的數(shù)據(jù)之后侯嘀,就可以使用這個初識密鑰進行解密了另凌,并且可以將自己的響應(yīng)再通過這個初始密鑰進行加密后返回給客戶端。
所以戒幔,從開始建立連接一直到數(shù)據(jù)傳送吠谢,只消耗了初始連接連接建立的 1 RTT
最終(與重復(fù))握手
之后的數(shù)據(jù)傳輸就可以使用初始密鑰(initial keys)加密了嗎?
其實并不完全是诗茎,因為初始密鑰畢竟是基于服務(wù)器的長期公鑰產(chǎn)生的工坊,而在公鑰失效前,幾乎多有的連接使用的都是同一把公鑰,所以栅组,這其實存在著一定的危險性雀瓢。
所以,為了達到前向保密 (Forward Secrecy) 的安全性玉掸,客戶端和服務(wù)端需要使用彼此的短期公鑰和自己的短期密鑰來進行運算刃麸。
在密碼學(xué)中,前向保密(英語:Forward Secrecy司浪,F(xiàn)S)是密碼學(xué)中通訊協(xié)議的安全屬性泊业,指的是長期使用的主密鑰泄漏不會導(dǎo)致過去的會話密鑰泄漏。
那么現(xiàn)在問題是啊易,客戶端的短期密鑰已經(jīng)發(fā)送給服務(wù)端吁伺,而服務(wù)端只把自己的長期密鑰給了客戶端,并沒有給到自己的短期密鑰租谈。
所以篮奄,服務(wù)端在收到Complete CHLO之后,會給到服務(wù)器一個server hello(SHLO)消息割去,這個消息會使用初始密鑰(initial keys)進行加密窟却。
這個CHLO消息包中,會包含一個服務(wù)端重新生成的短期公鑰呻逆。
這樣客戶端和服務(wù)端就都有了對方的短期公鑰(ephemeral Diffie-Hellman public value)夸赫。
這樣,客戶端和服務(wù)端都可以基于自己的短期密鑰和對方的短期公鑰做運算咖城,產(chǎn)生一個僅限于本次連接使用的前向保密密鑰 (Forward-Secure Key)茬腿,后續(xù)的請求發(fā)送,都基于這個密鑰進行加解密就可以了宜雀。
這樣切平,雙方就完成了最終的密鑰交換、連接的握手并且建立了QUIC連接州袒。
當(dāng)下一次要重新創(chuàng)建連接的時候揭绑,客戶端會從緩存中取出自己之前緩存下來的服務(wù)器的長期公鑰,并重新創(chuàng)建一個短期密鑰郎哭,重新生成一個初識密鑰他匪,再使用這個初始密鑰對想要傳輸?shù)臄?shù)據(jù)進行加密,向服務(wù)器發(fā)送一個Complete CHLO 請求即可夸研。這樣就達到了0 RTT的數(shù)據(jù)傳輸邦蜜。
所以,如果是有緩存的長期公鑰亥至,那么數(shù)據(jù)傳輸就會直接進行悼沈,準(zhǔn)備時間是0 RTT
以上贱迟,通過使用Diffie-Hellman算法協(xié)商密鑰,并且對加密和握手過程進行合并絮供,大大減小連接過程的RTT 衣吠,使得基于QUIC的連接建立可以少到1 RTT甚至0 RTT。
Google官網(wǎng)上面的一張關(guān)于QUIC連接建立的流程圖壤靶,可以幫助理解這個過程
另外缚俏,通過以上關(guān)于握手建立的過程,我們也可以知道贮乳,QUIC在整個過程中通過加解密的方式很好的保證了安全性忧换。
多路復(fù)用
基于TCP的協(xié)議實現(xiàn)的HTTP有一個最大的問題那就是隊頭阻塞問題,那么向拆,在這方面亚茬,QUIC是如何解決這個問題的呢?
TCP傳輸過程中會把數(shù)據(jù)拆分為一個個按照順序排列的數(shù)據(jù)包浓恳,這些數(shù)據(jù)包通過網(wǎng)絡(luò)傳輸?shù)搅私邮斩松卜欤邮斩嗽侔凑枕樞驅(qū)⑦@些數(shù)據(jù)包組合成原始數(shù)據(jù),這樣就完成了數(shù)據(jù)傳輸奖蔓。
但是如果其中的某一個數(shù)據(jù)包沒有按照順序到達赞草,接收端會一直保持連接等待數(shù)據(jù)包返回讹堤,這時候就會阻塞后續(xù)請求吆鹤。這就發(fā)生了TCP隊頭阻塞。
類似于HTTP/2洲守,QUIC在同一物理連接上可以有多個獨立的邏輯數(shù)據(jù)流疑务,這些數(shù)據(jù)流并行在同一個連接上傳輸,且多個數(shù)據(jù)流之間間的傳輸沒有時序性要求梗醇,也不會互相影響知允。
數(shù)據(jù)流(Streams)在QUIC中提供了一個輕量級、有序的字節(jié)流的抽象化
QUIC的單個數(shù)據(jù)流可以保證有序交付叙谨,但多個數(shù)據(jù)流之間可能亂序温鸽。這意味著單個數(shù)據(jù)流的傳輸是按序的,但是多個數(shù)據(jù)流中接收方收到的順序可能與發(fā)送方的發(fā)送順序不同手负!
也就是說同一個連接上面的多個數(shù)據(jù)流之間沒有任何依賴(不要求按照順序到達)涤垫,即使某一個數(shù)據(jù)包沒有達到,也只會影響自己這個數(shù)據(jù)流竟终,并不會影響到到其他的數(shù)據(jù)流蝠猬。
連接遷移
對于TCP連接的識別,需要通過服務(wù)器和客戶端過雙方的ip和端口四個參數(shù)進行的统捶。在網(wǎng)絡(luò)切換的場景中榆芦,比如手機切換網(wǎng)絡(luò)柄粹,那么自身的ip就會發(fā)生變化。這就導(dǎo)致之前的TCP連接就會失效匆绣,就需要重新建立驻右。
這種場景對于移動端設(shè)備普及的今天來說,還是比較頻繁的崎淳。
所以旺入,在這一點上,QUIC進行了優(yōu)化凯力。
QUIC協(xié)議使用特有的UUID來標(biāo)記每一次連接茵瘾,在網(wǎng)絡(luò)環(huán)境發(fā)生變化的時候,只要UUID不變咐鹤,就能不需要握手拗秘,繼續(xù)傳輸數(shù)據(jù)。
HTTP/2和HTTP/3的異同點
特性 | HTTP/2 | HTTP/3 |
---|---|---|
傳輸層協(xié)議 | TCP | 基于UDP的QUIC |
默認(rèn)加密 | 否 | 是 |
獨立的數(shù)據(jù)流 | 否 | 是 |
隊頭阻塞 | 存在TCP隊頭阻塞 | 無 |
報頭壓縮 | HPACK | QPACK |
握手時延 | TCP+TLS 的 1-3 RTT | 0-1 RTT |
連接遷移 | 無 | 有 |
服務(wù)器推送 | 有 | 有 |
多路復(fù)用 | 有 | 有 |
流量控制 | 有 | 有 |
數(shù)據(jù)重傳 | 有 | 有 |
擁塞控制 | 有 | 有 |