速讀原著-TCP/IP(糊塗窗口綜合症)
- 2020 年 3 月 12 日
- 筆記
第22章 TCP的堅持定時器
22.3 糊塗窗口綜合症
基於窗口的流量控制方案,如 T C P所使用的,會導致一種被稱為「糊塗窗口綜合症 S W S(Silly Window Syndrome)」的狀況。如果發生這種情況,則少量的數據將通過連接進行交換,而不是滿長度的報文段[Clark 1982]。
該現象可發生在兩端中的任何一端:接收方可以通告一個小的窗口(而不是一直等到有大的窗口時才通告),而發送方也可以發送少量的數據(而不是等待其他的數據以便發送一個大的報文段)。可以在任何一端採取措施避免出現糊塗窗口綜合症的現象。
- 接收方不通告小窗口。通常的演算法是接收方不通告一個比當前窗口大的窗口(可以為0),除非窗口可以增加一個報文段大小(也就是將要接收的 M S S)或者可以增加接收方快取空間的一半,不論實際有多少。
- 發送方避免出現糊塗窗口綜合症的措施是只有以下條件之一滿足時才發送數據: ( a )可以發送一個滿長度的報文段; ( b )可以發送至少是接收方通告窗口大小一半的報文段; ( c )可以發送任何數據並且不希望接收 A C K(也就是說,我們沒有還未被確認的數據)或者該連接上不能使用N a g l e演算法(見第1 9 . 4節)。條件( b )主要對付那些總是通告小窗口(也許比 1個報文段還小)的主機,條件 ( c )使我們在有尚未被確認的數據(正在等待被確認)以及在不能使用 N a g l e演算法的情況下,避免發送小的報文段。如果應用進程在進行小數據的寫操作(例如比該報文段還小),條件( c )可以避免出現糊塗窗口綜合症。
這三個條件也可以讓我們回答這樣一個問題:在有尚未被確認數據的情況下,如果 N a g l e演算法阻止我們發送小的報文段,那麼多小才算是小呢?從條件 ( a )中可以看出所謂「小」就是指位元組數小於報文段的大小。條件 ( b )僅用來對付較老的、原始的主機。步驟2中的條件( b )要求發送方始終監視另一方通告的最大窗口大小,這是一種發送方猜測對方接收快取大小的企圖。雖然在連接建立時接收快取的大小可能會減小,但在實際中這種情況很少見。
一個例子 現在我們通過仔細查看一個詳細的例子來觀察實際避免出現糊塗窗口綜合症的情況,該例子也包括了堅持定時器。我們將在發送主機 s u n上運行s o c k程式,並向網路寫 6個1 0 2 4位元組的數據。
sun % sock -i -n6 bsdi 7777
但是在主機b s d i的接收過程中我們加入一些暫停。在第 1次讀數據前暫停4秒,之後每次讀之前暫停2秒。而且,接收方進行的是 2 5 6位元組的讀操作:
bsdi % sock -i -s -P4 -p2 -r256 7777
最初的暫停是為了讓接收快取被填滿,迫使發送方停止發送。隨後由於接收方從網路上進行了一些小數目的讀取,我們預期能看到接收方採取的避免糊塗窗口綜合症的措施。
圖2 2 – 2是傳輸6 1 4 4位元組數據的時間系列(我們去掉了連接建立過程)。我們還需要跟蹤在每個時間點上讀取數據時應用程式的運行情況、當前正在接收快取中的數據的序號以及接收快取中可用空間的大小。圖 2 2 – 3顯示了所發生的每件事情。
圖2 2 – 3中的第1列是每個行為的相對時間點。那些帶有3位小數點的時間是從t c p d u m p的輸出結果(圖2 2 – 2)中得到的,而小數點部分為 9 9的則是在接收伺服器上產生行為的估計時間(使這些在接收方的估計時間包含一秒的9 9 %僅與圖2 2 – 2中的報文段2 0和2 2有關,它們是我們能夠從t c p d u m p的輸出結果中看到的由接收主機超時引起的僅有的兩個事件。而在主機 b s d i上觀察到的其他分組,則是由接收到來自發送方的一個報文段所引起的。這同樣是有意義的,因為這就使我們可以將最初的 4秒暫停剛好放置在發送方發送第 1個數據報文段的時間 0前面。這是接收方在連接建立過程中收到它的S Y N的A C K之後將要獲得控制權的大致時間)。

