TCP協議缺陷不完全記錄
零。前言
TCP自從1974年被發明出來之後,曆經30多年發展,目前成為最重要的互連網基礎協議。有線網路環境下,TCP表現的如虎添翼,但在移動互連網和物聯網環境下,稍微表現得略有不足。
移動互連網突出特性不穩定:訊號不穩定,網路連接不穩定。雖然目前發展到4G,行動電話通訊頻寬有所增強,但因其流動特性,訊號也不是那麼穩定:坐長途公交車,或搭乘城鐵時,或周邊上網密集時等環境,現實環境很複雜。
以下討論基於Linux伺服器環境,假定環境為移動互連網環境。記錄我目前所知TCP的一些不足,有所偏差,請給與指正。
一。三向交握
在確定傳遞資料之前需要三向交握,顯然有些多餘,業界提出了TCP Fast Open (TFO)擴充機制,兩次握手之後就可以發送正常業務資料了。但這需要用戶端和伺服器端核心層面都支援才行: Linux核心3.6用戶端,3.7支援伺服器端。
進階閱讀:TCP Fast Open: expediting web services
二。慢啟動
一次的HTTP請求,應用程式層發送較大HTML頁面的資料,需要經過若干個往返迴圈時間(Round-Trip Time)之後,擁塞視窗才能夠擴充到最大適合數值,中間過程頗為冗餘。這個參數直接關係著系統輸送量,輸送量大了,系統延遲小了。但設定成多大,需要根據業務進行抉擇。
3.0核心之前初始化擁塞視窗(initcwnd)大小為3。一個已建立串連初始傳輸資料時可傳遞3個MSS,若1個MSS為1400那麼一次性可傳遞4K的資料,若為10,一次性可傳遞13K的資料。
Google經過調研,建議移動互連網WEB環境下建議initcwnd設定成10,linux核心3.0版本之後預設值為10。遇到較低核心,需要手動進行設定。
若是區域網路環境有類似大資料或檔案的傳輸需求,可以考慮適當放寬一些。
若長串連建立之後傳輸的都是小訊息,每次傳輸二進位不到4K,那麼慢啟動改動與否都是無關緊要的事情了。
進階閱讀:
- Tuning initcwnd for optimum performance
- Optimizing Your Linux Stack for Maximum Mobile Web Performance
- An Argument for Increasing TCP's Initial Congestion Window
三。線頭阻塞(Head-of-line blocking, HOL)
TCP協議資料轉送需要按序傳輸,可以理解為FIFO先進先出隊列,當前面資料轉送丟失後,後續資料單元只能等待,除非已經丟失的資料被重傳並確認接收以後,後續資料包才會被交付給用戶端裝置,這就是所謂的線頭(HOL,head-of-line blocking)阻塞。比較浪費伺服器頻寬又降低了系統效能,不高效。
1. 多工不理想
HTTP/2提出的業務層面多工,雖然在一定程度上解決了HTTP/1.*單路傳輸問題,但依然受制於所依賴的TCP本身線頭阻塞的缺陷。構建於TCP上層協議的多工,一旦發生出現線頭阻塞,需要小心對待多路的業務資料發送失敗問題。
2. TCP Keepalive機制失效
理論上TCP的Keepalive保活擴充機制,在出現線頭阻塞的時候,發送不出去被一直阻塞,完全失效。
類似於NFS檔案系統,一般採用雙向的TCP Keepalive保活機制,用以規避某一端因線頭阻塞出現導致Keepalive無效的問題,及時感知一端存活情況。
3. 線頭阻塞逾時提示
資料包發送了,啟動接收確認定時器,逾時後會重發,重發依然無確認,後續資料會一直堆積到待發送隊列中,這裡會有一個阻塞逾時,演算法很複雜。上層應用會接收到來自核心協議棧的彙報"No route to host"的錯誤資訊,預設不大於16分鐘時間。在伺服器端(沒有業務心跳支援的情況下)發送資料前把終端強制斷線,順便結合TCPDUMP截包,等15分鐘左右核心警告"EHOSTUNREACH"錯誤,應用程式層面就可以看到"No route to host"的通知。
四。四次擺手
兩端串連成功建立之後,需要關閉時,需要產生四次互動,這在移動互連網環境下,顯得有些多餘。快速關閉,快速響應,冗餘互動導致網路頻寬被佔用。
五。確認機制通知到上層應用?
這是一個比較美好的願望,上層應用在調用核心層介面發送大段資料,核心完成發送並且收到對方完整確認,然後通知上層應用已經發送成功,那麼在一些環境下,可以節省不少業務層面互動步驟。
六。NAT Gateway逾時
IPV4有限,區域網路環境藉助於NAT路由裝置擴充了接入終端裝置的數量。當建立一個TCP長串連時,NAT裝置需要維護一個內部終端串連外部伺服器所使用的內部IP:PORT與出去的IP:PORT映射對應關係。這個關係需要維護,比較耗費記憶體資源,有逾時定時器清理,否則會導致記憶體撐爆。
不同NAT裝置逾時值不一樣,因此才需要心跳輔助,確保經過NAT裝置的串連一直保持,避免因過長的時間被踢掉。比如針對中國移動網路連接持久時間一般設定為不超過5分鐘。各種網路略有差異,引入智能心跳機制比較合適。
七。終端IP漫遊
手機終端經常在2G/3G/4G和WIFI之間切換,導致IP地址頻繁發生改變。這樣造成的後果就是已有的網路要求-回應被放棄和終止,需要人工幹預或重新發起請求,存在資源浪費現象。
支援Multipath TCP的終端裝置,可以同時利用 2G/3G/4G 和 WiFi 建立Mutlpath串連,通過多點最佳化網路下載,且互為備份。可以很好解決多個網路共存的情況下,一個網路中斷不會導致全域請求處理中斷,在裝置的串連穩定和可靠性方面有所增強。
當然,伺服器之間也可以利用Multipath TCP的多個網路增強網路輸送量。
現狀是:
- 目前只有IOS 7以及後續版本支援
- Linux kernel 3.10實驗分支上可以看到其支援身影,但何時合并到主分支上,暫時未知
進階閱讀:A closer look at the scientific literature on Multipath TCP
八。TCP緩衝膨脹
當路由器接收到的資料包超越其隊列長度時,一般會隨機丟包,以減少膨脹。針對上層應用程式而言,延遲增加,或誤認為資料丟失,或串連丟失等。
遇到這種情況,一般建議快速發包,以避免丟失的資料部分。核心層面今早升級到最新版,不低於3.6即可。
進階閱讀:Bufferbloat
九。TCP不是絕對可靠的
- IP和TCP協議在頭部都會有check sum錯誤校正和機制,16位表示,反碼相加,結果求反,具體可參考 TCP校正和的原理和實現。一般錯誤很輕鬆可檢測出來,但遇到兩個16位元字相加後結果不變的情況就一籌莫展了
乙太網路幀CRC32校正一般情況下都很OK,但可能遇到兩端隔離多個路由器情況下,就有可能出現問題,比如陳碩老師提供的一張圖:
中Client向Server發了一個TCP segment,這個segment先被封裝成一個IP packet,再被封裝成ethernet frame,發送到路由器(圖中訊息a)。Router收到ethernet frame (b),轉寄到另一個網段(c),最後Server收到d,通知應用程式。Ethernet CRC能保證a和b相同,c和d相同;TCP header check sum的強度不足以保證收發payload的內容一樣。另外,如果把Router換成NAT,那麼NAT自己會構造c(替換掉源地址),這時候a和d的payload不能用tcp header checksum校正。
路由器可能偶然出現硬體/記憶體故障導致收發IP報文出現多bit/單bit的反轉或雙位元組交換,這個反轉如果發生在payload區,那麼無法用鏈路層、網路層、傳輸層的check sum查出來,只能通過應用程式層的check sum來檢測。因此建議應用程式層要設法添加校正資料功能。
大檔案下載添加校正保證資料完整性,一般採用MD5,也用於防止安全篡改
參考資料:
- Paper《When the CRC and TCP checksum disagree》
- The Limitations of the Ethernet CRC and TCP/IP checksums for error detection
- Amazon S3遇到的單bit反轉線上事故
十。小結
在這個滿世界都是TCP的環境下,要想對TCP動大手術,這個是不太可能的,因為它已經固化到已有的系統核心和韌體中。比如升級終端(比如Android/IOS等)系統/韌體,Linux伺服器核心,中間裝置/中介裝置(如路由器等),這是一個浩大工程,目前看也不現實。
TCP位於系統核心層,核心空間的升級、修複,最為麻煩。伺服器端升級還好說一些,使用者終端系統的升級那叫一個難。使用者空間/使用者核的應用升級、改造相對比來說可控性強,基於此Google專家們直接在UDP協議上進行構建、並且運行在使用者空間的QUIC協議,綜合了UDP的輕量和TCP的可靠性,是一個比較新穎的方向。
若是對以後底層傳輸協議有所期望的話:
- 在使用者空間(使用者核)出現可以定製的協議,類似於QUIC
- 傳統的TCP/UDP可以運行在使用者空間,直接略過核心
- 完整協議棧以靜態連結庫形式提供給上層應用
- 上層應用可以在編譯、打包的時包含其所依賴協議棧靜態連結庫so檔案
- dpdk/netmap等Packet IO架構 + 使用者空間協議堆棧,資料將從網卡直接送達上層應用
- Linux核心重要性降低,常規的SSH系統維護
雖然TCP存在這樣、那樣的問題,但目前還是無法繞過的網路基礎設施,但稍微明白一些不足的地方,或許會對我們當前使用的現狀有所協助。
本文永久更新連結地址: