天天看點

一文了解TCP滑動視窗原理

一文了解TCP滑動視窗原理

在我們當初學習網絡程式設計的時候,都接觸過TCP,在TCP中,對于資料傳輸有各種政策,比如滑動視窗、擁塞視窗機制,又比如慢啟動、快速恢複、擁塞避免等。通過本文,我們将了解滑動視窗在TCP中是如何使用的。

滑動視窗實作了TCP流控制。首先明确滑動視窗的範疇:

  • TCP是雙工的協定,會話的雙方都可以同時接收和發送資料。
  • 會話的雙方都各自維護一個發送視窗和一個接收視窗。各自的接收視窗大小取決于應用、系統、硬體的限制(TCP傳輸速率不能大于應用的資料處理速率)。各自的發送視窗則要求取決于對端通告的接收視窗,要求相同。

滑動視窗解決的是流量控制的的問題,就是如果接收端和發送端對資料包的處理速度不同,如何讓雙方達成一緻。接收端的緩存傳輸資料給應用層,但這個過程不一定是即時的,如果發送速度太快,會出現接收端資料overflow,流量控制解決的是這個問題。

發送端視窗

一文了解TCP滑動視窗原理

上圖是發送端滑動視窗的簡圖。 我們可以将資料分為4個部分:

  • 發送和已确認的位元組(藍色部分)
  • 已發送但尚未确認的位元組(黃色部分)
  • 未發送的位元組和接收方準備接收的位元組,即在緩沖區buffer中(綠色部分)
  • 未發送且接收方未準備接收的位元組(灰色部分)

其中第三部分,也就是綠色部分,也稱為可用視窗,因為這是發送方可以使用的視窗。

發送視窗由黃色和綠色部分組成。 這些位元組要麼已經發送,要麼可以發送。

當發送方發送21-25位元組并使用可用視窗中的所有位元組時,可用視窗可能為空,發送視窗保持不變(如下圖)。

一文了解TCP滑動視窗原理

當發送方收到第16-19位元組的 ACK 時,發送視窗向右滑動 4 個位元組。 更新的可用視窗可用于隊列中的以下位元組(如下圖)。

一文了解TCP滑動視窗原理

為了便于了解,我們後續将視窗名使用簡稱,即:

  • SND.WND,代表發送視窗
  • SND.UNA, 代表Send Unacknowledged指針,指向發送視窗的第一個位元組
  • SND.NXT, 代表Send Next指針,指向可用視窗的第一個位元組

使用簡寫後,如下圖所示:

一文了解TCP滑動視窗原理

基于這些定義,我們可以用公式表示可用的視窗大小。

可用視窗(可用視窗)大小 = SND.UNA + SND.WND - SND.NXT複制代碼
      

接收端視窗

一文了解TCP滑動視窗原理

接收視窗有三種:

  • 1、接收并且已經向發送端發送确認ACK
  • 2、尚未接收但允發送端發送資料
  • 3、尚未接收且不允許發送端發送資料

第二種稱為接收視窗,也稱為RCV.WND。 類似于發送視窗,指針RCV.NXT,代表Receive Next指針,指向接收視窗的第一個位元組。

一文了解TCP滑動視窗原理

接收視窗不是靜态的。如果服務端性能高,讀取資料快,接收視窗可能會擴大。 否則,它可能會縮小。

接收方通過在TCP段報頭中的視窗字段中訓示大小來傳達其接收視窗。 當發送方收到它時,這個視窗大小就成為可用視窗。

發送和接收資料需要時間。 是以,接收視窗不等于特定時刻的可用視窗。

下面,為了更好的了解滑動視窗在TCP中的使用,我們将使用一個簡單的例子進行模拟說明。

示例(大小不變)

我們模拟一個請求和響應,以更好地了解滑動視窗的工作原理。 為了模拟起來簡單,我們盡可能的簡化裡面的過程,比如:

  • 我們忽略最大段大小 (MSS)。 MSS 因選擇的網絡路由而不同。
  • 使接收視窗等于可用視窗,并且在此過程中兩者保持不變。
一文了解TCP滑動視窗原理

上圖示例中,有10個步驟。 用戶端請求資源,伺服器分三段響應:

  • 1、一個 50 位元組的標頭
  • 2、一個 80 位元組的資料1
  • 3、一個 100 位元組的資料2

每一方都可以同時是發送方和接收方。

我們假設用戶端的發送視窗 (SND.WND) 是 300 位元組,接收視窗 (RCV.WND) 是 150 位元組。 是以,伺服器的 SND.WND 為 150 位元組,RCV.WND 為 300 位元組。

一文了解TCP滑動視窗原理

上圖用戶端的起始狀态。

我們假設它之前已經從伺服器接收了300個位元組,是以RCV.NXT指向301。由于它還沒有發送任何東西,SND.UNA和SND.NXT都指向1。

可用視窗(可用視窗)大小 = SND.UNA + SND.WND - SND.NXT複制代碼
      

根據這個公式,用戶端的可用視窗大小為 1 + 300 - 1 = 300。

一文了解TCP滑動視窗原理

這是服務端的起始狀态,鏡像另一端即用戶端的狀态。

因為它已經發送了300個位元組,是以SND.UNA和SND.NXT都指向301。

RCV.NXT指向1,因為用戶端尚未發送任何請求。 伺服器的可用視窗是301 + 150 - 301 = 150。

現在,我們從步驟1開始:

一文了解TCP滑動視窗原理

用戶端發送它的第一個100位元組請求。

此刻,窗戶發生了變化。

  • 這 100 個位元組已發送,但尚未收到 ACK。 是以,SND.NXT 向右滑動 100 個位元組。
  • 其他指針保持不變。

可用視窗更改為 1 + 300 - 101 = 200。

一文了解TCP滑動視窗原理

在第 2 步,我們的焦點轉移到伺服器上,從服務端的角度來分析。

  • 當伺服器收到請求時,RCV.NXT 向右滑動 100 個位元組。
  • 然後它發送一個帶有 ACK 的 50 位元組回複。 這 50 個位元組已發送,但尚未發送 ACK,是以 SND.NXT 向右移動 50 個位元組。
  • SND.UNA不動。

可用視窗大小變為301 + 150 - 351 = 100。

一文了解TCP滑動視窗原理

讓我們現在繼續轉向用戶端。

  • 當收到50位元組的回複時,RCV.NXT向右移動50位元組。
  • SND.UNA 在收到前一個發送的 100 個位元組的 ACK 時向右滑動。
  • SND.NXT保持不變,因為用戶端不發送任何資料。

可用視窗更改為101 + 300 - 101 = 300。

一文了解TCP滑動視窗原理

再次移動到伺服器端。

可用視窗為 100 位元組。伺服器可以發送 80 位元組的段。

  • SND.NXT 向右滑動 80 個位元組。
  • SND.UNA 保持不變,因為最後 50 位元組尚未得到确認。
  • RCV.NXT 保持不變,因為伺服器沒有收到任何資料。

可用視窗更改為 301 + 150 - 431 = 20。

一文了解TCP滑動視窗原理

用戶端收到檔案的第一部分并立即發送ACK。

  • 當用戶端接收到 80 位元組的資料時,RCV.NXT 向右移動。
  • 其他部分不變。

可用視窗大小仍為300。

一文了解TCP滑動視窗原理

此時,伺服器在發送 50 位元組的回複時收到了第 2 步的 ACK。

  • SND.UNA 向右移動 50 個位元組。
  • 其他部分保持不變。

可用視窗大小變為351 + 150 - 431 = 70。

一文了解TCP滑動視窗原理

