TCP協議可靠性是如何保證之 流量控制和擁塞控制

原創文章首發於公衆號:「碼農富哥」,致力於分享後端技術 (高併發架構, 中間件, Linux, TCP/IP, HTTP, MySQL, Redis), Python 等 原創乾貨 和 面試指南

TCP/IP協議是非常重要的一個知識點,也一直是面試的高頻題,當面試官問你,能說說TCP協議是怎麼保證可靠傳輸的嗎,你能回答上嗎?

TCP 是一種提供可靠性交付的協議。
也就是說,通過 TCP 連接傳輸的數據,無差錯、不丟失、不重複、並且按序到達。
TCP 是通過下面幾個特性保證數據傳輸的可靠性:

  • 序列號和確認應答信號
  • 超時重發控制
  • 連接管理
  • 滑動窗口控制
  • 流量控制
  • 擁塞控制

上一篇 TCP協議可靠性是如何保證之滑動窗口,超時重發,序列號確認應答信號 我們討論了TCP協議可靠性的幾個機制:序列號和確認應答信號超時重發控制連接管理滑動窗口控制

這一篇我們繼續要討論TCP協議可靠性的另外兩個機制: 流量控制擁塞控制

關於TCP協議的文章還有幾篇,大家也可以看看:
一文徹底搞懂 TCP三次握手、四次揮手過程及原理
面試官:說說UDP和TCP的區別及應用場景

擁塞控制與流量控制的區別

  • 流量控制 是作用於接收者的,它是控制發送者的發送速度從而使接收者來得及接收,防止丟失數據包的。

  • 擁塞控制 擁塞控制是作用於網絡的,它是防止過多的數據注入到網絡中,避免出現網絡負載過大的情況

流量控制

所謂流量控制,就是讓發送端不要發送的過快,讓接收端能來得及接收

假設沒有流量控制,發送端根據自己的實際情況發送數據,如果發送的速度太快,導致接收端的接收緩衝區很快填滿了,此時發送端如果繼續發送數據,接收端處理不過來,這時接收端就會把本來應該接收的數據丟棄,這會觸發發送端的重發機制,從而導致網絡流量的無端浪費。

所以TCP需要提供一種機制:讓發送端根據接收端實際的接收能力控制發送的數據量。這就是所謂的流量控制。

TCP 利用滑動窗口實現流量控制的機制, 而滑動窗口大小是通過TCP首部的窗口大小字段來通知對方。

我們重溫一下TCP的頭部:
TCP首部

在TCP協議的頭部信息當中,有一個16位字段的窗口大小,窗口大小的內容實際上是接收端接收數據緩衝區的剩餘大小。這個數字越大,證明接收端接收緩衝區的剩餘空間越大,網絡的吞吐量越大。
不過,當接收端這個接收緩衝區面臨數據溢出時,窗口大小的值就會隨之設置成一個更小值,告訴發送端要控制一下發送的數據量了。
發送端接收到接收端的窗口變化指示後,就會對數據發送量進行調整,從而形成一個完整的流量控制。

流量控制的具體操作就是:接收端會在確認應答發送ACK報文時,將自己的即時窗口大小rwnd(receiver window)填入,並跟隨ACK報文一起發送過去。而發送方根據ACK報文裏的窗口大小的值進而改變自己的發送速度。

看看下面的圖,展示了TCP流量控制的大概過程:

TCP流量控制圖

如上圖所示,主機B接收到了一個1-1000序列號的數據包以後,返回一個ACK給發送端,並且告訴發送端它的窗口大小爲3000,意味着發送端還能發送3000個字節的數據。
主機A收到指示後,繼續發送數據,直到主機B收到3001-4000的數據段後其接收緩衝區滿了,主機B的返回窗口大小爲0,讓主機A要暫停發數據了。

就是這樣一個流程,可以防止發送端一次發送過大的數據導致接收端無法處理的情況。

那麼另外一個問題來了:發送端停止發送數據後,什麼時候可以繼續發送數據呢?
我們繼續看上圖,答案就是等接收端處理完了緩衝區的數據後發送一個窗口更新的數據包通知,發送端纔可以繼續根據窗口大小發送數據。
但是如果發送端在重發超時的時間內都沒有收到窗口更新的通知或者窗口更新的包丟失了,就沒法正常通信了,那怎麼辦呢?

TCP爲每一個連接設有一個持續計時器(persistence timer)。 只要TCP連接的一方收到對方的零窗口通知,就啓動持續計時器。若持續計時器設置的時間到期,就發送一個零窗口控測數據段(這個數據段只包含一個字節),那麼收到這個報文段的一方就重新設置持續計時器。

所以發送端會定時向接收端發送一個 窗口探測 的數據段,這目的是爲了獲取最新的窗口大小信息。

就這樣,完成了TCP流量控制的整個過程。

擁塞控制

什麼是擁塞

我們都知道計算機網絡中的資源是有限的。某段時間內網絡中對資源的需求超過了網絡中的可用部分,而導致網絡性能下降的情況就是擁塞。
通俗點說就是發送的數據包太多網絡中的設備處理不過來,而導致網絡性能下降的情況。

TCP 爲什麼要進行擁塞控制

網絡中的路由器會有一個數據包處理隊列,當路由器接收到的數據包太多而一下子處理不過來時,就會導致數據包處理隊列過長。此時,路由器就會無條件的丟棄新接收到的數據封包。
這就會導致上層的 TCP 協議以爲數據包在網絡中丟失,進而重傳這些數據包,而路由器又會丟棄這些重傳的數據包,如此以往,就會導致網絡性能急劇下降,引起網絡癱瘓。因此,TCP 需要控制數據包發送的數量來避免網絡性能的下降。

擁塞控制原理

有了TCP的滑動窗口控制,收發主機之間即使不再以一個“段”爲單位,而是以一個“窗口”爲單位發送確認應答信號,所以發送主機夠連續發送大量數據包。然而,如果在通信剛開始的時候就發送大量的數據包,也有可能會導致網絡的癱瘓。

在擁塞控制中,發送方維持一個叫做擁塞窗口cwnd(congestion window)的狀態變量。擁塞窗口的大小取決於網絡的擁塞程度,並且動態地在變化
發送窗口取擁塞窗口和接收端窗口的最小值,避免發送接收端窗口還大的數據。

擁塞控制使用了兩個重要的算法: 慢啓動算法擁塞避免算法

(一)慢啓動算法
慢啓動算法的思路是,不要一開始就發送大量的數據,先試探一下網絡的擁塞程度,也就是說由小到大逐漸增加擁塞窗口的大小。慢算法中,每個傳輸輪次後將 cwnd 加倍
舉個例子:一開始發送方設置cwnd=1(爲方便理解,這裏用報文段的個數作爲窗口大小的單位),然後每經過一個傳輸輪次,cwnd都發加倍,比如1, 2, 4, 8…指數增長
所以,這裏的慢啓動,不是指擁塞窗口增長慢,而是相對於一開始就上來傳輸大窗口的數據要顯得慢。

慢開始算法

當然,cwnd 的大小肯定不可能一直以這種指數的方式增長下去,要不然很快就會增長到引起網絡崩潰的程度了。所以,經過一定時間或條件,我們就要換成擁塞避免算法來發送數據。

(二)擁塞避免算法
擁塞避免算法也是逐漸的增大 cwnd 的大小,只是採用的是線性增長 而不是像慢啓動算法那樣的指數增長。
具體來說就是每個傳輸輪次後將 cwnd 的大小加一(加法增大),如果發現出現網絡擁塞的話就按照上面的方法重新設置ssthresh的大小(乘法減小,原來的二分之一)並從cwnd=1開始重新執行慢開始算法。

擁塞避免算法

