自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

你還在為TCP重傳、滑動(dòng)窗口、流量控制、擁塞控制發(fā)愁嗎?看完圖解就不愁了

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
相信大家都知道 TCP 是一個(gè)可靠傳輸?shù)膮f(xié)議,那如何它是如何保證可靠的呢?今天,將重點(diǎn)介紹 TCP 的重傳機(jī)制、滑動(dòng)窗口、流量控制、擁塞控制。

前言

前一篇「硬不硬你說(shuō)了算!近 40 張圖解被問(wèn)千百遍的 TCP 三次握手和四次揮手面試題」得到了很多讀者的認(rèn)可,在此特別感謝你們的認(rèn)可,大家都暖暖的。

[[322830]]

來(lái)了,今天又來(lái)圖解 TCP 了,小林可能會(huì)遲到,但不會(huì)缺席。

遲到的原因,主要是 TCP 巨復(fù)雜,它為了保證可靠性,用了巨多的機(jī)制來(lái)保證,真是個(gè)「?jìng)ゴ蟆沟膮f(xié)議,寫著寫著發(fā)現(xiàn)這水太深了。。。

本文的全部圖片都是小林繪畫的,非常的辛苦且累,不廢話了,直接進(jìn)入正文吧,Go!

正文

相信大家都知道 TCP 是一個(gè)可靠傳輸?shù)膮f(xié)議,那如何它是如何保證可靠的呢?

為了實(shí)現(xiàn)可靠性傳輸,需要考慮很多事情,例如數(shù)據(jù)的破壞、丟包、重復(fù)以及分片順序混亂等問(wèn)題。如不能解決這些問(wèn)題,也就無(wú)從談起可靠傳輸。

那么,TCP 是通過(guò)序列號(hào)、確認(rèn)應(yīng)答、重發(fā)控制、連接管理以及窗口控制等機(jī)制實(shí)現(xiàn)可靠性傳輸?shù)摹?/p>

今天,將重點(diǎn)介紹 TCP 的重傳機(jī)制、滑動(dòng)窗口、流量控制、擁塞控制。

提綱

一、重傳機(jī)制

TCP 實(shí)現(xiàn)可靠傳輸?shù)姆绞街?,是通過(guò)序列號(hào)與確認(rèn)應(yīng)答。

在 TCP 中,當(dāng)發(fā)送端的數(shù)據(jù)到達(dá)接收主機(jī)時(shí),接收端主機(jī)會(huì)返回一個(gè)確認(rèn)應(yīng)答消息,表示已收到消息。

正常的數(shù)據(jù)傳輸

但在錯(cuò)綜復(fù)雜的網(wǎng)絡(luò),并不一定能如上圖那么順利能正常的數(shù)據(jù)傳輸,萬(wàn)一數(shù)據(jù)在傳輸過(guò)程中丟失了呢?

所以 TCP 針對(duì)數(shù)據(jù)包丟失的情況,會(huì)用重傳機(jī)制解決。

接下來(lái)說(shuō)說(shuō)常見的重傳機(jī)制:

  • 超時(shí)重傳
  • 快速重傳
  • SACK
  • D-SACK

1. 超時(shí)重傳

重傳機(jī)制的其中一個(gè)方式,就是在發(fā)送數(shù)據(jù)時(shí),設(shè)定一個(gè)定時(shí)器,當(dāng)超過(guò)指定的時(shí)間后,沒有收到對(duì)方的 ACK 確認(rèn)應(yīng)答報(bào)文,就會(huì)重發(fā)該數(shù)據(jù),也就是我們常說(shuō)的超時(shí)重傳。

TCP 會(huì)在以下兩種情況發(fā)生超時(shí)重傳:

  • 數(shù)據(jù)包丟失
  • 確認(rèn)應(yīng)答丟失

超時(shí)重傳的兩種情況

(1) 超時(shí)時(shí)間應(yīng)該設(shè)置為多少呢?

我們先來(lái)了解一下什么是 RTT(Round-Trip Time 往返時(shí)延),從下圖我們就可以知道:

RTT

RTT 就是數(shù)據(jù)從網(wǎng)絡(luò)一端傳送到另一端所需的時(shí)間,也就是包的往返時(shí)間。

超時(shí)重傳時(shí)間是以 RTO (Retransmission Timeout 超時(shí)重傳時(shí)間)表示。

假設(shè)在重傳的情況下,超時(shí)時(shí)間 RTO 「較長(zhǎng)或較短」時(shí),會(huì)發(fā)生什么事情呢?

超時(shí)時(shí)間較長(zhǎng)與較短

上圖中有兩種超時(shí)時(shí)間不同的情況:

  • 當(dāng)超時(shí)時(shí)間 RTO 較大時(shí),重發(fā)就慢,丟了老半天才重發(fā),沒有效率,性能差;
  • 當(dāng)超時(shí)時(shí)間 RTO 較小時(shí),會(huì)導(dǎo)致可能并沒有丟就重發(fā),于是重發(fā)的就快,會(huì)增加網(wǎng)絡(luò)擁塞,導(dǎo)致更多的超時(shí),更多的超時(shí)導(dǎo)致更多的重發(fā)。

精確的測(cè)量超時(shí)時(shí)間 RTO 的值是非常重要的,這可讓我們的重傳機(jī)制更高效。

根據(jù)上述的兩種情況,我們可以得知,超時(shí)重傳時(shí)間 RTO 的值應(yīng)該略大于報(bào)文往返 RTT 的值。

RTO 應(yīng)略大于 RTT

至此,可能大家覺得超時(shí)重傳時(shí)間 RTO 的值計(jì)算,也不是很復(fù)雜嘛。

好像就是在發(fā)送端發(fā)包時(shí)記下 t0 ,然后接收端再把這個(gè) ack 回來(lái)時(shí)再記一個(gè) t1,于是 RTT = t1 – t0。沒那么簡(jiǎn)單,這只是一個(gè)采樣,不能代表普遍情況。

實(shí)際上「報(bào)文往返 RTT 的值」是經(jīng)常變化的,因?yàn)槲覀兊木W(wǎng)絡(luò)也是時(shí)常變化的。也就因?yàn)椤笀?bào)文往返 RTT 的值」 是經(jīng)常波動(dòng)變化的,所以「超時(shí)重傳時(shí)間 RTO 的值」應(yīng)該是一個(gè)動(dòng)態(tài)變化的值。

我們來(lái)看看 Linux 是如何計(jì)算 RTO 的呢?

估計(jì)往返時(shí)間,通常需要采樣以下兩個(gè):

  • 需要 TCP 通過(guò)采樣 RTT 的時(shí)間,然后進(jìn)行加權(quán)平均,算出一個(gè)平滑 RTT 的值,而且這個(gè)值還是要不斷變化的,因?yàn)榫W(wǎng)絡(luò)狀況不斷地變化。
  • 除了采樣 RTT,還要采樣 RTT 的波動(dòng)范圍,這樣就避免如果 RTT 有一個(gè)大的波動(dòng)的話,很難被發(fā)現(xiàn)的情況。

RFC6289 建議使用以下的公式計(jì)算 RTO:

RFC6289 建議的 RTO 計(jì)算

其中 SRTT 是計(jì)算平滑的RTT ,DevRTR 是計(jì)算平滑的RTT 與 最新 RTT 的差距。

在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。別問(wèn)怎么來(lái)的,問(wèn)就是大量實(shí)驗(yàn)中調(diào)出來(lái)的。

如果超時(shí)重發(fā)的數(shù)據(jù),再次超時(shí)的時(shí)候,又需要重傳的時(shí)候,TCP 的策略是超時(shí)間隔加倍。

也就是每當(dāng)遇到一次超時(shí)重傳的時(shí)候,都會(huì)將下一次超時(shí)時(shí)間間隔設(shè)為先前值的兩倍。兩次超時(shí),就說(shuō)明網(wǎng)絡(luò)環(huán)境差,不宜頻繁反復(fù)發(fā)送。

超時(shí)觸發(fā)重傳存在的問(wèn)題是,超時(shí)周期可能相對(duì)較長(zhǎng)。那是不是可以有更快的方式呢?

于是就可以用「快速重傳」機(jī)制來(lái)解決超時(shí)重發(fā)的時(shí)間等待。

2. 快速重傳

TCP 還有另外一種快速重傳(Fast Retransmit)機(jī)制,它不以時(shí)間為驅(qū)動(dòng),而是以數(shù)據(jù)驅(qū)動(dòng)重傳。

快速重傳機(jī)制,是如何工作的呢?其實(shí)很簡(jiǎn)單,一圖勝千言。

快速重傳機(jī)制

在上圖,發(fā)送方發(fā)出了 1,2,3,4,5 份數(shù)據(jù):

  • 第一份 Seq1 先送到了,于是就 Ack 回 2;
  • 結(jié)果 Seq2 因?yàn)槟承┰驔]收到,Seq3 到達(dá)了,于是還是 Ack 回 2;
  • 后面的 Seq4 和 Seq5 都到了,但還是 Ack 回 2,因?yàn)?Seq2 還是沒有收到;
  • 發(fā)送端收到了三個(gè) Ack = 2 的確認(rèn),知道了 Seq2 還沒有收到,就會(huì)在定時(shí)器過(guò)期之前,重傳丟失的 Seq2。
  • 最后,接收到收到了 Seq2,此時(shí)因?yàn)?Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。

所以,快速重傳的工作方式是當(dāng)收到三個(gè)相同的 ACK 報(bào)文時(shí),會(huì)在定時(shí)器過(guò)期之前,重傳丟失的報(bào)文段。

快速重傳機(jī)制只解決了一個(gè)問(wèn)題,就是超時(shí)時(shí)間的問(wèn)題,但是它依然面臨著另外一個(gè)問(wèn)題。就是重傳的時(shí)候,是重傳之前的一個(gè),還是重傳所有的問(wèn)題。

比如對(duì)于上面的例子,是重傳 Seq2 呢?還是重傳 Seq2、Seq3、Seq4、Seq5 呢?因?yàn)榘l(fā)送端并不清楚這連續(xù)的三個(gè) Ack 2 是誰(shuí)傳回來(lái)的。

根據(jù) TCP 不同的實(shí)現(xiàn),以上兩種情況都是有可能的??梢?,這是一把雙刃劍。

為了解決不知道該重傳哪些 TCP 報(bào)文,于是就有 SACK方法。

3. SACK 方法

還有一種實(shí)現(xiàn)重傳機(jī)制的方式叫:SACK( Selective Acknowledgment 選擇性確認(rèn))。

這種方式需要在 TCP 頭部「選項(xiàng)」字段里加一個(gè) SACK的東西,它可以將緩存的地圖發(fā)送給發(fā)送方,這樣發(fā)送方就可以知道哪些數(shù)據(jù)收到了,哪些數(shù)據(jù)沒收到,知道了這些信息,就可以只重傳丟失的數(shù)據(jù)。

如下圖,發(fā)送方收到了三次同樣的 ACK 確認(rèn)報(bào)文,于是就會(huì)觸發(fā)快速重發(fā)機(jī)制,通過(guò) SACK 信息發(fā)現(xiàn)只有 200~299 這段數(shù)據(jù)丟失,則重發(fā)時(shí),就只選擇了這個(gè) TCP 段進(jìn)行重復(fù)。

選擇性確認(rèn)

如果要支持 SACK,必須雙方都要支持。在 Linux 下,可以通過(guò) net.ipv4.tcp_sack 參數(shù)打開這個(gè)功能(Linux 2.4 后默認(rèn)打開)。

4. Duplicate SACK

Duplicate SACK 又稱 D-SACK,其主要使用了 SACK 來(lái)告訴「發(fā)送方」有哪些數(shù)據(jù)被重復(fù)接收了。

下面舉例兩個(gè)栗子,來(lái)說(shuō)明 D-SACK 的作用。

栗子一號(hào):ACK 丟包

ACK 丟包

  • 「接收方」發(fā)給「發(fā)送方」的兩個(gè) ACK 確認(rèn)應(yīng)答都丟失了,所以發(fā)送方超時(shí)后,重傳第一個(gè)數(shù)據(jù)包(3000 ~ 3499)
  • 于是「接收方」發(fā)現(xiàn)數(shù)據(jù)是重復(fù)收到的,于是回了一個(gè) SACK = 3000~3500,告訴「發(fā)送方」 3000~3500 的數(shù)據(jù)早已被接收了,因?yàn)?ACK 都到了 4000 了,已經(jīng)意味著 4000 之前的所有數(shù)據(jù)都已收到,所以這個(gè) SACK 就代表著 D-SACK。
  • 這樣「發(fā)送方」就知道了,數(shù)據(jù)沒有丟,是「接收方」的 ACK 確認(rèn)報(bào)文丟了。

栗子二號(hào):網(wǎng)絡(luò)延時(shí)

