quic為什么出現?
quic主要是為了解決TCP協議的局限性而提出的,具體來說是要解決如下問題:
1. 加密連接建立時間長
TCP協議是傳輸層協議,而TLS是會話層協議,在Linux等主流操作系統中TCP在內核實現而TLS一般在用戶態實現,因此基于TCP+TLS的典型加密連接至少需要2RTT才能完成建連。
2. 隊頭阻塞/多流建立時延
TCP協議中沒有明確的流(stream)的概念,或者說TCP語境中流和連接是等價的。在需要并發傳輸多段數據的情況下,TCP需要建立多條連接才能實現多數據段的完全并行傳輸,而多連接的建立又會產生更多的時延,即使使用TCP fastopen也仍然會產生一次TLS握手時延。
并行傳輸的另一個實現方式是多請求復用單個連接。雖然在HTTP2等協議中支持多請求流并行收發,但這只是一個應用層的邏輯概念,在TCP傳輸中仍然只支持單流傳輸。由于TCP向應用層交付數據時是保證有序的,因此在單TCP連接上進行多請求數據并行傳輸時,只要有一個請求的報文丟失就會阻塞所有請求的數據交付。并行傳輸效果不穩定,且在實現中多邏輯流并發使用單連接socket容易產生并發正確性和性能問題。
事實上在廣域網RDMA協議iWARP中,也有報文亂序交付設計與TCP按序交付規范相沖突的問題,iWARP規范中提出了修改TCP協議實現來實現按需亂序交付的能力,以提高數據交付性能。但這種修改讓TCP協議的實現變的更加復雜,缺乏經過驗證的標準實現,因此沒有得到廣泛支持和應用。
3. 網絡切換與連接遷移
隨著移動網絡場景的普及,網絡切換(主要是wifi到4G/5G移動網絡)成為了一個高頻場景。雖然4G/5G移動網已經支持了基站間的地址遷移,網內基站切換不會產生地址切換,但跨網絡的切換仍然會造成設備網絡地址的改變。TCP連接是以網絡地址來標識的,地址切換意味著切換前的TCP連接全部失效,正在傳輸的數據流中斷。這種情況下就需要應用層支持數據的斷點恢復和續傳,增加了應用開發成本和重復度。同時需要重建所有TCP連接,引入了額外的建連時延。
雖然TCP也支持Fast open特性,可以實現快速建連,但上層協議狀態(例如TLS加密狀態)仍然無法透明恢復。此外fastopen也不支持IP地址切換后使用。
quic協議的發展過程
2012年:QUIC(Quick UDP Internet Connections)協議由Google提出,旨在解決傳統TCP協議在現代化網絡環境中暴露的局限性,特別是在HTTP協議使用場景中。早期quic協議由google定義和實現,基于quic的http協議稱作http-over-quic。
2015年:QUIC被提交至IETF標準化,逐步脫離Google的私有實現。
2018年:http-over-quic正式被命名為http/3,意味著下一代http協議完全基于quic傳輸協議
2021年:quic協議規范RFC9000正式發布
2022年:http/3協議規范RFC9114正式發布
可以看到,quic最初是用于解決http協議傳輸效率問題而提出的,之后成為了http下一代規范的指定傳輸協議。但quic本身也是一個標準的可靠加密傳輸協議,可以應用于其他網絡傳輸場景。
quic協議實現原理
快速建連
1RTT:quic協議將可靠傳輸通道建連與TLS加密通道建連結合,在一個報文中攜帶兩種握手信息(quic initial + tls client/server hello),從而只需要一次交互就能完成可靠加密傳輸通道建立,耗時1RTT。
0RTT:在通過1RTT建立成功后,可以基于TLS的會話復用機制,實現0RTT的建連。在這種模式下,quic initial協商報文與數據載荷報文同時發出,其中載荷報文復用已建立的TLS會話密鑰加密。如果對端接受quic initial報文中的會話復用協商,就能處理直接處理載荷報文。需要注意的是0RTT機制允許在未協商的情況下處理和緩存數據,容易受到重放攻擊和flooding攻擊,需要仔細考慮使用場景和策略。
單連接多流復用
quic的最小邏輯傳輸單元是流,而不是連接。建立一條quic連接后,可以在相同五元組連接上建立多條邏輯傳輸流。流不需要專門的協商創建流程,使用新的stream id傳輸stream幀就可以在兩端建立新的流。
quic連接的每條流有獨立的緩存、排序和重傳狀態,每條流內部保證數據的有序可靠交付。
每條流有獨立的接受窗口(window)用于流量控制,這是因為每條流對應的處理邏輯不同,處理能力也有差異。
但擁塞控制算法和擁塞窗口(cwnd)是連接級別的,因為同一條連接的網絡轉發路徑基本相同,擁塞對連接中的所有流都存在。
連接遷移
quic連接建立時,兩端會分別為這個連接分配connection id并同步給對方。在quic協議內連接的標識是雙方分配的connection id,而非五元組信息。這就為跨五元組的連接遷移提供了支持基礎。
在客戶端的ip發生變化后,通過如下流程進行連接遷移:
1. 使用新IP+原connection id繼續發送數據幀
2. server收到數據幀發現client的ip發生改變,向client發送PATH CHALLENGE幀
3. client向server回應PATH RESPONSE幀
4. server驗證PATH RESPONSE中的路徑內容,遷移完成
TCP真的不行嗎?
從上面介紹的quic協議核心特性實現來看,在TCP的基礎上進行擴展其實也是可以實現類似效果的。
- 快速建連:可以在TCP的三次握手報文中增加對TLS1.3協商的擴展選項,來實現1RTT協商建連
- 多流復用:可以通過TCP fastopen+TLS 會話復用,實現0RTT的后續流快速建立和并行傳輸
- 連接遷移:可以通過擴展改造TCP fastopen機制,實現連接連接標識在IP變化后仍能被識別,并且將新建連接關聯到舊連接數據上。
但這樣的改造在TCP上面臨的阻力會非常大。TCP協議是一個古老而復雜的協議,并且作為網絡的主要傳輸協議被實現在各種操作系統內核中。不管是重新定義和擴展TCP協議,還是修改擴展內核協議棧,都基本不可能被標準組織和內核社區接受成為標準實現,也就無法推廣應用。
此外,雖然TCP可以通過快速建立多條連接來實現類似多流復用的效果,但畢竟還是需要建立多條連接的,對端口資源的占用和建連協商驗證的資源開銷是無法避免的。因此無法完全達到和quic相同的效果。這是TCP面向連接可靠交付的核心特質決定的,無法修改。MPTCP中的連接有子流的概念,其實每個子流也是一個獨立的TCP連接。
因此,在TLS全流量加密+HTTP多流傳輸的剛需場景下,選擇在用戶態重新定義可靠加密會話傳輸協議,基于限制最小的UDP協議成為了一種自然的選擇。
但如果是在用戶態協議棧中,基于TCP協議擴展來實現quic類似的特性就重新成為了可能的選項。用戶態協議棧完全獨立于內核,可以自定義實現方式和擴展特性。與UDP協議相比,TCP協議可以通過TCP選項頭協商來實現與標準TCP協議的兼容和自動回落,同時避免部分運營商和轉發設備對UDP報文的針對性丟包和低優處理,因此也有其存在價值,值得考慮。