一、QUIC是什么南用?
????????QUIC是快速UDP網(wǎng)絡(luò)連接(Quick UDP Internet Connections)的縮寫,? 是Google公司研發(fā)的一種基于? UDP? 協(xié)議的低時延互聯(lián)網(wǎng)傳輸協(xié)議。在過去,QUIC用在HTTP上苹粟,后來IETF(國際互聯(lián)網(wǎng)工程任務(wù)組)發(fā)現(xiàn)QUIC是個好東西,于是希望把QUIC從HTTP中分離出來(如 : IP / UDP / QUIC / HTTP)跃闹。在2016年IETF會議中嵌削,HTTP-over-QUIC協(xié)議被重命名為HTTP/3,并成為? HTTP? 協(xié)議的第三個正式版本望艺。
????????社區(qū)中的人們已經(jīng)使用非正式名稱如iQUIC和gQUIC來指代這些不同版本的協(xié)議苛秕,以將QUIC協(xié)議與IETF和Google分開,在wireshark中可以通過輸入gquic或quic過濾quic報文荣茫。
QUIC是個很大的協(xié)議想帅,位于傳輸層,使用TLS 1.3(RFC 8446)啡莉,在UDP之上實現(xiàn)類似于TCP + TLS + HTTP/2港准。
QUIC相關(guān)時間:
2013 年由Google提出
2015 年被提議作為 IETF 的標(biāo)準(zhǔn)草案
2016年11月IETF召開了第一次QUIC工作組會議
二、為什么是UDP咧欣,而不是TCP浅缸?
吐槽歷代HTTP
1、HTTP0.9 只有Get方法
2魄咕、HTTP1.0 使用短連接衩椒,每次請求都要經(jīng)過三次握手,四次揮手。需要使用keep-alive參數(shù)來告知服務(wù)器端要建立一個長連接毛萌,而HTTP1.1默認(rèn)支持長連接苟弛。
3、HTTP 1.1 隊頭阻塞阁将。
4膏秫、HTTP2.0? IETF借鑒了SPDY,雖然消除了以請求為單元的隊頭阻塞做盅,但是請求內(nèi)是以TCP同步發(fā)送缤削,沒能根治隊頭阻塞。
2吹榴、吐槽TCP:
? ? ? ? a. TCP由操作系統(tǒng)內(nèi)核實現(xiàn)亭敢,如果要改變,那么所有操作系統(tǒng)都得改變图筹,不現(xiàn)實帅刀。
????????b. TCP對報文控制太精細(xì),很難有改進(jìn)的空間远剩,數(shù)據(jù)包經(jīng)過嚴(yán)格控制劝篷,不能來一場說走就走的旅行。曾經(jīng)Google有人提出TFO (tcp fast open)對TCP擴(kuò)展民宿,也提交了RFC,但是沒普及像鸡。
? ? ? ? c. UDP報文結(jié)構(gòu)簡單活鹰,在此基礎(chǔ)上做文章很方便。而且QUIC并非基于系統(tǒng)內(nèi)核修改只估,可以進(jìn)行快速迭代更新志群。
3、吐槽完:
UDP是不可靠傳輸蛔钙,IP也是不可靠傳輸锌云。為啥QUIC不直接建立在IP上呢?
世界上有很多終端設(shè)備通過NAT通信吁脱,所有的NAT產(chǎn)品都支持TCP/UDP桑涎,根據(jù)IP+PORT與終端的會話一一對應(yīng)。如果QUIC建立在IP之上兼贡,并沒有端口號攻冷,意味著每一個NAT設(shè)備只能記憶一個終端的會話,一個全球IP 與一個私有IP的一一映射遍希。那將意味著NAT設(shè)備后只能有一個會話可以訪問同一個服務(wù)器的頁面等曼。那還不如趕緊普及IPv6。
再退一步,即使在IP層之上給到QUIC端口號禁谦,那也得所有的NAT設(shè)備都得修改可以兼容QUIC的規(guī)則胁黑。所以QUIC建立在UDP上是唯一出路翰灾。
4虑绵、QUIC相比現(xiàn)在廣泛應(yīng)用的TCP + TLS + HTTP/2協(xié)議有如下優(yōu)勢:
(1)通過減少往返次數(shù),以縮短連接建立時間
(2)多路復(fù)用哩俭,解決HTTP/2隊頭阻塞問題
????????QUIC 最基本的傳輸單元是 Packet拥诡,不會超過 MTU 的大小触趴,整個加密和認(rèn)證過程都是基于 Packet 的,不會跨越多個 Packet渴肉。這樣就能避免 TLS 協(xié)議存在的隊頭阻塞冗懦。Stream 之間相互獨立,比如 Stream2 丟了一個 Pakcet仇祭,不會影響 Stream3 和 Stream4披蕉。不存在 TCP 隊頭阻塞。
????????QUIC? 的流量控制類似? HTTP2乌奇,即在? Connection? 和? Stream? 級別提供了兩種流量控制没讲。為什么需要兩類流量控制呢?主要是因為? QUIC? 支持多路復(fù)用礁苗。Stream? 可以認(rèn)為就是一條? HTTP? 請求爬凑。Connection? 可以類比一條? TCP? 連接。多路復(fù)用意味著在一條? Connetion? 上會同時存在多條? Stream试伙。既需要對單個? Stream? 進(jìn)行控制嘁信,又需要針對所有? Stream? 進(jìn)行總體控制。相當(dāng)于rabbitmq的channel和Connection的關(guān)系疏叨。
????????簡單來說潘靖,http2解決http1.1在應(yīng)用層中隊頭阻塞的問題,但是沒有解決傳輸層TCP的單個報文的重傳阻塞(傳輸層的隊頭阻塞沒有解決)蚤蔓。而UDP沒有報文阻塞這個問題卦溢,所以基于UDP的quic協(xié)議,可以更深一層的解決阻塞問題秀又。
(3)使用FEC(前向糾錯)恢復(fù)丟失的包单寂,以減少超時重傳
????????前向糾錯是一種差錯控制方式,它是指信號在被送入傳輸信道之前預(yù)先按一定的算法進(jìn)行編碼處理吐辙,加入帶有信號本身特征的冗碼凄贩,在接收端按照相應(yīng)算法對接收到的信號進(jìn)行解碼,從而找出在傳輸過程中產(chǎn)生的錯誤碼并將其糾正的技術(shù)袱讹。QUIC用的是一階冗余疲扎,如果有一個Group內(nèi)有其中一條QUIC報文出錯可以修正昵时。如果有超過一個QUIC報文出錯,則NACK椒丧。
(4)使用一個隨機(jī)數(shù)(CID)標(biāo)志一個連接壹甥,取代傳統(tǒng)IP + PORT的方式,使得切換網(wǎng)絡(luò)環(huán)境如從4G到wifi仍然能使用之前的連接壶熏。
? ? ? ? 當(dāng)使用手機(jī)在 WIFI 和 4G 移動網(wǎng)絡(luò)切換時句柠,客戶端的 IP 肯定會發(fā)生變化,需要重新建立和服務(wù)端的 TCP 連接棒假。而任何一條 QUIC 連接不再以 IP 及端口四元組標(biāo)識溯职,而是以一個 64 位的隨機(jī)數(shù)作為 ID 來標(biāo)識(?由于這個 ID 是客戶端隨機(jī)產(chǎn)生的,并且長度有 64 位帽哑,所以沖突概率非常低)谜酒,這樣就算 IP 或者端口發(fā)生變化時,只要 ID 不變妻枕,這條連接依然維持著僻族,上層業(yè)務(wù)邏輯感知不到變化,不會中斷屡谐,也就不需要重連述么。
三、QUIC報文
參考?https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#ref-3
第一字節(jié)為:Public ?Flags:
Public Flag的8位如下所示愕掏,左邊為高位度秘,右邊為低位
public flags
如果Bit0被置上(0x01 = PUBLIC_FLAG_VERSION = 0x01),該字段的含義取決于報文是客戶端還是服務(wù)端發(fā)送饵撑。如果是客戶端發(fā)送敷钾,這一位被置上表示QUIC Version字段不為空,且QUIC Version字段被填充為客戶端的QUIC版本信息肄梨。在客戶端收到服務(wù)端同意建立該版本的連接報文抵達(dá)之前,客戶端發(fā)送的所有報文的這一位必須都要被設(shè)置挠锥。如果服務(wù)端同意建立該版本的連接众羡,那么這一位不用設(shè)置。如果這一位被服務(wù)端置上蓖租,那么表示該報文是版本協(xié)商報文(Version Negotiation Packet)粱侣。
如果Bit1被置上(0x02 = PUBLIC_FLAG_RESET),表明該報文是公共重置報文(Public Reset packet)
Bit2蓖宦、Bit3兩位表示報文中的Connection ID的長度齐婴。直至協(xié)商另外一個值之前,在所有的報文中稠茂,這兩位必須設(shè)置被設(shè)置為相同(客戶端可能請求更少的數(shù)據(jù)柠偶,所以ConnectID的長度需要變化)
0x0C(Bit3及Bit2均為1)表示Connection ID長度是8字節(jié)
0x08(Bit3位1情妖,Bit2為0)表示Connection ID長度是4字節(jié)
0x04(Bit3位0,Bit2為1)表示Connection ID長度是1字節(jié)
0x00(Bit3及Bit2均為0)表示無Connection ID
Bit4诱担、Bit5兩位表示每個數(shù)據(jù)包中存在的數(shù)據(jù)包編號的字節(jié)數(shù)毡证。對于幀數(shù)據(jù),這兩位才使用蔫仙,對于Public Reset報文及Version Negotiation報文料睛,這兩位必須為0
0x30(Bit5及Bit4均為1)表示:包序號是6個字節(jié)
0x20(Bit5為1,Bit4為0)表示:包序號是4個字節(jié)
0x10(Bit5為0摇邦,Bit4為1)表示:包序號是2個字節(jié)
0x00(Bit5及Bit4均為0)表示:包序號是1個字節(jié)
Bit6:預(yù)留給多路徑使用
Bit7:未使用恤煞,必須為0
Connection ID:客戶端隨機(jī)選擇的最大長度為64位的無符號整數(shù)。但是施籍,長度可以協(xié)商居扒。
QUIC Version:QUIC協(xié)議的版本號,32位的可選字段法梯。如果Public Flag & FLAG_VERSION != 0苔货,這個字段必填×⒀疲客戶端設(shè)置Public Flag中的Bit0為1夜惭,并且填寫期望的版本號。如果客戶端期望的版本號服務(wù)端不支持铛绰,服務(wù)端設(shè)置Public Flag中的Bit0為1诈茧,并且在該字段中列出服務(wù)端支持的協(xié)議版本(0或者多個),并且該字段后不能有任何報文捂掰。
Packet Number:長度取決于Public Flag中Bit4及Bit5兩位的值敢会,最大長度6字節(jié)。發(fā)送端在每個普通報文中設(shè)置Packet Number这嚣。發(fā)送端發(fā)送的第一個包的序列號是1鸥昏,隨后的數(shù)據(jù)包中的序列號的都大于前一個包中的序列號。
QUIC報文分為:
1姐帚、特殊報文
版本協(xié)商報文(Version Negotiation Packets)
只由服務(wù)端發(fā)送吏垮。版本協(xié)議報文以1字節(jié)的Public Flag及8字節(jié)的Connection ID開始。必須設(shè)置PUBLIC_FLAG_VERSION且標(biāo)識Connection ID長度為8字節(jié)罐旗,最后面就是服務(wù)端支持的協(xié)議版本(4字節(jié))膳汪。
公共重置報文(Public Reset Packets)
Public Reset報文以1字節(jié)的Public Flag及8字節(jié)的Connection ID開始。必須設(shè)置PUBLIC_FLAG_RESET且標(biāo)識Connection ID長度為8字節(jié)九秀,剩余的部分是QUIC Tag遗嗽。
2、普通報文
普通的報文被驗證(authenticated)且被加密鼓蜒。公有頭被驗證但是沒有加密痹换,從Private Flags字段開始的報文被加密征字。普通的報文包含AEAD(authenticated encryption and associated data)報文。普通報文必須被解密晴音,并且密文被解密后柔纵,明文以Private Header開始。
幀報文
除了私有頭外锤躁,幀包有一系列的基于類型的幀數(shù)據(jù)的負(fù)載搁料,通用的幀包的格式
FEC(Forward Error Correction)報文
FEC包(FLAG_FEC標(biāo)志被置為1)的負(fù)載只包含位于FEC組中的每個數(shù)據(jù)包的空填充負(fù)載的XOR值。每個FEC包的FLAG_FEC_GROUP標(biāo)志也必須被置為1
四系羞、瀏覽器中查看QUIC
有豆瓣郭计,google,F(xiàn)acebook椒振,YouTube昭伸,QQ空間。以下是F12豆瓣網(wǎng)的澎迎,可以發(fā)現(xiàn)豆瓣有在用HTTP1.1庐杨,HTTP2.0(h2),QUIC夹供。
在Google新版的Chrome瀏覽器中灵份,支持QUIC協(xié)議,在Chrome瀏覽器中打開“實驗性功能”頁面(chrome://flags/)哮洽,把Experimental QUIC protocol設(shè)置enabled填渠。
五、抓包驗證
抓不到正常通信的包鸟辅。將就看個版本匹配失敗的例子? - -
客戶端發(fā)送一個CHLO報文氛什,報文里Public Flags的bit0(Version)設(shè)置1,而且設(shè)置Version值Q039匪凉。創(chuàng)建CID連接號枪眉。
服務(wù)端收到客戶端的連接請求,返回協(xié)商報文再层,Public Flags的bit0(Version)設(shè)置1贸铜,并返回可支持的QUIC Version版本列表。
客戶端發(fā)現(xiàn)服務(wù)端返回的版本列表里树绩,并沒有符合的,于是發(fā)送CONNECTION_CLOSE消息隐轩,關(guān)閉通信饺饭。雖然看到連接關(guān)閉的字樣,但實際上我是可以訪問到豆瓣的职车,因為此時TCP會有替補(bǔ)瘫俊,走基于TCP的HTTP通信鹊杖。
六、支持QUIC的開源項目
Caddy:Go 寫的 Web 服務(wù)器 扛芽,QUIC只是附屬功能骂蓖,但用它的人更多是用來做QUIC實驗 :D, 勉強(qiáng)能用在生產(chǎn)環(huán)境川尖。
quic-go :是完全用 go 寫的 QUIC 協(xié)議棧登下,開發(fā)很活躍,已在 Caddy 中使用叮喳,MIT 許可被芳。
Chromium :Google 官方維護(hù)基本沒有坑,隨時可以跟隨 chrome 更新到最新版本馍悟。不過編譯 Chromium 比較麻煩畔濒。
七、附錄:
1锣咒、header壓縮
????????在HTTP1.x中侵状,頭部元數(shù)據(jù)都是以純文本的形式發(fā)送的,通常會給每個請求增加500~800字節(jié)的負(fù)荷毅整。
????????HTTP2.0使用HPACK算法來減少需要傳輸?shù)膆eader大小趣兄,通訊雙方各自cache一份header fields表,既避免了重復(fù)header的傳輸毛嫉,又減小了需要傳輸?shù)拇笮》谈8咝У膲嚎s算法可以很大的壓縮header,減少發(fā)送包的數(shù)量從而降低延遲承粤。
2暴区、參考文檔 https://tools.ietf.org/html/draft-ietf-quic-http-17
3、FEC算法? https://blog.csdn.net/u010178611/article/details/82656838?