慢啓動算法和擁塞避免算法結合:

問題:在擁塞控制中, 慢啓動算法 和 擁塞避免算法 是怎麼配合使用的呢?

像上面所說,慢啓動算法下的cwnd大小是指數增長,所以不能任 cwnd 任意增長,所以我們引入一個慢啓動門限(ssthresh)的閾值來控制 cwnd 的增長。

ssthresh的作用是:

  • 當cwnd < ssthresh時,使用慢開始算法。
  • 當cwnd > ssthresh時,改用擁塞避免算法。
  • 當cwnd = ssthresh時,慢開始與擁塞避免算法隨機

還有一個問題就是這個 ssthresh 是怎麼設置的呢?
TCP/IP 中規定無論是在慢開始階段還是在擁塞避免階段,只要發現網絡中出現擁塞(沒有按時收到確認),就要把ssthresh設置爲此時發送窗口的一半大小(不能小於2)。

擁塞控制過程

如上圖所示,擁塞控制的大致流程如下:

  • 一開始把ssthresh初始值設置成16,開始慢啓動增加擁塞窗口cwnd,直到cwnd=16 停止慢啓動,開始擁塞避免算法。
  • 使用擁塞避免算法線性增加cwnd,直到cwnd=24,這時候網絡出現擁塞(ACK確認信號沒有及時到達),把ssthresh設置成原來的一半,也就是ssthresh=12,同時把cwnd設爲1。
  • 重新開始慢啓動,直到cwnd到達ssthresh=12,然後執行擁塞避免算法進行加法增大,直到遇到網絡擁塞,把ssthresh調成原來的一一半。
  • 如此反覆動態計算cwnd,以達到擁塞控制的目的。

快重傳 VS 超時重傳

TCP 的可靠傳輸的原理就是超時重傳機制,而重發機制有兩種:超時重傳機 和 快重傳

  • TCP超時重傳機制,結合上面說的慢啓動擁塞避免使用就是發送完數據後開始倒計時,如果在重發超時內沒有收到對接收方發來的ACK的話就去執行上述的乘法減小過程(設置sstresh爲原來一半) 並重新開始慢開始算法,重新發送數據。

  • TCP快重傳, 則是允許發送方再連續收到3 個重複的確認後就可以開始執行乘法減小過程而不必再等待重發超時時間。這就需要接收方每收到一個失序的報文段就立即發出重複確認以讓發送發及早知道有報文段丟失,而不是等待自己發送數據的時候進行捎帶確認。

TCP快重傳的示意圖如下:
TCP快重傳機制

如圖,由於發送端不必等待每個數據段都確認才能繼續發送,而是以一個窗口爲單位發送數據,所以就算主機A發送的1001-2000序列號數據段丟失,主機A依然會繼續發剩下的窗口大小數據,而此時主機B發現1001-2000數據丟失,它會每次收到其他序列號的數據包,都返回一個序列號2000的ACK,以此明確通知主機A,當主機A收到三次2000的ACK直到丟失了1001-2000數據包,就需要重傳1001-2000的數據包了。
以此達到哪怕沒到重發超時時間,都能快速重傳的目的

快恢復

快恢復算法是與快重傳算法配合使用的一個算法。

快恢復主要是指,當快重傳的時候,發送方快速收到了3個重複的確認,因此會認爲網絡不是擁塞狀態,所以在乘法減小過程(設置sstresh爲原來一半),會啓動 “擁塞避免”,而不是TCP超時重發機制的重新啓動的慢啓動

總結

這篇文章總結了TCP協議在傳輸可靠性的兩個重要機制:

有收穫?

如果大家有所收穫的話

  • 可以轉發+點贊,讓更多人可以看到這篇文章,也是給我一個大大的鼓勵喲!
  • 關注我的原創公衆號:碼農富哥,專注分享後端技術原創乾貨文章

關注後回覆 1024 獲取 精選的後端技術和架構電子書

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章