TCP可靠性傳輸
相信大家都熟知TCP協議作為一種可靠傳輸協議,但它是如何確保傳輸的可靠性呢?
要實現可靠性傳輸,需要考慮許多因素,比如資料的損壞、丟失、重複以及分片順序混亂等問題。如果不能解決這些問題,就無法實現可靠傳輸。
因此,TCP採用了序列號、確認應答、重發控制、連線管理和視窗控制等機制來實現可靠性傳輸。
在本文中,我們將重點介紹TCP的滑動視窗、流量控制和擁塞控制。重傳機制將在下一章節單獨講解。
流量控制
流量控制實際上是生產者和消費者之間微妙關係的一個具體體現。你可能在工作中或者面試中經常遇到這種考察場景。如果生產者的生產能力大大超過消費者的消費能力,就會導致佇列無限增長。更嚴重的情況是,你可能知道當RabbitMQ訊息堆積過多時,會導致整個MQ伺服器效能下降。TCP也是類似的道理,如果不加以控制,過多的訊息都被放到網路中,消費者已經超過了其能力範圍,而生產者仍在重複傳送,這會大大影響網路傳輸效能。
為了解決這種現象,TCP提供了一種機制,讓傳送方根據接收方的實際接收能力來控制傳送的資料量,這就是所謂的流量控制。接收方維護一個接收視窗,而傳送方維護一個傳送視窗。需要注意的是,這些視窗只針對單個TCP連線,而不是所有連線共享一個視窗。
TCP透過使用一個接收視窗的變數來提供流量控制。接收視窗給傳送方一個指示,告訴它還有多少可用的快取空間。傳送端根據接收端的實際接受能力來控制傳送的資料量。
接收端主機會通知傳送端主機自己可以接收資料的大小,傳送端會傳送不超過這個限度的資料。這個大小限度就是視窗大小,你還記得TCP首部嗎?有一個接收視窗欄位,它用於指示接收方能夠或願意接收的位元組數量。
傳送端主機會定期傳送一個視窗探測包,用於探測接收端主機是否還能夠接受資料。當接收端的緩衝區面臨資料溢位的風險時,視窗大小的值也會相應地被設定為一個更小的值,通知傳送端控制資料傳送量。
以下是一個流量控制示意圖:
為了確保接收端主機能夠及時處理資料,傳送端主機會根據接收端主機的視窗大小進行流量控制。這樣可以防止傳送端主機一次傳送過大的資料導致接收端主機無法處理。
如上圖所示,假設主機B的緩衝區已經滿了,在接收到報文段2000-2999之後,它不得不暫停接收資料。為了解決這個問題,主機A會傳送視窗探測包,這個包非常小,只有一個位元組。主機B會根據接收緩衝區的情況更新接收視窗大小,併傳送視窗更新通知給主機A。然後主機A可以繼續傳送報文段。
在上述傳送過程中,視窗更新通知有可能會丟失。一旦丟失,傳送端就不會繼續傳送資料。為了避免這種情況發生,視窗探測包會被隨機傳送,以確保通知的可靠傳輸。
擁塞控制
在介紹擁塞控制之前,我們需要了解除了接收視窗和傳送視窗之外,還有一個擁塞視窗,它主要用於解決傳送方以什麼速度開始傳送資料給接收視窗的問題。因此,擁塞視窗也是由TCP傳送方進行維護的。我們需要一個演算法來決定傳送多少資料是合適的,因為傳送資料過少或過多都不理想,所以就有了擁塞視窗的概念。
在之前的流量控制中,我們避免的是傳送方的資料填滿接收方的快取,但是我們並不知道網路中發生了什麼情況。通常情況下,計算機網路處於一個共享的環境中。因此,可能會因為其他主機之間的通訊而導致網路擁堵。
當網路出現擁堵時,如果繼續傳送大量資料包,可能會導致資料包的延遲和丟失等問題。此時,TCP會重傳資料,但是重傳會增加網路的負擔,導致更大的延遲和更多的丟包。這種情況會進入惡性迴圈,並不斷放大。
因此,TCP不能忽略網路上發生的情況。當網路發生擁塞時,TCP會自我犧牲,降低傳送的資料量。
因此,擁塞控制就應運而生,其目的是避免傳送方的資料填滿整個網路。為了調節傳送方應該傳送的資料量,所以TCP定義了一個叫做擁塞視窗的概念。擁塞控制的演算法會根據網路的擁塞程度來調整擁塞視窗的大小,從而控制傳送方的資料量。
什麼是擁塞視窗?和傳送視窗有什麼關係呢?
擁塞視窗(Congestion Window)是傳送方維護的一個狀態變數,它決定了傳送方可以傳送的資料量。擁塞視窗會根據網路的擁塞程度動態變化。
傳送視窗(Sending Window)是傳送方和接收方之間約定的一個視窗大小,表示接收方可以接收的資料量。擁塞視窗和傳送視窗有關係,傳送視窗的值通常等於擁塞視窗和接收視窗中的最小值,即swnd = min(cwnd, rwnd)。
擁塞視窗cwnd的變化規則如下:
如果網路中沒有出現擁塞,即沒有發生超時重傳,擁塞視窗會增大;
如果網路中出現了擁塞,擁塞視窗會減小。
傳送方透過觀察是否在規定時間內收到ACK確認報文來判斷網路是否出現了擁塞。如果傳送方在規定時間內沒有收到ACK確認報文,就認為網路出現了擁塞。
除了擁塞視窗,下⾯我們就該聊⼀下 TCP 的擁塞控制演算法(TCP congestion control algorithm) 了。TCP 擁塞控制演算法主要包含三個部分:
- 慢啟動(Slow Start):初始時,擁塞視窗cwnd的值比較小,傳送方以指數增加的方式增大擁塞視窗,以快速適應網路的容量。
- 擁塞避免(Congestion Avoidance):擁塞視窗超過一定閾值後,傳送方以線性增加的方式增大擁塞視窗,以減緩擁塞視窗的增長速度,避免過載網路。
- 快速恢復(Fast Recovery):如果發生擁塞,傳送方將擁塞視窗減半,並進入快速恢復狀態,透過接收到的重複ACK來確定網路恢復的位置,然後繼續增加擁塞視窗。
慢啟動
當一條TCP連線建立時,擁塞視窗cwnd的初始值會設為一個MSS(最大報文段長度)的較小值。這樣,初始的傳送速率大約是MSS/RTT(往返時間)位元組/秒。實際可用頻寬通常比MSS/RTT大得多,因此TCP希望找到最佳的傳送速率,可以透過慢啟動的方式來實現。
在慢啟動過程中,擁塞視窗cwnd的值會初始化為1個MSS,並且每次傳輸的報文段確認後,cwnd的值會增加一個MSS,即cwnd的值會變為2個MSS。之後,每成功傳輸一個報文段,cwnd的值就會翻倍,依此類推。具體的增長過程如下圖所示。
然而,傳送速率不可能一直增長,增長總有結束的時候。那麼,何時結束髮送速率的增長呢?慢啟動通常會使用以下幾種方式來結束髮送速率的增長。
第一種方式是在慢啟動的傳送過程中出現丟包的情況。當發生丟包時,TCP會將傳送方的擁塞視窗cwnd設定為1,並重新開始慢啟動的過程。此時,引入了一個慢啟動閾值ssthresh的概念,它的初始值就是產生丟包的cwnd的值的一半。也就是說,當檢測到擁塞時,ssthresh的值就是視窗值的一半。
第二種方式是直接和慢啟動閾值ssthresh的值相關聯。因為當檢測到擁塞時,ssthresh的值就是視窗值的一半,那麼當cwnd大於ssthresh時,每次翻番都可能會出現丟包。所以,最好的方式就是將cwnd的值設為ssthresh,這樣TCP就會轉為擁塞控制模式,結束慢啟動。
慢啟動結束的最後⼀種⽅式就是如果檢測到 3 個冗餘 ACK,TCP 就會執⾏⼀種快速重傳並進⼊恢復狀態。(如果不清楚為什麼會有三次同樣的ACK報文,在重傳機制中會單獨講解)
擁塞避免
當TCP進入擁塞控制狀態後,cwnd的值會被設為擁塞閾值ssthresh的一半。這意味著無法每次收到報文段後都將cwnd的值翻倍。相反,採用了一種相對保守的方式,即每次傳輸完成後,只將cwnd的值增加一個MSS(最大報文段長度)。例如,即使收到了10個報文段的確認,但cwnd的值只會增加一個MSS。這是一種線性增長模式,它也有一個增長上限。當出現丟包時,cwnd的值將變為一個MSS,ssthresh的值將設為cwnd的一半;或者當收到3個冗餘的ACK響應時,也會停止MSS的增長。如果在將cwnd的值減半後仍然收到3個冗餘ACK,那麼ssthresh的值將記錄為cwnd值的一半,並進入快速恢復狀態。
快速恢復
在快速恢復(Fast Recovery)狀態中,對於每個收到的冗餘ACK(即不是按順序到達的ACK),擁塞視窗cwnd的值會增加一個MSS。這是為了利用網路中已經傳輸成功的報文段,儘可能地提高傳輸效率。
當丟失的報文段的一個ACK到達時,TCP會降低cwnd的值,然後進入擁塞避免狀態。這是為了控制擁塞視窗的大小,避免繼續加重網路擁塞。
如果在擁塞控制狀態後出現超時,說明網路狀況變得更加嚴重,TCP會從擁塞避免狀態遷移到慢啟動狀態。此時,擁塞視窗cwnd的值被設定為1個MSS(最大報文段長度),而慢啟動閾值ssthresh的值被設定為cwnd的一半。這樣做的目的是為了在網路恢復後,重新逐漸增加擁塞視窗的大小,以平衡傳輸速率和網路擁塞程度。
總結
TCP協議作為一種可靠傳輸協議,透過序列號、確認應答、重發控制、連線管理和視窗控制等機制來實現可靠性傳輸。其中,流量控制機制透過傳送方根據接收方的實際接收能力來控制傳送的資料量,避免了網路擁堵和效能下降的問題。而擁塞控制機制則透過調節傳送方的資料傳送量,避免了網路擁塞的發生。擁塞視窗和傳送視窗的概念相互關聯,透過動態調整擁塞視窗的大小來控制傳送方的資料量。慢啟動、擁塞避免和快速恢復是TCP擁塞控制演算法的三個主要部分,透過不同的策略來調節擁塞視窗的大小,以適應網路的容量和擁塞程度。
在下一章節中,我們將詳細講解TCP的重傳機制。重傳機制是TCP實現可靠傳輸的重要組成部分,透過對丟失、損壞或延遲的資料進行重傳,確保資料的可靠性傳輸。重傳機制的實現原理和策略將在下一章節中進行詳細的介紹和解析。敬請期待!