1. 程式人生 > 其它 >TCP的慢啟動、擁塞避免、重傳、快恢復亂七八糟總是記不清?11個連環問讓你一次性打通任督二脈

TCP的慢啟動、擁塞避免、重傳、快恢復亂七八糟總是記不清?11個連環問讓你一次性打通任督二脈

摘要:如果你的開發過程涉及資料傳輸,一直在重傳、超時之類的方案裡有困惑的話,不妨重新學一學可靠性最精緻的TCP協議。

本文分享自華為雲社群《TCP的慢啟動、擁塞避免、重傳、快恢復亂七八糟總是記不清?11個連環問讓你一次性打通任督二脈》,作者: breakDraw。

TCP的擁塞避免等機制對於初學者來說還是比較複雜的,工作中如果開發時偏應用層,那麼大部分時候就會摸不到這個機制,感受也就沒那麼深了。但如果你的開發過程涉及資料傳輸,一直在重傳、超時之類的方案裡有困惑的話,不妨重新學一學可靠性最精緻的TCP協議。

所以這裡我拋去死記硬背的那堆概念,用10個連續的問題來學習這個機制,注意看的時候先自己思考一下如果是自己,會怎麼設計,再去看實際的TCP設計,來理解它的精妙之處。

Q: 建立連線後,每次傳送的報文數量是固定的嗎?即每次都發1條或者10條?

A:不是。
建立連線後,會先只發1條, 然後發2條,接著再發4條,逐步增加。
這個過程叫“慢啟動”。
這個1、2、4遞增的數量被稱之為擁塞視窗cwnd

可以理解為TCP希望剛開始,可以大膽點,不斷加數量。但為了保險期間還是從1條還是倍增。

Q: 慢啟動過程中,那麼傳送數量(擁塞視窗)什麼時候不再倍增?是無限倍增嗎?

A:不會無限倍增。

到達慢啟動門限ssthreshold時,會變成每次都增加1條。
這個過程叫擁塞避免過程, 也有叫他擁塞避免演算法的

可以理解為tcp感覺到有風險了,於是開始慢慢地、小心翼翼地1條1條地添加發送條數。

Q:那麼,當進入擁塞避免,每次+1時,什麼時候才會不再繼續加?

A:隨著每次傳送的數量越發越多, 最終會超出頻寬限制,於是就會有某條報文發生超時。

有可能是發的中途丟了, 亦或者是返回的資料全阻塞住了,一條都回不來。

當傳送端檢測到發生超時時,就會讓慢啟動門限ssthreshold = 當前擁塞視窗cwnd/2

接著cwnd 重新置為1,從新開始 慢啟動演算法。

這樣的好處在於可以檢測到每次傳送的上限,動態調整發送視窗。

上面的過程叫做超時重傳

注意發生超時重傳時, cwnd會重置成1。

Q: 上面提到了超時, 那麼TCP客戶端是怎麼判斷報文傳送超時的呢?

A:每次傳送資料包的時候, 都會有一個相應的計時器,一旦超過 RTO(超時時間) 而沒有收到 ACK, TCP就會重發該資料包。

沒收到 ACK 的資料包都會存在重傳緩衝區裡,等到 ACK 後,就從緩衝區裡刪除。

Q:上面提到的超時時間RTO是怎麼來的?萬一設得太大可能導致很遲才能反應過來, 設得太小則可能導致每條都超時

A:通過“每次報文的往返時間樣本”和“之前樣本的偏差值”動態計算出來的。

  • RTT : 報文往返時間(指從傳送到收到ack的時間)。每個報文發出後都有個定時器,收到後都會計算出一個RTT樣本
  • RTTs: 加權平均往返時間,類似於一個估算的往返時間,實時在變。
    RTTs = (1-a) * RTTs + a * RTT最新樣本
    即每次得到RTT樣本後, 都會使用a這個佔比去更新RTTs。
  • RTTd: RTT偏差加權平均值(就是用來計算超時時間應該比RTT多多少)
    RTTd = (1 - b) * RTTd + b*RTTs - RTT最新樣本
    即每次會用新的RTTs以b的佔比去更新一下RTTd,並減去RTT樣本
  • RTO : 超時重傳時間
    等於平均往返時間 加上 4倍偏差值
    RTO = RTTs + 4*RTTd

Q: 如果發生重傳,卻還是沒有收到ack,那麼最新的RTT樣本應該怎麼算?即你都收不到最新的ack了, RTT難道取超時時間嗎?

A:會使用karn演算法: 發生重傳時,不更新這次的RTT樣。選用後面收到的ack

修正karn: 為了避免發生重傳後,實際RTT都變慢了,導致一下子所有請求都超時, 會在發生重傳時,把RTO假大1倍。

Q: 上面提到的ACK超時判斷會不會太久了? 假如只是發的時候丟了中間部分報文而已, 但大部分報文ACK還能正常返回,也要一直等超時嗎?

A:如果能正常接收其他報文的ACK, 只是中間的部分報文丟了, 則有另一個辦法。

接收端有一個冗餘確認機制:

  1. 傳送端A 傳送 1、2、3、4、5四條
  2. 但是B接收端只收到1、2、4、5,而3因為網路擁塞丟了。
  3. 於是B會發送ack=3而不是ack=5 給A。這就是冗餘確認機制,只發送缺失那部分的ack,後面的4和5都不管。
  4. A收到ack=3後, 繼續傳送3、4、5、6、7, 結果3還是丟了。
  5. 於是B又傳送ack=3。

當A發現連續3次收到了ack=3時,就會覺察到不對勁,我都發3次了你還是說沒收到,可你又能正常返回其他ACK給我,是不是我發的太多了?

上面這個判斷3次的重傳演算法叫“快重傳”。

於是A會馬上進入 “快速恢復”。

和之前類似,慢啟動門限ssthreshold = 當前擁塞視窗cwnd/2。但是!! 新的擁塞視窗cwnd會設定成ssthreshold/2, 而不是1。而且不會走慢啟動倍增的那種,而是走擁塞避免, 逐步+1的那種。

Q: 前面“超時重傳”的時候,是變成從1開始慢啟動, 為什麼這個“快重傳”卻是從ssthreshold/2開始,並且走擁塞避免? 為什麼會有這個區別?

A:因為前面發生超時重傳時, 是比較嚴重的情況, 超時時間內一個ACK都沒收到。就好像來回資料都憑空消失了。

而快速重傳發生時, 還是能收到部分ack的, 只是丟失了部分資料, 說明擁塞沒那麼嚴重,於是可以大膽一點將cwnd削減到1/4, 而不是直接從1開始。

其他

到了這裡,基本就能理清楚超時重傳和快重傳的區別了,重點是理解這2個區別是怎麼來的。後面再補幾個問題,避免你和其他概念搞混,但不會說得太深,具體需要你自己去擴充套件學習了。

Q: 為啥傳送地多了,資料就會部分丟失?這個是怎麼個原理?

A:路由器有快取,IP分組接收過多時就會耗盡空間,丟棄資料。詳細可以看路由器的資料轉發原理。

Q: TCP除了上面的重傳定時器, 好象還有個堅持定時器?區別是啥?

A:堅持定時器和超時、網路擁塞沒有關係, 和通告視窗即對端的接收能力有關。

簡單來說, 就是對方的傳輸層緩衝區(接收端視窗)滿了,告訴你別發了,我吃不下了,於是返回通告視窗為0。

但你想知道啥時候可以發,於是就啟動一個堅持定時器,每隔5s傳送1個位元組的小報文,小小地試探下。當通告視窗不為0了,就重新開始發。

點選關注,第一時間瞭解華為雲新鮮技術~