TCP發送視窗、接收視窗以及其工作原理

2023.11.16

TCP發送視窗、接收視窗以及其工作原理

可用視窗的計算是理解TCP滑動視窗的關鍵,要了解可用視窗的計算,我們需要理解三個指標——SND.UNA、SND.NXT和RCV.NXT。

上面的圖表是從發送方的角度拍攝的快照。我們可以將數據分為4組:

  • 已發送並已確認的位元組(藍色)
  • 已發送但尚未確認的位元組(黃色)
  • 未發送但接收方準備好接收的位元組(綠色)
  • 未發送且接收方未準備好接收的位元組(灰色)

第3類也稱為可用窗口,因為這是發送方可以使用的窗口。

發送視窗包括黃色和綠色部分。這些位元組要么已經被發送,要么可以被發送。

1*OqqxQKu4ZGasXzIlUZ9lyw.png

可用視窗在發送方發送了21-25位元組並使用了可用視窗中的所有位元組時可能為空。發送視窗保持不變。

1*JdTCgvYpVPRDcLyVb8Rwsg.png

當發送方接收到16-19位元組的確認時,發送視窗向右滑動4個位元組。佇列中的接下來的位元組會有一個更新的可用視窗。

1*9zFu_scvenahSK6m-khSjw.png

一些定義可以幫助我們更理解本文後面的複雜情況:

  • SND.WND,表示發送視窗
  • SND.UNA,表示發送未確認指針,指向發送視窗的第一個字節
  • SND.NXT,表示傳送下一個指針,指向可用視窗的第一個字節

1*IYBc3_OiPWAZ7JCvIaktkA.png

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

接收視窗

1*SbgJAvyVKyXYvPLuoBbGtA.png

接收視窗分為3個類別:

  • 已接收並已確認的位元組
  • 尚未接收但發送方允許發送的位元組
  • 尚未接收且發送方可能不允許發送的位元組

第2類稱為接收窗口,也可以稱為RCV.WND。

與發送視窗類似,有一個指標RCV.NXT,表示接收視窗的第一個位元組。

1*u3KoxvVK-rrM1g4gX6fbZQ.png

接收視窗並非靜態。如果伺服器運作得高效,接收視窗可以擴展。否則,它可能會縮小。

接收方透過在TCP段頭中的視窗欄位中指示大小來傳達其接收視窗。當發送方收到它時,這個視窗大小就成為了可用視窗。

發送和接收段需要時間。因此,接收視窗在特定時刻不等於可用視窗。

簡化的範例

讓我們模擬一次請求和回應,以更好地理解滑動視窗的工作原理。

有兩個修改簡化了我們的計算。

  • 我們忽略了最大段大小(MSS)。MSS會根據所選的網路路由而變化。
  • 我們使接收窗口等於可用窗口,而且在整個過程中都保持不變。

1*sAF4A2TyNeItzw2yK0xI9​​g.png

上面是一個顯示了10個步驟範例的圖表。

客戶端請求一個資源,伺服器以三個段落回應它:

  • 50位元組的頭部
  • 80位元組的正文部分1
  • 100位元組的正文部分2

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

我們假設客戶端的發送視窗(SND.WND)為300字節,接收視窗(RCV.WND)為150位元組。因此,伺服器的SND.WND為150字節,RCV.WND為300位元組。

1*cU9TEaEoezDwvw__3G1DFw.png

這是客戶端的起始狀態。

我們假設它之前已經從伺服器接收了300字節,因此RCV.NXT指向301。

由於它還沒有發送任何內容,SND.UNA和SND.NXT都指向1。

1*IYBc3_OiPWAZ7JCvIaktkA.png

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

1*kfa6ZdhSR_VJggJ2abUSAQ.png

這是伺服器的起始狀態,反映了另一側的狀態。

因為它已經發送了300字節,SND.UNA和SND.NXT都指向301。

由於客戶端還沒有發送任何請求,RCV.NXT指向1。

伺服器的可用視窗是301 + 150 - 301 = 150。

現在,第1步開始了。

客戶端發送了第一個100位元組的請求。在這一刻,窗口發生了變化。

  • 這100位元組已發送但尚未確認。因此,SND.NXT向右滑動了100位元組。
  • 其他指針保持不變。

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

1*ug0laVIMWQ3HGG-kPjZ0KA.png

在第2步,我們關注了伺服器。

  • 當伺服器接收到請求時,RCV.NXT向右滑動了100位元組。
  • 然後發送了帶有ACK的50位元組回應。這50位元組已發送但尚未確認,所以SND.NXT向右移動了50個位元組。
  • SND.UNA維持不變。

可用視窗變成301 + 150 - 351 = 100。

1*GAZqLwbVGj2yqUnm5DyI4w.png

移動到客戶端。

  • 當客戶端接收50位元組的回應時,RCV.NXT向右滑動了50位元組。
  • 當它收到前面發送的100位元組的ACK時,SND.UNA向右滑動。
  • 由於客戶端沒有發送任何數據,SND.NXT保持不變。

可用視窗變成101 + 300 - 101 = 300。

1*50UitYxS9XW3N4XV2Z7tOg.png

再次移動到伺服器的一端。

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

  • SND.NXT向右滑動了80個位元組。
  • 由於前面的50位元組尚未確認,SND.UNA保持不變。
  • 由於伺服器未接收任何數據,RCV.NXT保持不變。

可用視窗變成301 + 150 - 431 = 20。

1*soNJeyvqRj0zqDDrtBL9Fg.png

客戶端接收了文件的第一部分並立即發送了ACK。

  • 當客戶端接收到80位元組的資料時,RCV.NXT會向右滑動。
  • 其他指針保持不變。

可用視窗保持在300。

1*QJKwuY3HvslR9601ZRWCxA.png

此時,伺服器在發送步驟2時接收到ACK時。

  • 當伺服器發送50位元組的回應時,SND.UNA向右滑動了50個位元組。
  • 其他指針保持不變。

1*thuJ7lCYqreqxsL8nMz_ag.png

在步驟4中,伺服器發送了檔案的第一個80位元組部分,並再次收到了ACK確認。

  • SND.UNA(已確認序號)向右移動了80個位元組。
  • 其他指針保持不變。

可用視窗的計算變成431 + 150 - 431 = 150。

1*8sS5S0OkW0I2Vbp40nkkZQ.png

在第8步,伺服器發送了檔案的第二部分,共100位元組。

  • SND.NXT(下一個要傳送的序號)向右移動了100個位元組。
  • 其他指針保持不變。

可用視窗的計算變成431 + 150 - 531 = 50。

1*qXP9BCX80vPpkplc5utP6Q.png

接下來,輪到客戶端。

  • RCV.NXT(下一個要接收的序號)在客戶端接收100位元組後向右移動了100個位元組。
  • 其他指針保持不變。

可用視窗保持不變。

1*LkQ7tG-_1XQROjOZ3vTC9g.png

最後,伺服器接收了前一個回應的ACK。

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

可用視窗的計算變成531 + 150 - 531 = 150。

當視窗發生變化

在之前,我們假設發送視窗和接收視窗保持不變。但在實際情況中,這個假設是不正確的,因為兩個視窗中的位元組存在於作業系統緩衝區中,而緩衝區中的可用空間可以調整。當我們的應用程式無法快速讀取緩衝區中的位元組時,可用空間會減少。

讓我們看看視窗發生變化的情況,以及它如何影響可用視窗。

1*qyjkUdkAdsfrkRkqVlPClw.png

為了簡化,本例重點在於客戶端的可用視窗。在這個範例中,客戶端始終是發送方,伺服器是接收方。

1*u3KoxvVK-rrM1g4gX6fbZQ.png

當伺服器發送ACK時,它還包括了更新後的視窗大小。

1*pkiC_TWGpIZF3aSPOz6lcA.png

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

  • 這150位元組已發送但尚未確認。
  • 可用視窗縮小為150位元組。
  • 發送視窗保持在300位元組。

1*zs4VuHChJJ-7vWFmXyr8Ug.png

當伺服器接收請求時,應用程式讀取了前50字節,剩下的100位元組仍然在緩衝區中,從接收視窗中佔用了100位元組的可用空間。因此,接收視窗縮小到了200位元組。

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

1*SZDl6q22CB6kzY3P-CCHFA.png

客戶端接收ACK並將其發送視窗大小更新為200。

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

1*6gKYyaDUdOQSEGfHh6SWdA.png

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

1*uJiRzHmdV4kT8lPW62bz0g.png

在伺服器接收了這200位元組之後,應用程式仍然運作緩慢,總共只讀取了70字節,將280位元組留在緩衝區中。這導致接收視窗再次縮小,現在只剩下20位元組。

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

1*xnUjR-R45hPoGO7qhvCHKg.png

再次,客戶端在收到ACK後將其傳送視窗更新為20字節,可用視窗也變成20位元組。

在這種情況下,如果沒有更多來自伺服器的訊息,客戶端將停止發送大於20位元組的請求,直到在後續訊息中收到另一個視窗更新。

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

不會。為了避免這種情況,客戶端的TCP定期檢測視窗大小。

一旦釋放更多的空間,

可用視窗就會擴大,可以發送更多的資料。

主要內容

  • 可用視窗的計算是理解TCP滑動視窗的關鍵。
  • 要了解可用視窗的計算,我們需要理解3個指標-SND.UNA、SND.NXT和RCV.NXT。
  • 假設視窗大小永遠不會改變可以幫助我們理解整個過程。