當接收到來自發送方的數據時,接收方快取中的數據增加,而當應用進程從快取中讀取數據時,數據就減少。接下來我們關注的是接收方發給發送方的窗口通告以及這些窗口通告是什麼。這樣就可以使我們看到接收方是如何避免糊塗窗口綜合症的。
前4個數據報文段及其A C K(報文段1 ~ 5)表示發送方正在填充接收方的快取。在那個時刻發送方停止了發送,但仍然有更多的數據需要發送。它將自己的堅持定時器置為最小值 5分鐘。當堅持定時器時間到時,就發送出 1個位元組的數據(報文段 6)。接收的應用進程已經從接收快取中讀取了2 5 6位元組的數據(在時刻 3 . 9 9),因此這個位元組被接受並被確認(報文段 7段)。但是通告窗口仍為0,由於接收方仍然沒有足夠的空間來接收一個滿長度的報文,或者不能騰出快取空間的一半。這就是接收方的糊塗窗口避免措施。

發送方的堅持定時器被複位,並在 5秒後再次到時(在時刻1 0 . 1 5 1)。然後又發送一個位元組並被確認(報文段8和9),而接收方的快取空間還不夠用(1 0 2 2位元組),使得通告窗口為0。
發送方的堅持定時器在時刻 1 5 . 1 5 1再次時間到,又發送了另一個位元組並被確認(報文段1 0和11)。這一次由於接收方有 1 5 3 3位元組的有效快取空間,因此通告了一個非 0窗口。發送方立即利用這個窗口發送了1 0 2 4位元組的數據(報文段1 2)。對這1 0 2 4位元組數據的確認(報文段1 3)通告其窗口為5 0 9位元組。這看起來與我們在前面看到的小窗口通告相抵觸。
在這裡之所以發生這種情況,是因為報文段 11段通告了一個大小為 1 5 3 3位元組的窗口,而發送方只使用了其中的1 0 2 4位元組。如果在報文段1 3中的A C K通告其窗口為0,就會違反窗口的右邊沿不能向左邊沿移動而導致窗口收縮的 T C P原則(見第2 0 . 3節)。這就是為什麼必須通告一個5 0 9位元組的窗口的原因。
接下來我們看到發送方沒有立即向這個小窗口發送數據。這就是發送方採取的糊塗窗口避免策略。相反,它等待另一個堅持定時器在時刻 2 0 . 1 5 1到時間,並在該時刻發送 5 0 9位元組的數據。儘管它最終還是發送了一個長度為 5 0 9位元組的小數據段,但在發送前它等待了 5秒鐘,看是否會有一個A C K到達,以便可以將窗口開得更大。這 5 0 9位元組的數據使得接收快取僅剩下7 6 8位元組的有效空間,因此接收方通告窗口為 0(報文段1 5)。
堅持定時器在時刻2 5 . 1 5 1再次到時間,發送方發送 1個位元組,於是接收快取中有 1 2 7 9位元組的可用空間,這就是在報文段 1 7所通告的窗口大小。
發送方只有另外的5 11個位元組的數據需要發送,因此在收到 1 2 7 9的窗口通告後立刻發送了這些數據(報文段 1 8)。這個報文段也帶有 F I N標誌。接收方確認數據和 F I N,並通告窗口大小為7 6 7(見習題2 2 . 2)。
由於發送應用進程在執行完 6個1 0 2 4位元組的寫操作後發出關閉命令,發送方的連接從E S TA B L I S H E D狀態轉變到F I N _ WA I T _ 1狀態,再到F I N _ WA I T _ 2狀態(見圖1 8 – 1 2)。它一直處於這個狀態,直到收到對方的 F I N。在這個狀態上沒有設置定時器(回憶我們在 1 8 . 6節結束時的討論),因為它在報文段 1 8中發送的F I N被報文段1 9確認。這就是為什麼我們看到發送方直到接收到F I N(報文段2 1)為止沒有發送其他任何數據的原因。
接收應用進程繼續每隔 2秒從接收快取區中讀取 2 5 6個位元組的數據。為什麼在時刻 3 9 . 9 9發 送A C K(報文段2 0)呢?這是因為應用進程在時刻 3 9 . 9 9讀取數據時,接收快取中的可用空間已經從原來通告的7 6 7(報文段1 9)變為2 8 1 6,這相當於接收快取中增加了額外的 2 0 4 9位元組的空間。回憶本節開始講的第 1個規則,因為現在接收快取已經增加了其空間的一半,因此接收 方現在發送窗口更新。這意味著每次當應用進程從 T C P的接收快取中讀取數據時,接收的 T C P將檢查是否需要更新發送窗口。
應用進程在時間5 1 . 9 9發出最後一個讀操作,然後收到一個文件結束標誌,因為快取已經變空。這就導致了最後兩個完成連接終止的報文段(報文段 2 1和2 2)的發送。