網(wǎng)絡(luò)延時(shí)

  • 數(shù)據(jù)包(1000~1499) 被網(wǎng)絡(luò)延遲了,導(dǎo)致「發(fā)送方」沒有收到 Ack 1500 的確認(rèn)報(bào)文。
  • 而后面報(bào)文到達(dá)的三個(gè)相同的 ACK 確認(rèn)報(bào)文,就觸發(fā)了快速重傳機(jī)制,但是在重傳后,被延遲的數(shù)據(jù)包(1000~1499)又到了「接收方」;
  • 所以「接收方」回了一個(gè) SACK=1000~1500,因?yàn)?ACK 已經(jīng)到了 3000,所以這個(gè) SACK 是 D-SACK,表示收到了重復(fù)的包。
  • 這樣發(fā)送方就知道快速重傳觸發(fā)的原因不是發(fā)出去的包丟了,也不是因?yàn)榛貞?yīng)的 ACK 包丟了,而是因?yàn)榫W(wǎng)絡(luò)延遲了。

可見,D-SACK 有這么幾個(gè)好處:

  • 可以讓「發(fā)送方」知道,是發(fā)出去的包丟了,還是接收方回應(yīng)的 ACK 包丟了;
  • 可以知道是不是「發(fā)送方」的數(shù)據(jù)包被網(wǎng)絡(luò)延遲了;
  • 可以知道網(wǎng)絡(luò)中是不是把「發(fā)送方」的數(shù)據(jù)包給復(fù)制了;

在 Linux 下可以通過(guò) net.ipv4.tcp_dsack 參數(shù)開啟/關(guān)閉這個(gè)功能(Linux 2.4 后默認(rèn)打開)。

二、滑動(dòng)窗口

1. 引入窗口概念的原因

我們都知道 TCP 是每發(fā)送一個(gè)數(shù)據(jù),都要進(jìn)行一次確認(rèn)應(yīng)答。當(dāng)上一個(gè)數(shù)據(jù)包收到了應(yīng)答了, 再發(fā)送下一個(gè)。

這個(gè)模式就有點(diǎn)像我和你面對(duì)面聊天,你一句我一句。但這種方式的缺點(diǎn)是效率比較低的。

如果你說(shuō)完一句話,我在處理其他事情,沒有及時(shí)回復(fù)你,那你不是要干等著我做完其他事情后,我回復(fù)你,你才能說(shuō)下一句話,很顯然這不現(xiàn)實(shí)。

按數(shù)據(jù)包進(jìn)行確認(rèn)應(yīng)答

所以,這樣的傳輸方式有一個(gè)缺點(diǎn):數(shù)據(jù)包的往返時(shí)間越長(zhǎng),通信的效率就越低。

為解決這個(gè)問(wèn)題,TCP 引入了窗口這個(gè)概念。即使在往返時(shí)間較長(zhǎng)的情況下,它也不會(huì)降低網(wǎng)絡(luò)通信的效率。

那么有了窗口,就可以指定窗口大小,窗口大小就是指無(wú)需等待確認(rèn)應(yīng)答,而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值。

窗口的實(shí)現(xiàn)實(shí)際上是操作系統(tǒng)開辟的一個(gè)緩存空間,發(fā)送方主機(jī)在等到確認(rèn)應(yīng)答返回之前,必須在緩沖區(qū)中保留已發(fā)送的數(shù)據(jù)。如果按期收到確認(rèn)應(yīng)答,此時(shí)數(shù)據(jù)就可以從緩存區(qū)清除。

假設(shè)窗口大小為 3 個(gè) TCP 段,那么發(fā)送方就可以「連續(xù)發(fā)送」 3 個(gè) TCP 段,并且中途若有 ACK 丟失,可以通過(guò)「下一個(gè)確認(rèn)應(yīng)答進(jìn)行確認(rèn)」。如下圖:

用滑動(dòng)窗口方式并行處理

圖中的 ACK 600 確認(rèn)應(yīng)答報(bào)文丟失,也沒關(guān)系,因?yàn)榭梢酝ㄔ捪乱粋€(gè)確認(rèn)應(yīng)答進(jìn)行確認(rèn),只要發(fā)送方收到了 ACK 700 確認(rèn)應(yīng)答,就意味著 700 之前的所有數(shù)據(jù)「接收方」都收到了。這個(gè)模式就叫累計(jì)確認(rèn)或者累計(jì)應(yīng)答。

(1) 窗口大小由哪一方?jīng)Q定?

TCP 頭里有一個(gè)字段叫 Window,也就是窗口大小。

這個(gè)字段是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個(gè)接收端的處理能力來(lái)發(fā)送數(shù)據(jù),而不會(huì)導(dǎo)致接收端處理不過(guò)來(lái)。

所以,通常窗口的大小是由接收方的決定的。

發(fā)送方發(fā)送的數(shù)據(jù)大小不能超過(guò)接收方的窗口大小,否則接收方就無(wú)法正常接收到數(shù)據(jù)。

(2) 發(fā)送方的滑動(dòng)窗口

我們先來(lái)看看發(fā)送方的窗口,下圖就是發(fā)送方緩存的數(shù)據(jù),根據(jù)處理的情況分成四個(gè)部分,其中深藍(lán)色方框是發(fā)送窗口,紫色方框是可用窗口:

  • #1 是已發(fā)送并收到 ACK確認(rèn)的數(shù)據(jù):1~31 字節(jié)
  • #2 是已發(fā)送但未收到 ACK確認(rèn)的數(shù)據(jù):32~45 字節(jié)
  • #3 是未發(fā)送但總大小在接收方處理范圍內(nèi)(接收方還有空間):46~51字節(jié)
  • #4 是未發(fā)送但總大小超過(guò)接收方處理范圍(接收方?jīng)]有空間):52字節(jié)以后

在下圖,當(dāng)發(fā)送方把數(shù)據(jù)「全部」都一下發(fā)送出去后,可用窗口的大小就為 0 了,表明可用窗口耗盡,在沒收到 ACK 確認(rèn)之前是無(wú)法繼續(xù)發(fā)送數(shù)據(jù)了。

可用窗口耗盡

在下圖,當(dāng)收到之前發(fā)送的數(shù)據(jù) 32~36 字節(jié)的 ACK 確認(rèn)應(yīng)答后,如果發(fā)送窗口的大小沒有變化,則滑動(dòng)窗口往右邊移動(dòng) 5 個(gè)字節(jié),因?yàn)橛?5 個(gè)字節(jié)的數(shù)據(jù)被應(yīng)答確認(rèn),接下來(lái) 52~56 字節(jié)又變成了可用窗口,那么后續(xù)也就可以發(fā)送 52~56 這 5 個(gè)字節(jié)的數(shù)據(jù)了。

32 ~ 36 字節(jié)已確認(rèn)

(3) 程序是如何表示發(fā)送方的四個(gè)部分的呢?

TCP 滑動(dòng)窗口方案使用三個(gè)指針來(lái)跟蹤在四個(gè)傳輸類別中的每一個(gè)類別中的字節(jié)。其中兩個(gè)指針是絕對(duì)指針(指特定的序列號(hào)),一個(gè)是相對(duì)指針(需要做偏移)。

SND.WND、SND.UN、SND.NXT

  • SND.WND:表示發(fā)送窗口的大小(大小是由接收方指定的);
  • SND.UNA:是一個(gè)絕對(duì)指針,它指向的是已發(fā)送但未收到確認(rèn)的第一個(gè)字節(jié)的序列號(hào),也就是 #2 的第一個(gè)字節(jié)。
  • SND.NXT:也是一個(gè)絕對(duì)指針,它指向未發(fā)送但可發(fā)送范圍的第一個(gè)字節(jié)的序列號(hào),也就是 #3 的第一個(gè)字節(jié)。
  • 指向 #4 的第一個(gè)字節(jié)是個(gè)相對(duì)指針,它需要 SND.NXT 指針加上 SND.WND 大小的偏移量,就可以指向 #4 的第一個(gè)字節(jié)了。

那么可用窗口大小的計(jì)算就可以是:可用窗口大 = SND.WND -(SND.NXT - SND.UNA)

(4) 接收方的滑動(dòng)窗口

接下來(lái)我們看看接收方的窗口,接收窗口相對(duì)簡(jiǎn)單一些,根據(jù)處理的情況劃分成三個(gè)部分:

  • #1 + #2 是已成功接收并確認(rèn)的數(shù)據(jù)(等待應(yīng)用進(jìn)程讀取);
  • #3 是未收到數(shù)據(jù)但可以接收的數(shù)據(jù);
  • #4 未收到數(shù)據(jù)并不可以接收的數(shù)據(jù);

接收窗口

其中三個(gè)接收部分,使用兩個(gè)指針進(jìn)行劃分:

  • RCV.WND:表示接收窗口的大小,它會(huì)通告給發(fā)送方。
  • RCV.NXT:是一個(gè)指針,它指向期望從發(fā)送方發(fā)送來(lái)的下一個(gè)數(shù)據(jù)字節(jié)的序列號(hào),也就是 #3 的第一個(gè)字節(jié)。
  • 指向 #4 的第一個(gè)字節(jié)是個(gè)相對(duì)指針,它需要 RCV.NXT 指針加上 RCV.WND 大小的偏移量,就可以指向 #4 的第一個(gè)字節(jié)了。

(5) 接收窗口和發(fā)送窗口的大小是相等的嗎?

并不是完全相等,接收窗口的大小是約等于發(fā)送窗口的大小的。

因?yàn)榛瑒?dòng)窗口并不是一成不變的。比如,當(dāng)接收方的應(yīng)用進(jìn)程讀取數(shù)據(jù)的速度非常快的話,這樣的話接收窗口可以很快的就空缺出來(lái)。那么新的接收窗口大小,是通過(guò) TCP 報(bào)文中的 Windows 字段來(lái)告訴發(fā)送方。那么這個(gè)傳輸過(guò)程是存在時(shí)延的,所以接收窗口和發(fā)送窗口是約等于的關(guān)系。

三、流量控制

發(fā)送方不能無(wú)腦的發(fā)數(shù)據(jù)給接收方,要考慮接收方處理能力。

如果一直無(wú)腦的發(fā)數(shù)據(jù)給對(duì)方,但對(duì)方處理不過(guò)來(lái),那么就會(huì)導(dǎo)致觸發(fā)重發(fā)機(jī)制,從而導(dǎo)致網(wǎng)絡(luò)流量的無(wú)端的浪費(fèi)。

為了解決這種現(xiàn)象發(fā)生,TCP 提供一種機(jī)制可以讓「發(fā)送方」根據(jù)「接收方」的實(shí)際接收能力控制發(fā)送的數(shù)據(jù)量,這就是所謂的流量控制。

下面舉個(gè)栗子,為了簡(jiǎn)單起見,假設(shè)以下場(chǎng)景:

  • 客戶端是接收方,服務(wù)端是發(fā)送方
  • 假設(shè)接收窗口和發(fā)送窗口相同,都為 200
  • 假設(shè)兩個(gè)設(shè)備在整個(gè)傳輸過(guò)程中都保持相同的窗口大小,不受外界影響

流量控制

根據(jù)上圖的流量控制,說(shuō)明下每個(gè)過(guò)程:

  • 客戶端向服務(wù)端發(fā)送請(qǐng)求數(shù)據(jù)報(bào)文。這里要說(shuō)明下,本次例子是把服務(wù)端作為發(fā)送方,所以沒有畫出服務(wù)端的接收窗口。
  • 服務(wù)端收到請(qǐng)求報(bào)文后,發(fā)送確認(rèn)報(bào)文和 80 字節(jié)的數(shù)據(jù),于是可用窗口 Usable 減少為 120 字節(jié),同時(shí) SND.NXT 指針也向右偏移 80 字節(jié)后,指向 321,這意味著下次發(fā)送數(shù)據(jù)的時(shí)候,序列號(hào)是 321。
  • 客戶端收到 80 字節(jié)數(shù)據(jù)后,于是接收窗口往右移動(dòng) 80 字節(jié),RCV.NXT 也就指向 321,這意味著客戶端期望的下一個(gè)報(bào)文的序列號(hào)是 321,接著發(fā)送確認(rèn)報(bào)文給服務(wù)端。
  • 服務(wù)端再次發(fā)送了 120 字節(jié)數(shù)據(jù),于是可用窗口耗盡為 0,服務(wù)端無(wú)法在繼續(xù)發(fā)送數(shù)據(jù)。
  • 客戶端收到 120 字節(jié)的數(shù)據(jù)后,于是接收窗口往右移動(dòng) 120 字節(jié),RCV.NXT 也就指向 441,接著發(fā)送確認(rèn)報(bào)文給服務(wù)端。
  • 服務(wù)端收到對(duì) 80 字節(jié)數(shù)據(jù)的確認(rèn)報(bào)文后,SND.UNA指針往右偏移后指向 321,于是可用窗口 Usable 增大到 80。
  • 服務(wù)端收到對(duì) 120 字節(jié)數(shù)據(jù)的確認(rèn)報(bào)文后,SND.UNA指針往右偏移后指向 441,于是可用窗口 Usable 增大到 200。
  • 服務(wù)端可以繼續(xù)發(fā)送了,于是發(fā)送了 160 字節(jié)的數(shù)據(jù)后,SND.NXT 指向 601,于是可用窗口 Usable 減少到 40。
  • 客戶端收到 160 字節(jié)后,接收窗口往右移動(dòng)了 160 字節(jié),RCV.NXT 也就是指向了 601,接著發(fā)送確認(rèn)報(bào)文給服務(wù)端。
  • 服務(wù)端收到對(duì) 160 字節(jié)數(shù)據(jù)的確認(rèn)報(bào)文后,發(fā)送窗口往右移動(dòng)了 160 字節(jié),于是 SND.UNA 指針偏移了 160 后指向 601,可用窗口 Usable 也就增大至了 200。

1. 操作系統(tǒng)緩沖區(qū)與滑動(dòng)窗口的關(guān)系

前面的流量控制例子,我們假定了發(fā)送窗口和接收窗口是不變的,但是實(shí)際上,發(fā)送窗口和接收窗口中所存放的字節(jié)數(shù),都是放在操作系統(tǒng)內(nèi)存緩沖區(qū)中的,而操作系統(tǒng)的緩沖區(qū),會(huì)被操作系統(tǒng)調(diào)整。

當(dāng)應(yīng)用進(jìn)程沒辦法及時(shí)讀取緩沖區(qū)的內(nèi)容時(shí),也會(huì)對(duì)我們的緩沖區(qū)造成影響。

(1) 那操心系統(tǒng)的緩沖區(qū),是如何影響發(fā)送窗口和接收窗口的呢?

我們先來(lái)看看第一個(gè)例子。

當(dāng)應(yīng)用程序沒有及時(shí)讀取緩存時(shí),發(fā)送窗口和接收窗口的變化。

考慮以下場(chǎng)景:

  • 客戶端作為發(fā)送方,服務(wù)端作為接收方,發(fā)送窗口和接收窗口初始大小為 360;
  • 服務(wù)端非常的繁忙,當(dāng)收到客戶端的數(shù)據(jù)時(shí),應(yīng)用層不能及時(shí)讀取數(shù)據(jù)。

根據(jù)上圖的流量控制,說(shuō)明下每個(gè)過(guò)程:

  • 客戶端發(fā)送 140 字節(jié)數(shù)據(jù)后,可用窗口變?yōu)?220 (360 - 140)。
  • 服務(wù)端收到 140 字節(jié)數(shù)據(jù),但是服務(wù)端非常繁忙,應(yīng)用進(jìn)程只讀取了 40 個(gè)字節(jié),還有 100 字節(jié)占用著緩沖區(qū),于是接收窗口收縮到了 260 (360 - 100),最后發(fā)送確認(rèn)信息時(shí),將窗口大小通過(guò)給客戶端。
  • 客戶端收到確認(rèn)和窗口通告報(bào)文后,發(fā)送窗口減少為 260。
  • 客戶端發(fā)送 180 字節(jié)數(shù)據(jù),此時(shí)可用窗口減少到 80。
  • 服務(wù)端收到 180 字節(jié)數(shù)據(jù),但是應(yīng)用程序沒有讀取任何數(shù)據(jù),這 180 字節(jié)直接就留在了緩沖區(qū),于是接收窗口收縮到了 80 (260 - 180),并在發(fā)送確認(rèn)信息時(shí),通過(guò)窗口大小給客戶端。
  • 客戶端收到確認(rèn)和窗口通告報(bào)文后,發(fā)送窗口減少為 80。
  • 客戶端發(fā)送 80 字節(jié)數(shù)據(jù)后,可用窗口耗盡。
  • 服務(wù)端收到 80 字節(jié)數(shù)據(jù),但是應(yīng)用程序依然沒有讀取任何數(shù)據(jù),這 80 字節(jié)留在了緩沖區(qū),于是接收窗口收縮到了 0,并在發(fā)送確認(rèn)信息時(shí),通過(guò)窗口大小給客戶端。
  • 客戶端收到確認(rèn)和窗口通告報(bào)文后,發(fā)送窗口減少為 0。

可見最后窗口都收縮為 0 了,也就是發(fā)生了窗口關(guān)閉。當(dāng)發(fā)送方可用窗口變?yōu)?0 時(shí),發(fā)送方實(shí)際上會(huì)定時(shí)發(fā)送窗口探測(cè)報(bào)文,以便知道接收方的窗口是否發(fā)生了改變,這個(gè)內(nèi)容后面會(huì)說(shuō),這里先簡(jiǎn)單提一下。

我們先來(lái)看看第二個(gè)例子。

當(dāng)服務(wù)端系統(tǒng)資源非常緊張的時(shí)候,操心系統(tǒng)可能會(huì)直接減少了接收緩沖區(qū)大小,這時(shí)應(yīng)用程序又無(wú)法及時(shí)讀取緩存數(shù)據(jù),那么這時(shí)候就有嚴(yán)重的事情發(fā)生了,會(huì)出現(xiàn)數(shù)據(jù)包丟失的現(xiàn)象。

說(shuō)明下每個(gè)過(guò)程:

  • 客戶端發(fā)送 140 字節(jié)的數(shù)據(jù),于是可用窗口減少到了 220。
  • 服務(wù)端因?yàn)楝F(xiàn)在非常的繁忙,操作系統(tǒng)于是就把接收緩存減少了 100 字節(jié),當(dāng)收到 對(duì) 140 數(shù)據(jù)確認(rèn)報(bào)文后,又因?yàn)閼?yīng)用程序沒有讀取任何數(shù)據(jù),所以 140 字節(jié)留在了緩沖區(qū)中,于是接收窗口大小從 360 收縮成了 100,最后發(fā)送確認(rèn)信息時(shí),通告窗口大小給對(duì)方。
  • 此時(shí)客戶端因?yàn)檫€沒有收到服務(wù)端的通告窗口報(bào)文,所以不知道此時(shí)接收窗口收縮成了 100,客戶端只會(huì)看自己的可用窗口還有 220,所以客戶端就發(fā)送了 180 字節(jié)數(shù)據(jù),于是可用窗口減少到 40。
  • 服務(wù)端收到了 180 字節(jié)數(shù)據(jù)時(shí),發(fā)現(xiàn)數(shù)據(jù)大小超過(guò)了接收窗口的大小,于是就把數(shù)據(jù)包丟失了。
  • 客戶端收到第 2 步時(shí),服務(wù)端發(fā)送的確認(rèn)報(bào)文和通告窗口報(bào)文,嘗試減少發(fā)送窗口到 100,把窗口的右端向左收縮了 80,此時(shí)可用窗口的大小就會(huì)出現(xiàn)詭異的負(fù)值。

所以,如果發(fā)生了先減少緩存,再收縮窗口,就會(huì)出現(xiàn)丟包的現(xiàn)象。

為了防止這種情況發(fā)生,TCP 規(guī)定是不允許同時(shí)減少緩存又收縮窗口的,而是采用先收縮窗口,過(guò)段時(shí)間在減少緩存,這樣就可以避免了丟包情況。

2. 窗口關(guān)閉

在前面我們都看到了,TCP 通過(guò)讓接收方指明希望從發(fā)送方接收的數(shù)據(jù)大小(窗口大小)來(lái)進(jìn)行流量控制。

如果窗口大小為 0 時(shí),就會(huì)阻止發(fā)送方給接收方傳遞數(shù)據(jù),直到窗口變?yōu)榉?0 為止,這就是窗口關(guān)閉。

(1) 窗口關(guān)閉潛在的危險(xiǎn)

接收方向發(fā)送方通告窗口大小時(shí),是通過(guò) ACK 報(bào)文來(lái)通告的。

那么,當(dāng)發(fā)生窗口關(guān)閉時(shí),接收方處理完數(shù)據(jù)后,會(huì)向發(fā)送方通告一個(gè)窗口非 0 的 ACK 報(bào)文,如果這個(gè)通告窗口的 ACK 報(bào)文在網(wǎng)絡(luò)中丟失了,那麻煩就大了。

窗口關(guān)閉潛在的危險(xiǎn)

 

這會(huì)導(dǎo)致發(fā)送方一直等待接收方的非 0 窗口通知,接收方也一直等待發(fā)送方的數(shù)據(jù),如不不采取措施,這種相互等待的過(guò)程,會(huì)造成了死鎖的現(xiàn)象。

(2) TCP 是如何解決窗口關(guān)閉時(shí),潛在的死鎖現(xiàn)象呢?

為了解決這個(gè)問(wèn)題,TCP 為每個(gè)連接設(shè)有一個(gè)持續(xù)定時(shí)器,只要 TCP 連接一方收到對(duì)方的零窗口通知,就啟動(dòng)持續(xù)計(jì)時(shí)器。

如果持續(xù)計(jì)時(shí)器超時(shí),就會(huì)發(fā)送窗口探測(cè) ( Window probe ) 報(bào)文,而對(duì)方在確認(rèn)這個(gè)探測(cè)報(bào)文時(shí),給出自己現(xiàn)在的接收窗口大小。

窗口探測(cè)

  • 如果接收窗口仍然為 0,那么收到這個(gè)報(bào)文的一方就會(huì)重新啟動(dòng)持續(xù)計(jì)時(shí)器;
  • 如果接收窗口不是 0,那么死鎖的局面就可以被打破了。

窗口探查探測(cè)的次數(shù)一般為 3 此次,每次次大約 30-60 秒(不同的實(shí)現(xiàn)可能會(huì)不一樣)。如果 3 次過(guò)后接收窗口還是 0 的話,有的 TCP 實(shí)現(xiàn)就會(huì)發(fā) RST 報(bào)文來(lái)中斷連接。

3. 糊涂窗口綜合癥

如果接收方太忙了,來(lái)不及取走接收窗口里的數(shù)據(jù),那么就會(huì)導(dǎo)致發(fā)送方的發(fā)送窗口越來(lái)越小。

到最后,如果接收方騰出幾個(gè)字節(jié)并告訴發(fā)送方現(xiàn)在有幾個(gè)字節(jié)的窗口,而發(fā)送方會(huì)義無(wú)反顧地發(fā)送這幾個(gè)字節(jié),這就是糊涂窗口綜合癥。

要知道,我們的 TCP + IP 頭有 40 個(gè)字節(jié),為了傳輸那幾個(gè)字節(jié)的數(shù)據(jù),要達(dá)上這么大的開銷,這太不經(jīng)濟(jì)了。

就好像一個(gè)可以承載 50 人的大巴車,每次來(lái)了一兩個(gè)人,就直接發(fā)車。除非家里有礦的大巴司機(jī),才敢這樣玩,不然遲早破產(chǎn)。要解決這個(gè)問(wèn)題也不難,大巴司機(jī)等乘客數(shù)量超過(guò)了 25 個(gè),才認(rèn)定可以發(fā)車。

現(xiàn)舉個(gè)糊涂窗口綜合癥的栗子,考慮以下場(chǎng)景:

接收方的窗口大小是 360 字節(jié),但接收方由于某些原因陷入困境,假設(shè)接收方的應(yīng)用層讀取的能力如下:

接收方每接收 3 個(gè)字節(jié),應(yīng)用程序就只能從緩沖區(qū)中讀取 1 個(gè)字節(jié)的數(shù)據(jù);

  • 在下一個(gè)發(fā)送方的 TCP 段到達(dá)之前,應(yīng)用程序
  • 還從緩沖區(qū)中讀取了 40 個(gè)額外的字節(jié);

糊涂窗口綜合癥

每個(gè)過(guò)程的窗口大小的變化,在圖中都描述的很清楚了,可以發(fā)現(xiàn)窗口不斷減少了,并且發(fā)送的數(shù)據(jù)都是比較小的了。

所以,糊涂窗口綜合癥的現(xiàn)象是可以發(fā)生在發(fā)送方和接收方:

  • 接收方可以通告一個(gè)小的窗口
  • 而發(fā)送方可以發(fā)送小數(shù)據(jù)

于是,要解決糊涂窗口綜合癥,就解決上面兩個(gè)問(wèn)題就可以了

  • 讓接收方不通告小窗口給發(fā)送方
  • 讓發(fā)送方避免發(fā)送小數(shù)據(jù)

(1) 怎么讓接收方不通告小窗口呢?

接收方通常的策略如下:

當(dāng)「窗口大小」小于 min( MSS,緩存空間/2 ) ,也就是小于 MSS 與 1/2 緩存大小中的最小值時(shí),就會(huì)向發(fā)送方通告窗口為 0,也就阻止了發(fā)送方再發(fā)數(shù)據(jù)過(guò)來(lái)。

等到接收方處理了一些數(shù)據(jù)后,窗口大小 >= MSS,或者接收方緩存空間有一半可以使用,就可以把窗口打開讓發(fā)送方發(fā)送數(shù)據(jù)過(guò)來(lái)。

(2) 怎么讓發(fā)送方避免發(fā)送小數(shù)據(jù)呢?

發(fā)送方通常的策略:

