IP:超時,重傳,控流,擁塞處理(二)


本文概要:

  • TCP 重傳機制
  • TCP 控流
  • TCP 擁塞處理

TCP 重傳機制

1、ACK 攜帶信息

  • 期待要收到下一個數據包的編號;
  • 接收方的接收窗口的剩餘容量。

注意:由於 TCP 通信是雙向的,所以雙方都需要發送 ACK。兩方的窗口大小,很可能是不一樣的。而且 ACK 只是很簡單的幾個字段,通常與數據合併在一個數據包裡面發送。

TCP/IP:超時,重傳,控流,擁塞處理(二)

上圖一共4次通信。

第一次通信,A 主機發給B 主機的數據包編號是1,長度是100字節;

第二次通信 B 主機的 ACK 編號是 1 + 100 = 101,第三次通信 A 主機的數據包編號也是 101。同理

第二次通信 B 主機發給 A 主機的數據包編號是1,長度是200字節;

第三次通信 A 主機的 ACK 是201,第四次通信 B 主機的數據包編號也是201

2、TCP 協議可以保證數據通信的可靠性,做法如下

每一個數據包都帶有下一個數據包的編號。如果下一個數據包沒有收到,那麼 ACK 的編號就不會發生變化。

接收端給發送端的 Ack確認只會確認最後一個連續的包,比如,發送端發了1,2,3,4,5一共五份數據,接收端收到了1,2,於是回 ack 3,然後收到了4(注意此時3沒收到),此時的 TCP 會怎麼辦?

我們要知道,因為正如前面所說的,SeqNum 和 Ack 是以字節數為單位,所以 ack 的時候,不能跳著確認,只能確認最大的連續收到的包,不然,發送端就以為之前的都收到了。

如果發送方發現收到連續的重複 ACK,或者超時了還沒有收到任何 ACK,就會確認丟包,從而再次發送這個包。通過重傳這種機制,TCP 保證了不會有數據包丟失

3、超時重傳機制

一種是不回 ack,死等3,當發送方發現收不到3的 ack 超時後,會重傳3。一旦接收方收到3後,會 ack 回 4——意味著3和4都收到了。

但是,這種方式會有比較嚴重的問題,那就是因為要死等3,所以會導致4和5即便已經收到了,而發送方也完全不知道發生了什麼事,因為沒有收到 Ack,所以,發送方可能會悲觀地認為也丟了,所以有可能也會導致4和5的重傳。

對此有兩種選擇:

  • 1 )僅重傳 timeout 的包。也就是第3份數據。
  • 2) 重傳 timeout 後所有的數據,也就是第3,4,5這三份數據。
    第一種會節省帶寬,但是慢,第二種會快一點,但是會浪費帶寬,也可能會有無用功。但總體來說都不好。因為都在等 timeout,timeout 可能會很長,針對兩種情況 TCP/IP 協議給了三種算法供選擇:Fast Retransmit,Selective Acknowledgment,Duplicate SACK。後續可針對此類算法進行展開詳解

4、超時重傳中的超時 TIMEOUT 設置 問題

針對上述情況得知合理的設置 timeout 時間長度對重傳很重要

1)設長了,重發就慢,丟了老半天才重發,沒有效率,性能差;

2)設短了,會導致可能並沒有丟就重發。於是重發的就快,會增加網絡擁塞,導致更多的超時,更多的超時導致更多的重發。

這個超時時間在不同的網絡的情況下,不是設置一個固定值。只能動態地設置。 為了動態地設置,TCP 引入了 RTT——Round Trip Time,也就是一個數據包從發出去到回來的時間。

這樣發送端就大約知道需要多少的時間,從而可以方便地設置 Timeout——RTO(Retransmission TimeOut),以讓我們的重傳機制更高效。

二、TCP 控流

TCP 必需要解決的可靠傳輸以及包亂序(reordering)的問題,所以,TCP 必需要知道網絡實際的數據處理帶寬或是數據處理速度,這樣才不會引起網絡擁塞,導致丟包。

TCP 引入了一些技術和設計來做網絡流控,Sliding Window 是其中一個技術。 前面我們說過,TCP 頭裡有一個字段叫 Window,又叫 Advertised-Window,這個字段是接收端告訴發送端自己還有多少緩衝區可以接收數據。於是發送端就可以根據這個接收端的處理能力來發送數據,而不會導致接收端處理不過來。

接收端在給發送端回 ACK 中會彙報自己的可用窗口大小,而發送方會根據這個窗口來控制發送數據的大小,以保證接收方可以處理。

問題1:Window 變成0了,TCP 處理方式如下:

發送端在窗口變成0後,會發 ZWP 的包給接收方,讓接收方來 ack 他的 Window 尺寸,一般這個值會設置成3次,第次大約30-60秒(不同的實現可能會不一樣)。如果3次過後還是0的話,有的 TCP 實現就會發 RST 把鏈接斷了。

注意:只要有等待的地方都可能出現 DDoS 攻擊,Zero Window 也不例外,一些攻擊者會在和 HTTP建好鏈發完 GET 請求後,就把 Window 設置為0,然後服務端就只能等待進行 ZWP,於是攻擊者會併發大量的這樣的請求,把服務器端的資源耗盡

問題2:當對方 Window 越來越小,接收方處理方式如下:

如果接收方處理速度越來越慢,就導致接收方可用窗口越來越小。到最後,如果接收方騰出幾個字節並告訴發送方現在有幾個字節的 window,發送方會義無反顧地發送這幾個字節,本來一次通信可以傳輸很大塊字節,現在只能幾個字節,這就造成資源浪費。

要解決這個問題也不難,就是避免對小的 window size 做出響應,直到有足夠大的 window size 再響應,這個思路可以同時實現在 sender 和 receiver 兩端:

1)如果這個問題是由 Receiver 端引起的,那麼就會使用 David D Clark’s 方案。在 receiver 端,如果收到的數據導致 window size 小於某個值,可以直接 ack(0) 回 sender,這樣就把 window 給關閉了,也阻止了 sender 再發數據過來,等到 receiver 端處理了一些數據後 windows size 大於等於了 MSS,或者,receiver buffer 有一半為空,就可以把 window 打開讓 send 發送數據過來。

2)如果這個問題是由 Sender 端引起的,那麼就會使用著名的 Nagle’s algorithm。這個算法的思路也是延時處理,他有兩個主要的條件:

  • 要等到 Window Size>=MSS 或是 Data Size >=MSS,
  • 收到之前發送數據的 ack 回包,他才會發數據,否則就是在攢數據。

TCP 擁塞處理

TCP 通過 Sliding Window 來做流控(Flow Control),但是 TCP 覺得這還不夠,因為 Sliding Window 需要依賴於連接的發送端和接收端,其並不知道網絡中間發生了什麼。

TCP/IP:超時,重傳,控流,擁塞處理(二)

TCP 的設計者覺得,TCP 協議僅僅做到流控並不夠,因為流控只是網絡模型4層以上的事,TCP 還應該更聰明地知道整個網絡上的事。

具體一點,我們知道 TCP 通過一個 timer 採樣了 RTT 並計算 RTO,但是,如果網絡上的延時突然增加,TCP 對這個事做出的應對只有重傳數據;

但是,重傳會導致網絡的負擔更重,於是會導致更大的延遲以及更多的丟包,這個情況就會進入惡性循環被不斷地放大。

試想一下,如果一個網絡內有成千上萬的 TCP 連接都這麼行事,那麼馬上就會形成“網絡風暴”,TCP 協議就會拖垮整個網絡。

所以,TCP 不能忽略網絡上發生的事情,而一個勁地重發數據,對網絡造成更大的傷害。

對此TCP 的設計理念是:TCP 不是一個自私的協議,當擁塞發生的時候,要做自我犧牲。就像交通阻塞一樣,每個車都應該把路讓出來,而不要再去搶路了

最後,擁塞控制主要是四個算法:

  • 慢啟動;
  • 擁塞避免;
  • 擁塞發生;
  • 快速恢復。


分享到:


相關文章: