TCP 隊(duì)頭阻塞的主要原因是數(shù)據(jù)包超時(shí)確認(rèn)或丟失阻塞了當(dāng)前窗口向右滑動(dòng),我們最容易想到的解決隊(duì)頭阻塞的方案是不讓超時(shí)確認(rèn)或丟失的數(shù)據(jù)包將當(dāng)前窗口阻塞在原地。QUIC (Quick UDP Internet Connections)也正是采用上述方案來(lái)解決TCP 隊(duì)頭阻塞問(wèn)題的。
TCP 為了保證可靠性,使用了基于字節(jié)序號(hào)的 Sequence Number 及 Ack 來(lái)確認(rèn)消息的有序到達(dá)。QUIC 同樣是一個(gè)可靠的協(xié)議,它使用 Packet Number 代替了 TCP 的 Sequence Number,并且每個(gè) Packet Number 都嚴(yán)格遞增,也就是說(shuō)就算 Packet N 丟失了,重傳的 Packet N 的 Packet Number 已經(jīng)不是 N,而是一個(gè)比 N 大的值,比如Packet N M。
QUIC 使用的Packet Number 單調(diào)遞增的設(shè)計(jì),可以讓數(shù)據(jù)包不再像TCP 那樣必須有序確認(rèn),QUIC 支持亂序確認(rèn),當(dāng)數(shù)據(jù)包Packet N 丟失后,只要有新的已接收數(shù)據(jù)包確認(rèn),當(dāng)前窗口就會(huì)繼續(xù)向右滑動(dòng)。 待發(fā)送端獲知數(shù)據(jù)包Packet N 丟失后,會(huì)將需要重傳的數(shù)據(jù)包放到待發(fā)送隊(duì)列,重新編號(hào)比如數(shù)據(jù)包Packet N M 后重新發(fā)送給接收端,對(duì)重傳數(shù)據(jù)包的處理跟發(fā)送新的數(shù)據(jù)包類似,這樣就不會(huì)因?yàn)閬G包重傳將當(dāng)前窗口阻塞在原地,從而解決了隊(duì)頭阻塞問(wèn)題。那么,既然重傳數(shù)據(jù)包的Packet N M 與丟失數(shù)據(jù)包的Packet N 編號(hào)并不一致,我們?cè)趺创_定這兩個(gè)數(shù)據(jù)包的內(nèi)容一樣呢? 還記得前篇博文:HTTP/2 是如何解決HTTP/1.1 性能瓶頸的?使用Stream ID 來(lái)標(biāo)識(shí)當(dāng)前數(shù)據(jù)流屬于哪個(gè)資源請(qǐng)求,這同時(shí)也是數(shù)據(jù)包多路復(fù)用傳輸?shù)浇邮斩撕竽苷=M裝的依據(jù)。重傳的數(shù)據(jù)包Packet N M 和丟失的數(shù)據(jù)包Packet N 單靠Stream ID 的比對(duì)一致仍然不能判斷兩個(gè)數(shù)據(jù)包內(nèi)容一致,還需要再新增一個(gè)字段Stream Offset,標(biāo)識(shí)當(dāng)前數(shù)據(jù)包在當(dāng)前Stream ID 中的字節(jié)偏移量。
有了Stream Offset 字段信息,屬于同一個(gè)Stream ID 的數(shù)據(jù)包也可以亂序傳輸了(HTTP/2 中僅靠Stream ID 標(biāo)識(shí),要求同屬于一個(gè)Stream ID 的數(shù)據(jù)幀必須有序傳輸),通過(guò)兩個(gè)數(shù)據(jù)包的Stream ID 與 Stream Offset 都一致,就說(shuō)明這兩個(gè)數(shù)據(jù)包的內(nèi)容一致。
上圖中數(shù)據(jù)包Packet N 丟失了,后面重傳該數(shù)據(jù)包的編號(hào)為Packet N 2,丟失的數(shù)據(jù)包和重傳的數(shù)據(jù)包Stream ID 與 Offset 都一致,說(shuō)明這兩個(gè)數(shù)據(jù)包的內(nèi)容一致。這些數(shù)據(jù)包傳輸?shù)浇邮斩撕?,接收端能根?jù)Stream ID 與 Offset 字段信息正確組裝成完整的資源。
QUIC 通過(guò)單向遞增的Packet Number,配合Stream ID 與 Offset 字段信息,可以支持非連續(xù)確認(rèn)應(yīng)答Ack而不影響數(shù)據(jù)包的正確組裝,擺脫了TCP 必須按順序確認(rèn)應(yīng)答Ack 的限制(也即不能出現(xiàn)非連續(xù)的空位),解決了TCP 因某個(gè)數(shù)據(jù)包重傳而阻塞后續(xù)所有待發(fā)送數(shù)據(jù)包的問(wèn)題(也即隊(duì)頭阻塞問(wèn)題)。
QUIC 解決了TCP 的隊(duì)頭阻塞問(wèn)題,同時(shí)繼承了HTTP/2 的多路復(fù)用優(yōu)點(diǎn),因?yàn)镾tream Offset 字段的引入,QUIC 中同一Stream ID 的數(shù)據(jù)幀也支持亂序傳輸,不再像HTTP/2 要求的同一Stream ID 的數(shù)據(jù)幀必須有序傳輸那么嚴(yán)格。
從上面QUIC 的數(shù)據(jù)包結(jié)構(gòu)中可以看出,同一個(gè)Connection ID 可以同時(shí)傳輸多個(gè)Stream ID,由于QUIC 支持非連續(xù)的Packet Number 確認(rèn),某個(gè)Packet N 超時(shí)確認(rèn)或丟失,不會(huì)影響其它未包含在該數(shù)據(jù)包中的Stream Frame 的正常傳輸。
同一個(gè)Packet Number 可承載多個(gè)Stream Frame,若該數(shù)據(jù)包丟失,則其承載的Stream Frame 都需要重新傳輸。因?yàn)橥籗tream ID 的數(shù)據(jù)幀亂序傳輸后也能正確組裝,這些需要重傳的Stream Frame 并不會(huì)影響其它待發(fā)送Stream Frame 的正常傳輸。
TCP 建立連接的三次握手過(guò)程都做了哪些工作呢?首先確認(rèn)雙方是否能正常收發(fā)數(shù)據(jù),通信雙方交換待發(fā)送數(shù)據(jù)的初始序列編號(hào)并作為有序確認(rèn)應(yīng)答的基點(diǎn),通信雙方根據(jù)預(yù)設(shè)的狀態(tài)轉(zhuǎn)換圖完成各自的狀態(tài)遷移過(guò)程,通信雙方為分組數(shù)據(jù)的可靠傳輸和狀態(tài)信息的記錄管理分配控制塊緩存資源等。下面給出TCP 連接建立、數(shù)據(jù)傳輸、連接釋放三個(gè)階段的報(bào)文交互過(guò)程和狀態(tài)遷移圖示(詳見(jiàn)博文:TCP協(xié)議與Transmission Control Protocol):
到了移動(dòng)互聯(lián)網(wǎng)時(shí)代,客戶端(比如手機(jī))的位置可能一直在變,接入不同的基站可能就會(huì)被分配不同的Source IP 和Source Port。即便在家里,客戶端可能也需要在LTE 和WIFI 之間切換,這兩個(gè)網(wǎng)絡(luò)分配給客戶端的Source IP 和Source Port 可能也是不同的。
早期移動(dòng)電話使用Mobile IP 技術(shù)來(lái)解決網(wǎng)絡(luò)遷移或切換過(guò)程引起的斷連問(wèn)題,Mobile IP 主要是通過(guò)新建IP 隧道的方式(也即建立一個(gè)新連接來(lái)轉(zhuǎn)發(fā)數(shù)據(jù)包)保持原來(lái)的連接不斷開(kāi),但這種方式增加了數(shù)據(jù)包的傳輸路徑,也就增大了數(shù)據(jù)包的往返時(shí)間,降低了數(shù)據(jù)包的傳輸效率。Mobile IP 的工作原理如下(移動(dòng)主機(jī)遷移到外部代理后,為了保持原連接不斷開(kāi),新建了一條到歸屬代理的IP 隧道,讓歸屬代理以原主機(jī)IP 轉(zhuǎn)發(fā)數(shù)據(jù)包):
QUIC 擺脫了TCP 的諸多限制,可以重新設(shè)計(jì)連接標(biāo)識(shí),還記得前面給出的QUIC 數(shù)據(jù)包結(jié)構(gòu)嗎?QUIC 數(shù)據(jù)包結(jié)構(gòu)中有一個(gè)Connection ID 字段專門標(biāo)識(shí)連接,Connection ID 是一個(gè)64位的通用唯一標(biāo)識(shí)UUID (Universally Unique IDentifier)。
借助Connection ID,QUIC 的連接不再綁定IP 與 Port 信息,即便因?yàn)榫W(wǎng)絡(luò)遷移或切換導(dǎo)致Source IP 和Source Port 發(fā)生變化,只要Connection ID 不變就仍是同一個(gè)連接,協(xié)議層只需要將控制塊中記錄的Source IP 和Source Port 信息更新即可,不需要像TCP 那樣先斷開(kāi)連接,這就可以保證連接的順暢遷移或切換,用戶基本不會(huì)感知到連接切換過(guò)程。
由于TCP 重傳 segment 的 Sequence Number 和原始的 segment 的 Sequence Number 保持不變,當(dāng)發(fā)送端觸發(fā)重傳數(shù)據(jù)包Sequence N后,接收到了該數(shù)據(jù)包,發(fā)送端無(wú)法判斷接收到的數(shù)據(jù)包是來(lái)自原始請(qǐng)求的響應(yīng),還是來(lái)自重傳請(qǐng)求的響應(yīng),這就帶來(lái)了TCP 重傳的歧義性,該問(wèn)題肯定會(huì)影響采樣RTT 測(cè)量值的準(zhǔn)確性,進(jìn)而影響重發(fā)超時(shí)閾值計(jì)算的準(zhǔn)確度,可能會(huì)增大數(shù)據(jù)包超時(shí)重傳的概率。
QUIC 采用單向遞增的Packet Number 來(lái)標(biāo)識(shí)數(shù)據(jù)包,原始請(qǐng)求的數(shù)據(jù)包與重傳請(qǐng)求的數(shù)據(jù)包編號(hào)并不一樣,自然也就不會(huì)引起重傳的歧義性,采樣RTT 的測(cè)量更準(zhǔn)確。