使用 Nagle 算法,該算法的思路是延時(shí)處理,它滿足以下兩個(gè)條件中的一條才可以發(fā)送數(shù)據(jù):

  • 要等到窗口大小 >= MSS 或是 數(shù)據(jù)大小 >= MSS
  • 收到之前發(fā)送數(shù)據(jù)的 ack 回包

只要沒滿足上面條件中的一條,發(fā)送方一直在囤積數(shù)據(jù),直到滿足上面的發(fā)送條件。

另外,Nagle 算法默認(rèn)是打開的,如果對(duì)于一些需要小數(shù)據(jù)包交互的場(chǎng)景的程序,比如,telnet 或 ssh 這樣的交互性比較強(qiáng)的程序,則需要關(guān)閉 Nagle 算法。

可以在 Socket 設(shè)置 TCP_NODELAY 選項(xiàng)來(lái)關(guān)閉這個(gè)算法(關(guān)閉 Nagle 算法沒有全局參數(shù),需要根據(jù)每個(gè)應(yīng)用自己的特點(diǎn)來(lái)關(guān)閉)

  1. setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int)); 

四、擁塞控制

1. 為什么要有擁塞控制呀,不是有流量控制了嗎?

前面的流量控制是避免「發(fā)送方」的數(shù)據(jù)填滿「接收方」的緩存,但是并不知道網(wǎng)絡(luò)的中發(fā)生了什么。

一般來(lái)說(shuō),計(jì)算機(jī)網(wǎng)絡(luò)都處在一個(gè)共享的環(huán)境。因此也有可能會(huì)因?yàn)槠渌鳈C(jī)之間的通信使得網(wǎng)絡(luò)擁堵。

在網(wǎng)絡(luò)出現(xiàn)擁堵時(shí),如果繼續(xù)發(fā)送大量數(shù)據(jù)包,可能會(huì)導(dǎo)致數(shù)據(jù)包時(shí)延、丟失等,這時(shí) TCP 就會(huì)重傳數(shù)據(jù),但是一重傳就會(huì)導(dǎo)致網(wǎng)絡(luò)的負(fù)擔(dān)更重,于是會(huì)導(dǎo)致更大的延遲以及更多的丟包,這個(gè)情況就會(huì)進(jìn)入惡性循環(huán)被不斷地放大….

所以,TCP 不能忽略網(wǎng)絡(luò)上發(fā)生的事,它被設(shè)計(jì)成一個(gè)無(wú)私的協(xié)議,當(dāng)網(wǎng)絡(luò)發(fā)送擁塞時(shí),TCP 會(huì)自我犧牲,降低發(fā)送的數(shù)據(jù)量。

于是,就有了擁塞控制,控制的目的就是避免「發(fā)送方」的數(shù)據(jù)填滿整個(gè)網(wǎng)絡(luò)。

為了在「發(fā)送方」調(diào)節(jié)所要發(fā)送數(shù)據(jù)的量,定義了一個(gè)叫做「擁塞窗口」的概念。

2. 什么是擁塞窗口?和發(fā)送窗口有什么關(guān)系呢?

擁塞窗口 cwnd是發(fā)送方維護(hù)的一個(gè) 的狀態(tài)變量,它會(huì)根據(jù)網(wǎng)絡(luò)的擁塞程度動(dòng)態(tài)變化的。

我們?cè)谇懊嫣岬竭^(guò)發(fā)送窗口 swnd 和接收窗口 rwnd 是約等于的關(guān)系,那么由于入了擁塞窗口的概念后,此時(shí)發(fā)送窗口的值是swnd = min(cwnd, rwnd),也就是擁塞窗口和接收窗口中的最小值。

擁塞窗口 cwnd 變化的規(guī)則:

  • 只要網(wǎng)絡(luò)中沒有出現(xiàn)擁塞,cwnd 就會(huì)增大;
  • 但網(wǎng)絡(luò)中出現(xiàn)了擁塞,cwnd 就減少;

3. 那么怎么知道當(dāng)前網(wǎng)絡(luò)是否出現(xiàn)了擁塞呢?

其實(shí)只要「發(fā)送方」沒有在規(guī)定時(shí)間內(nèi)接收到 ACK 應(yīng)答報(bào)文,也就是發(fā)生了超時(shí)重傳,就會(huì)認(rèn)為網(wǎng)絡(luò)出現(xiàn)了用擁塞。

4. 擁塞控制有哪些控制算法?

擁塞控制主要是四個(gè)算法:

  • 慢啟動(dòng)
  • 擁塞避免
  • 擁塞發(fā)生
  • 快速恢復(fù)

(1) 慢啟動(dòng)

TCP 在剛建立連接完成后,首先是有個(gè)慢啟動(dòng)的過(guò)程,這個(gè)慢啟動(dòng)的意思就是一點(diǎn)一點(diǎn)的提高發(fā)送數(shù)據(jù)包的數(shù)量,如果一上來(lái)就發(fā)大量的數(shù)據(jù),這不是給網(wǎng)絡(luò)添堵嗎?

慢啟動(dòng)的算法記住一個(gè)規(guī)則就行:當(dāng)發(fā)送方每收到一個(gè) ACK,就擁塞窗口 cwnd 的大小就會(huì)加 1。

這里假定擁塞窗口 cwnd 和發(fā)送窗口 swnd 相等,下面舉個(gè)栗子:

  • 連接建立完成后,一開始初始化 cwnd = 1,表示可以傳一個(gè) MSS 大小的數(shù)據(jù)。
  • 當(dāng)收到一個(gè) ACK 確認(rèn)應(yīng)答后,cwnd 增加 1,于是一次能夠發(fā)送 2 個(gè)
  • 當(dāng)收到 2 個(gè)的 ACK 確認(rèn)應(yīng)答后, cwnd 增加 2,于是就可以比之前多發(fā)2 個(gè),所以這一次能夠發(fā)送 4 個(gè)
  • 當(dāng)這 4 個(gè)的 ACK 確認(rèn)到來(lái)的時(shí)候,每個(gè)確認(rèn) cwnd 增加 1, 4 個(gè)確認(rèn) cwnd 增加 4,于是就可以比之前多發(fā) 4 個(gè),所以這一次能夠發(fā)送 8 個(gè)。

慢啟動(dòng)算法

可以看出慢啟動(dòng)算法,發(fā)包的個(gè)數(shù)是指數(shù)性的增長(zhǎng)。

那慢啟動(dòng)漲到什么時(shí)候是個(gè)頭呢?

有一個(gè)叫慢啟動(dòng)門限 ssthresh (slow start threshold)狀態(tài)變量。

  • 當(dāng) cwnd < ssthresh 時(shí),使用慢啟動(dòng)算法。
  • 當(dāng) cwnd >= ssthresh 時(shí),就會(huì)使用「擁塞避免算法」。

