首頁 > 軟體

TCP協定缺陷不完全記錄

2020-06-16 17:59:07

零。前言

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的資料。

谷歌經過調研,建議行動網際網路WEB環境下建議initcwnd設定成10,linux核心3.0版本之後預設值為10。遇到較低核心,需要手動進行設定。

若是區域網環境有類似巨量資料或檔案的傳輸需求,可以考慮適當放寬一些。

若長連線建立之後傳輸的都是小訊息,每次傳輸二進位制不到4K,那麼慢啟動改動與否都是無關緊要的事情了。

進階閱讀:

三。線頭阻塞(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閘道器超時

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的多個網路增強網路吞吐量。

現狀是:

  1. 目前只有IOS 7以及後續版本支援
  2. Linux kernel 3.10實驗分支上可以看到其支援身影,但何時合併到主分支上,暫時未知

進階閱讀:A closer look at the scientific literature on Multipath TCP

八。TCP快取膨脹

當路由器接收到的封包超越其佇列長度時,一般會隨機丟包,以減少膨脹。針對上層應用程式而言,延遲增加,或誤認為資料丟失,或連線丟失等。

遇到這種情況,一般建議快速發包,以避免丟失的資料部分。核心層面今早升級到最新版,不低於3.6即可。

進階閱讀:Bufferbloat

九。TCP不是絕對可靠的

  1. IP和TCP協定在頭部都會有check sum錯誤校驗和機制,16位元表示,反碼相加,結果求反,具體可參考 TCP校驗和的原理和實現。一般錯誤很輕鬆可檢測出來,但遇到兩個16位元數位相加後結果不變的情況就一籌莫展了
  2. 乙太網幀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校驗。

  3. 路由器可能偶然出現硬體/記憶體故障導致收發IP報文出現多bit/單bit的反轉或雙位元組交換,這個反轉如果發生在payload區,那麼無法用鏈路層、網路層、傳輸層的check sum查出來,只能通過應用層的check sum來檢測。因此建議應用層要設法新增校驗資料功能。

  4. 大檔案下載新增校驗保證資料完整性,一般採用MD5,也用於防止安全篡改

參考資料:

十。小結

在這個滿世界都是TCP的環境下,要想對TCP動大手術,這個是不太可能的,因為它已經固化到已有的系統核心和韌體中。比如升級終端(比如Android/IOS等)系統/韌體,Linux伺服器核心,中間裝置/中介裝置(如路由器等),這是一個浩大工程,目前看也不現實。

TCP位於系統核心層,核心空間的升級、修復,最為麻煩。伺服器端升級還好說一些,使用者終端系統的升級那叫一個難。使用者空間/使用者核的應用升級、改造相對比來說可控性強,基於此Google專家們直接在UDP協定上進行構建、並且執行在使用者空間的QUIC協定,綜合了UDP的輕量和TCP的可靠性,是一個比較新穎的方向。

若是對以後底層傳輸協定有所期望的話:

  • 在使用者空間(使用者核)出現可以客製化的協定,類似於QUIC
  • 傳統的TCP/UDP可以執行在使用者空間,直接略過核心
  • 完整協定棧以靜態連結庫形式提供給上層應用
  • 上層應用可以在編譯、打包的時包含其所依賴協定棧靜態連結庫so檔案
  • dpdk/netmap等Packet IO框架 + 使用者空間協定堆疊,資料將從網絡卡直接送達上層應用
  • Linux核心重要性降低,常規的SSH系統維護

雖然TCP存在這樣、那樣的問題,但目前還是無法繞過的網路基礎設施,但稍微明白一些不足的地方,或許會對我們當前使用的現狀有所幫助。

本文永久更新連結地址http://www.linuxidc.com/Linux/2015-06/118561.htm


IT145.com E-mail:sddin#qq.com