當伺服器發送資料1即80位元組部分時,再次收到第4步的另一個ACK。

  • SND.UNA 向右移動 80 個位元組。
  • 其他部分保持不變。

可用視窗大小變為431 + 150 - 431 = 150。

一文了解TCP滑動視窗原理

在第 8 步,伺服器資料2,大小為100位元組。

  • SND.NXT向右移動 100 個位元組。
  • 其他部分保持不變。

可用視窗大小變為431 + 150 - 531 = 50。

一文了解TCP滑動視窗原理

繼續轉到用戶端。

  • 當用戶端收到 100 位元組時,RCV.NXT 向右移動 100 位元組。
  • 其他部分保持不變。

可用視窗大小保持不變。

一文了解TCP滑動視窗原理

最後,伺服器收到前一個響應的 ACK。

  • SND.UNA向右移動100個位元組。
  • 其他部分保持不變。

可用視窗大小變為531 + 150 - 531 = 150。

至此,對于滑動視窗不變的示例,講解完畢,那麼對于滑動視窗大小變化的呢?在TCP中又是如果實作的呢?

示例(大小變化的視窗)

在前面的示例中,我們假設發送視窗和接收視窗保持不變。 這個假設本身在實際中就是不成立的,因為不存在。

兩個視窗中的位元組都存在于作業系統緩沖區中,可以對其進行調整。 例如,當我們的應用程式沒有足夠快地從中讀取位元組時,緩沖區中的可用空間就會縮小。

我們來介紹一下這種情況下的視窗變化,看看它是如何影響可用視窗的。

一文了解TCP滑動視窗原理

我們簡化了這種情況以将可用視窗集中在用戶端上。 在這個例子中,用戶端始終是發送方,而伺服器是接收方。

一文了解TCP滑動視窗原理

當伺服器發送 ACK 時,它也會在其中包含更新後的視窗大小。

一文了解TCP滑動視窗原理

一開始,用戶端發送第一個150位元組的請求。

  • 這 150 個位元組已發送,但尚未發送 ACK。
  • 可用視窗縮小到 150 位元組。

發送視窗保持在300位元組。

一文了解TCP滑動視窗原理

當伺服器收到請求時,應用程式讀取前 50 個位元組,還有 100 個位元組仍在緩沖區中,從接收視窗中占用 100 個位元組的可用空間。 是以,接收視窗縮小到 200 位元組。

接下來,伺服器發送帶有更新的 200 位元組接收視窗的 ACK。

一文了解TCP滑動視窗原理

用戶端收到 ACK 并将其發送視窗大小更新為 200。

此時,可用視窗與發送視窗相同,因為所有 150 個位元組都被确認。

一文了解TCP滑動視窗原理

用戶端再次發送另一個 200 位元組的請求,使用可用視窗中的所有可用空間。

一文了解TCP滑動視窗原理

伺服器接收到 200 位元組後,應用程式仍然運作緩慢,總共隻讀取了 70 位元組,并在緩沖區中留下了 280 位元組。

這會導緻接收視窗再次縮小。 現在,我們隻剩下 20 個位元組了。

在 ACK 消息中,伺服器與用戶端共享更新的視窗大小。

一文了解TCP滑動視窗原理

同樣,用戶端在收到 ACK 後将其發送視窗更新為 20 位元組。 可用視窗也變為 20 位元組。

在這種情況下,用戶端停止發送任何大于 20 位元組的請求,直到它收到以下消息中的另一個視窗更新。

如果沒有更多來自伺服器的消息,我們會被困在 20 位元組的可用視窗嗎?

我們不會。 為了避免這種情況,用戶端的 TCP 會定期檢測視窗大小。 一旦釋放更多空間,可用視窗就會擴大,并且可以發送更多資料。

結語

可用視窗的計算是了解TCP滑動視窗的關鍵。

要學習可用視窗的計算,我們需要了解 3 個指針——SND.UNA、SND.NXT 和 RCV.NXT。

繼續閱讀