(2) 擁塞避免算法

前面說(shuō)道,當(dāng)擁塞窗口 cwnd 「超過(guò)」慢啟動(dòng)門限 ssthresh 就會(huì)進(jìn)入擁塞避免算法。

一般來(lái)說(shuō) ssthresh 的大小是 65535 字節(jié)。

那么進(jìn)入擁塞避免算法后,它的規(guī)則是:每當(dāng)收到一個(gè) ACK 時(shí),cwnd 增加 1/cwnd。

接上前面的慢啟動(dòng)的栗子,現(xiàn)假定 ssthresh 為 8:

當(dāng) 8 個(gè) ACK 應(yīng)答確認(rèn)到來(lái)時(shí),每個(gè)確認(rèn)增加 1/8,8 個(gè) ACK 確認(rèn) cwnd 一共增加 1,于是這一次能夠發(fā)送 9 個(gè) MSS 大小的數(shù)據(jù),變成了線性增長(zhǎng)。

擁塞避免

所以,我們可以發(fā)現(xiàn),擁塞避免算法就是將原本慢啟動(dòng)算法的指數(shù)增長(zhǎng)變成了線性增長(zhǎng),還是增長(zhǎng)階段,但是增長(zhǎng)速度緩慢了一些。

就這么一直增長(zhǎng)著后,網(wǎng)絡(luò)就會(huì)慢慢進(jìn)入了擁塞的狀況了,于是就會(huì)出現(xiàn)丟包現(xiàn)象,這時(shí)就需要對(duì)丟失的數(shù)據(jù)包進(jìn)行重傳。

當(dāng)觸發(fā)了重傳機(jī)制,也就進(jìn)入了「擁塞發(fā)生算法」。

(3) 擁塞發(fā)生

當(dāng)網(wǎng)絡(luò)出現(xiàn)擁塞,也就是會(huì)發(fā)生數(shù)據(jù)包重傳,重傳機(jī)制主要有兩種:

  • 超時(shí)重傳
  • 快速重傳

這兩種使用的擁塞發(fā)送算法是不同的,接下來(lái)分別來(lái)說(shuō)說(shuō)。

a. 發(fā)生超時(shí)重傳的擁塞發(fā)生算法

當(dāng)發(fā)生了「超時(shí)重傳」,則就會(huì)使用擁塞發(fā)生算法。

這個(gè)時(shí)候,sshresh 和 cwnd 的值會(huì)發(fā)生變化:

  • ssthresh 設(shè)為 cwnd/2,
  • cwnd 重置為 1

擁塞發(fā)送 —— 超時(shí)重傳

接著,就重新開始慢啟動(dòng),慢啟動(dòng)是會(huì)突然減少數(shù)據(jù)流的。這真是一旦「超時(shí)重傳」,馬上回到解放前。但是這種方式太激進(jìn)了,反應(yīng)也很強(qiáng)烈,會(huì)造成網(wǎng)絡(luò)卡頓。

就好像本來(lái)在秋名山高速漂移著,突然來(lái)個(gè)緊急剎車,輪胎受得了嗎。。。

b. 發(fā)生快速重傳的擁塞發(fā)生算法

還有更好的方式,前面我們講過(guò)「快速重傳算法」。當(dāng)接收方發(fā)現(xiàn)丟了一個(gè)中間包的時(shí)候,發(fā)送三次前一個(gè)包的 ACK,于是發(fā)送端就會(huì)快速地重傳,不必等待超時(shí)再重傳。

TCP 認(rèn)為這種情況不嚴(yán)重,因?yàn)榇蟛糠譀]丟,只丟了一小部分,則 ssthresh 和 cwnd 變化如下:

  • cwnd = cwnd/2 ,也就是設(shè)置為原來(lái)的一半;
  • ssthresh = cwnd;
  • 進(jìn)入快速恢復(fù)算法

(4) 快速恢復(fù)

快速重傳和快速恢復(fù)算法一般同時(shí)使用,快速恢復(fù)算法是認(rèn)為,你還能收到 3 個(gè)重復(fù) ACK 說(shuō)明網(wǎng)絡(luò)也不那么糟糕,所以沒有必要像 RTO 超時(shí)那么強(qiáng)烈。

正如前面所說(shuō),進(jìn)入快速恢復(fù)之前,cwnd 和 ssthresh已被更新了:

  • cwnd = cwnd/2 ,也就是設(shè)置為原來(lái)的一半;
  • ssthresh = cwnd;

然后,進(jìn)入快速恢復(fù)算法如下:

  • 擁塞窗口 cwnd = ssthresh + 3 ( 3 的意思是確認(rèn)有 3 個(gè)數(shù)據(jù)包被收到了)
  • 重傳丟失的數(shù)據(jù)包
  • 如果再收到重復(fù)的 ACK,那么 cwnd 增加 1
  • 如果收到新數(shù)據(jù)的 ACK 后,設(shè)置 cwnd 為 ssthresh,接著就進(jìn)入了擁塞避免算法

快速重傳和快速恢復(fù)

也就是沒有像「超時(shí)重傳」一夜回到解放前,而是還在比較高的值,后續(xù)呈線性增長(zhǎng)。

 

責(zé)任編輯:趙寧寧 來(lái)源: 小林coding
相關(guān)推薦

2021-07-27 05:13:12

TCPUDP 擁塞

2020-07-23 15:01:15

TCP流量擁塞

2022-10-17 08:21:29

UDPTCP

2023-10-17 16:30:00

TCP

2015-12-29 13:53:14

BeeCloud云計(jì)算支付

2019-07-02 10:22:15

TCP流量數(shù)據(jù)

2022-02-15 08:30:04

TCP三次握手四次揮手

2010-02-03 23:04:31

流量控制P2P華夏創(chuàng)新

2019-04-16 11:02:10

TCPIPLinux

2022-07-27 07:36:01

TCP可靠性

2023-10-08 12:14:42

Sentinel流量控制

2011-06-23 09:09:37

流量控制

2013-07-22 14:25:29

iOS開發(fā)ASIHTTPRequ

2024-09-06 09:51:47

2013-11-18 10:04:31

TCP 滑動(dòng)窗口

2010-06-17 17:00:07

Linux流量控制

2010-06-04 10:49:58

Linux流量控制

2021-03-09 07:38:15

Percona Xtr流量控制運(yùn)維

2022-10-26 08:16:58

jar包依賴關(guān)系Maven

2010-06-10 15:14:32

TCP傳輸控制協(xié)議
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)