22.1 引言
我們已經(jīng)看到TCP通過讓接收方指明希望從發(fā)送方接收的數(shù)據(jù)字節(jié)數(shù)(即窗口大小)來進(jìn)行流量控制哼绑。如果窗口大小為0會發(fā)生什么情況呢骨稿?這將有效地阻止發(fā)送方傳送數(shù)據(jù)座菠,直到窗口變?yōu)榉?為止。
可以在圖20-3中看到這種情況沉桌。當(dāng)發(fā)送方接收到報(bào)文段9時膳殷,它打開被報(bào)文段8關(guān)閉的窗口并立即開始發(fā)送數(shù)據(jù)鸠信。TCP必須能夠處理打開此窗口的ACK(報(bào)文段9)丟失的情況。ACK的傳輸并不可靠,也就是說键痛,TCP不對ACK報(bào)文段進(jìn)行確認(rèn)血淌,TCP只確認(rèn)那些包含有數(shù)據(jù)的ACK報(bào)文段判呕。
如果一個確認(rèn)丟失了品嚣,則雙方就有可能因?yàn)榈却龑Ψ蕉惯B接終止:接收方等待接收數(shù)據(jù)(因?yàn)樗呀?jīng)向發(fā)送方通告了一個非0的窗口),而發(fā)送方在等待允許它繼續(xù)發(fā)送數(shù)據(jù)的窗口更新。為防止這種死鎖情況的發(fā)生瘤运,發(fā)送方使用一個堅(jiān)持定時器(persist timer)來周期性地向接收方查詢窍霞,以便發(fā)現(xiàn)窗口是否已增大。這些從發(fā)送方發(fā)出的報(bào)文段稱為窗口探查(window probe)拯坟。在本章中但金,我們將討論窗口探查和堅(jiān)持定時器,還將討論與堅(jiān)持定時器有關(guān)的糊涂窗口綜合癥郁季。
22.2 一個例子
為了觀察到實(shí)際中的堅(jiān)持定時器傲绣,我們啟動一個接收進(jìn)程。它監(jiān)聽來自客戶的連接請求巩踏,接受該連接請求,然后在從網(wǎng)上讀取數(shù)據(jù)前休眠很長一段時間续搀。
sock程序可以通過指定一個暫停選項(xiàng)-P使服務(wù)器在接受連接和進(jìn)行第一次讀動作之間進(jìn)入休眠塞琼。我們以這種方式調(diào)用服務(wù)器:
svr4 % sock -i -s -P100000 5555
該命令在從網(wǎng)絡(luò)上讀數(shù)據(jù)之前休眠100 000秒(27.8小時)〗希客戶運(yùn)行在主機(jī)bsdi上彪杉,并向服務(wù)器的5555端口執(zhí)行1024字節(jié)的寫操作。圖22-1給出了tcpdump的輸出結(jié)果(我們已經(jīng)在結(jié)果中去掉了連接的建立過程)牵咙。
報(bào)文段1~13顯示的是從客戶到服務(wù)器的正常的數(shù)據(jù)傳輸過程派近,有9216個字節(jié)的數(shù)據(jù)填充了窗口。服務(wù)器通告窗口大小為4096字節(jié)洁桌,且默認(rèn)的插口緩存大小為4096字節(jié)渴丸。但實(shí)際上它一共接收了9216字節(jié)的數(shù)據(jù),這是在SVR4中TCP代碼和流子系統(tǒng)(stream subsystem)之間某種形式交互的結(jié)果另凌。
在報(bào)文段13中谱轨,服務(wù)器確認(rèn)了前面4個數(shù)據(jù)報(bào)文段,然后通告窗口為0吠谢,從而使客戶停止發(fā)送任何其他的數(shù)據(jù)土童。這就引起客戶設(shè)置其堅(jiān)持定時器。如果在該定時器時間到時客戶還沒有接收到一個窗口更新工坊,它就探查這個空的窗口以決定窗口更新是否丟失献汗。由于服務(wù)器進(jìn)程處于休眠狀態(tài),所以TCP緩存9216字節(jié)的數(shù)據(jù)并等待應(yīng)用進(jìn)程讀取王污。
請注意客戶發(fā)出的窗口探查之間的時間間隔罢吃。在收到一個大小為0的窗口通告后的第1個(報(bào)文段14)間隔為4.949秒,下一個(報(bào)文段16)間隔是4.996秒玉掸,隨后的間隔分別約為6,12,24,48和60秒刃麸。
為什么這些間隔總是比5、6司浪、12泊业、24把沼、48和60小一個零點(diǎn)幾秒呢?因?yàn)檫@些探查被TCP的500 ms定時器超時例程所觸發(fā)吁伺。當(dāng)定時器時間到時饮睬,就發(fā)送窗口探查,并大約在4ms之后收到一個應(yīng)答篮奄。接收到應(yīng)答使得定時器被重新啟動捆愁,但到下一個時鐘滴答之間的時間則約為500減4ms。
計(jì)算堅(jiān)持定時器時使用了普通的TCP指數(shù)退避窟却。對一個典型的局域網(wǎng)連接昼丑,首次超時時間算出來是1.5秒,第2次的超時值增加一倍夸赫,為3秒菩帝,再下次乘以4為6秒,之后再乘以8為12秒等茬腿。但是堅(jiān)持定時器總是在5~60秒之間呼奢,這與我們在圖22-1中觀察到的現(xiàn)象一致。
窗口探查包含一個字節(jié)的數(shù)據(jù)(序號為9217)切平。TCP總是允許在關(guān)閉連接前發(fā)送一個字節(jié)的數(shù)據(jù)握础。請注意,盡管如此悴品,所返回的窗口為0的ACK并不是確認(rèn)該字節(jié)(它們確認(rèn)了包括9216在內(nèi)的所有數(shù)據(jù))禀综,因此這個字節(jié)被持續(xù)重傳。
堅(jiān)持狀態(tài)與第21章中介紹的重傳超時之間一個不同的特點(diǎn)就是TCP從不放棄發(fā)送窗口探查他匪。這些探查每隔60秒發(fā)送一次菇存,這個過程將持續(xù)到或者窗口被打開,或者應(yīng)用進(jìn)程使用的連接被終止邦蜜。
22.3 糊涂窗口綜合癥
基于窗口的流量控制方案依鸥,如TCP所使用的,會導(dǎo)致一種被稱為“糊涂窗口綜合癥SWS (Silly Window Syndrome)”的狀況悼沈。如果發(fā)生這種情況贱迟,則少量的數(shù)據(jù)將通過連接進(jìn)行交換,而不是滿長度的報(bào)文段[Clark 1982]絮供。
該現(xiàn)象可發(fā)生在兩端中的任何一端:接收方可以通告一個小的窗口(而不是一直等到有大的窗口時才通告)衣吠,而發(fā)送方也可以發(fā)送少量的數(shù)據(jù)(而不是等待其他的數(shù)據(jù)以便發(fā)送一個大的報(bào)文段)∪腊校可以在任何一端采取措施避免出現(xiàn)糊涂窗口綜合癥的現(xiàn)象缚俏。
1:接收方不通告小窗口。通常的算法是接收方不通告一個比當(dāng)前窗口大的窗口(可以為0),除非窗口可以增加一個報(bào)文段大杏腔弧(也就是將要接收的MSS)或者可以增加接收方緩存空間的一半恬惯,不論實(shí)際有多少。
2:發(fā)送方避免出現(xiàn)糊涂窗口綜合癥的措施是只有以下條件之一滿足時才發(fā)送數(shù)據(jù):(a)可以發(fā)送一個滿長度的報(bào)文段亚茬;(b)可以發(fā)送至少是接收方通告窗口大小一半的報(bào)文段酪耳;(c)可以發(fā)送任何數(shù)據(jù)并且不希望接收ACK(也就是說,我們沒有還未被確認(rèn)的數(shù)據(jù))或者該連接上不能使用Nagle算法(見第19.4節(jié))刹缝。
條件(b)主要對付那些總是通告小窗口(也許比1個報(bào)文段還型氚怠)的主機(jī),條件(c)使我們在有尚未被確認(rèn)的數(shù)據(jù)(正在等待被確認(rèn))以及在不能使用Nagle算法的情況下梢夯,避免發(fā)送小的報(bào)文段言疗。如果應(yīng)用進(jìn)程在進(jìn)行小數(shù)據(jù)的寫操作(例如比該報(bào)文段還小)颂砸,條件(c)可以避免出現(xiàn)糊涂窗口綜合癥洲守。
這三個條件也可以讓我們回答這樣一個問題:在有尚未被確認(rèn)數(shù)據(jù)的情況下,如果Nagle算法阻止我們發(fā)送小的報(bào)文段沾凄,那么多小才算是小呢?從條件(a)中可以看出所謂“小”就是指字節(jié)數(shù)小于報(bào)文段的大小知允。條件(b)僅用來對付較老的撒蟀、原始的主機(jī)。
步驟2中的條件(b)要求發(fā)送方始終監(jiān)視另一方通告的最大窗口大小温鸽,這是一種發(fā)送方猜測對方接收緩存大小的企圖保屯。雖然在連接建立時接收緩存的大小可能會減小,但在實(shí)際中這種情況很少見涤垫。
一個例子
現(xiàn)在我們通過仔細(xì)查看一個詳細(xì)的例子來觀察實(shí)際避免出現(xiàn)糊涂窗口綜合癥的情況姑尺,該例子也包括了堅(jiān)持定時器。我們將在發(fā)送主機(jī)sun上運(yùn)行sock程序蝠猬,并向網(wǎng)絡(luò)寫6個1024字節(jié)的數(shù)據(jù)切蟋。
sun % sock -i -n6 bsdi 7777
但是在主機(jī)bsdi的接收過程中我們加入一些暫停。在第1次讀數(shù)據(jù)前暫停4秒榆芦,之后每次讀之前暫停2秒柄粹。而且,接收方進(jìn)行的是256字節(jié)的讀操作:
bsdi % sock -i -s -P4 -p2 -r256 7777
最初的暫停是為了讓接收緩存被填滿匆绣,迫使發(fā)送方停止發(fā)送驻右。隨后由于接收方從網(wǎng)絡(luò)上進(jìn)行了一些小數(shù)目的讀取,我們預(yù)期能看到接收方采取的避免糊涂窗口綜合癥的措施崎淳。
圖22-2是傳輸6144字節(jié)數(shù)據(jù)的時間系列(我們?nèi)サ袅诉B接建立過程)堪夭。
我們還需要跟蹤在每個時間點(diǎn)上讀取數(shù)據(jù)時應(yīng)用程序的運(yùn)行情況、當(dāng)前正在接收緩存中的數(shù)據(jù)的序號以及接收緩存中可用空間的大小。圖22-3顯示了所發(fā)生的每件事情森爽。
圖22-3中的第1列是每個行為的相對時間點(diǎn)恨豁。那些帶有3位小數(shù)點(diǎn)的時間是從tcpdump的輸出結(jié)果(圖22-2)中得到的,而小數(shù)點(diǎn)部分為99的則是在接收服務(wù)器上產(chǎn)生行為的估計(jì)時間(使這些在接收方的估計(jì)時間包含一秒的99%僅與圖22-2中的報(bào)文段20和22有關(guān)拗秘,它們是我們能夠從tcpdump的輸出結(jié)果中看到的由接收主機(jī)超時引起的僅有的兩個事件圣絮。而在主機(jī)bsdi上觀察到的其他分組,則是由接收到來自發(fā)送方的一個報(bào)文段所引起的雕旨。這同樣是有意義的扮匠,因?yàn)檫@就使我們可以將最初的4秒暫停剛好放置在發(fā)送方發(fā)送第1個數(shù)據(jù)報(bào)文段的時間0前面。這是接收方在連接建立過程中收到它的SYN的ACK之后將要獲得控制權(quán)的大致時間)凡涩。
當(dāng)接收到來自發(fā)送方的數(shù)據(jù)時棒搜,接收方緩存中的數(shù)據(jù)增加,而當(dāng)應(yīng)用進(jìn)程從緩存中讀取數(shù)據(jù)時活箕,數(shù)據(jù)就減少力麸。接下來我們關(guān)注的是接收方發(fā)給發(fā)送方的窗口通告以及這些窗口通告是什么。這樣就可以使我們看到接收方是如何避免糊涂窗口綜合癥的育韩。
前4個數(shù)據(jù)報(bào)文段及其ACK(報(bào)文段1~5)表示發(fā)送方正在填充接收方的緩存克蚂。在那個時刻發(fā)送方停止了發(fā)送,但仍然有更多的數(shù)據(jù)需要發(fā)送筋讨。它將自己的堅(jiān)持定時器置為最小值5分鐘埃叭。
當(dāng)堅(jiān)持定時器時間到時,就發(fā)送出1個字節(jié)的數(shù)據(jù)(報(bào)文段6)悉罕。接收的應(yīng)用進(jìn)程已經(jīng)從接收緩存中讀取了256字節(jié)的數(shù)據(jù)(在時刻3.99)赤屋,因此這個字節(jié)被接受并被確認(rèn)(報(bào)文段7段)。但是通告窗口仍為0壁袄,由于接收方仍然沒有足夠的空間來接收一個滿長度的報(bào)文类早,或者不能騰出緩存空間的一半。這就是接收方的糊涂窗口避免措施嗜逻。
發(fā)送方的堅(jiān)持定時器被復(fù)位涩僻,并在5秒后再次到時(在時刻10.151)。然后又發(fā)送一個字節(jié)并被確認(rèn)(報(bào)文段8和9)栈顷,而接收方的緩存空間還不夠用(1022字節(jié))令哟,使得通告窗口為0。
發(fā)送方的堅(jiān)持定時器在時刻15.151再次時間到妨蛹,又發(fā)送了另一個字節(jié)并被確認(rèn)(報(bào)文段10和11)屏富。這一次由于接收方有1533字節(jié)的有效緩存空間,因此通告了一個非0窗口蛙卤。發(fā)送方立即利用這個窗口發(fā)送了1024字節(jié)的數(shù)據(jù)(報(bào)文段12)狠半。對這1024字節(jié)數(shù)據(jù)的確認(rèn)(報(bào)文段13)通告其窗口為509字節(jié)噩死。這看起來與我們在前面看到的小窗口通告相抵觸。
在這里之所以發(fā)生這種情況神年,是因?yàn)閳?bào)文段11段通告了一個大小為1533字節(jié)的窗口已维,而發(fā)送方只使用了其中的1024字節(jié)。如果在報(bào)文段13中的ACK通告其窗口為0已日,就會違反窗口的右邊沿不能向左邊沿移動而導(dǎo)致窗口收縮的TCP原則(見第20.3節(jié))垛耳。這就是為什么必須通告一個509字節(jié)的窗口的原因。
接下來我們看到發(fā)送方?jīng)]有立即向這個小窗口發(fā)送數(shù)據(jù)飘千。這就是發(fā)送方采取的糊涂窗口避免策略堂鲜。相反,它等待另一個堅(jiān)持定時器在時刻20.151到時間护奈,并在該時刻發(fā)送509字節(jié)的數(shù)據(jù)缔莲。盡管它最終還是發(fā)送了一個長度為509字節(jié)的小數(shù)據(jù)段,但在發(fā)送前它等待了5秒鐘霉旗,看是否會有一個ACK到達(dá)痴奏,以便可以將窗口開得更大。這509字節(jié)的數(shù)據(jù)使得接收緩存僅剩下768字節(jié)的有效空間厌秒,因此接收方通告窗口為0(報(bào)文段15)读拆。
堅(jiān)持定時器在時刻25.151再次到時間,發(fā)送方發(fā)送1個字節(jié)鸵闪,于是接收緩存中有1279字節(jié)的可用空間建椰,這就是在報(bào)文段17所通告的窗口大小。
發(fā)送方只有另外的511個字節(jié)的數(shù)據(jù)需要發(fā)送岛马,因此在收到1279的窗口通告后立刻發(fā)送了這些數(shù)據(jù)(報(bào)文段18)。這個報(bào)文段也帶有FIN標(biāo)志屠列。接收方確認(rèn)數(shù)據(jù)和FIN啦逆,并通告窗口大小為767(見習(xí)題22.2)。
由于發(fā)送應(yīng)用進(jìn)程在執(zhí)行完6個1024字節(jié)的寫操作后發(fā)出關(guān)閉命令笛洛,發(fā)送方的連接從ESTA BLISHED狀態(tài)轉(zhuǎn)變到FIN_WA IT_1狀態(tài)夏志,再到FIN_WA IT_2狀態(tài)(見圖18-12)。它一直處于這個狀態(tài)苛让,直到收到對方的FIN沟蔑。在這個狀態(tài)上沒有設(shè)置定時器(回憶我們在18.6節(jié)結(jié)束時的討論),因?yàn)樗趫?bào)文段18中發(fā)送的FIN被報(bào)文段19確認(rèn)狱杰。這就是為什么我們看到發(fā)送方直到接收到FIN(報(bào)文段21)為止沒有發(fā)送其他任何數(shù)據(jù)的原因瘦材。
接收應(yīng)用進(jìn)程繼續(xù)每隔2秒從接收緩存區(qū)中讀取256個字節(jié)的數(shù)據(jù)。為什么在時刻39.99發(fā)送ACK(報(bào)文段20)呢仿畸?這是因?yàn)閼?yīng)用進(jìn)程在時刻39.99讀取數(shù)據(jù)時食棕,接收緩存中的可用空間已經(jīng)從原來通告的767(報(bào)文段19)變?yōu)?816朗和,這相當(dāng)于接收緩存中增加了額外的2049字節(jié)的空間〔鞠回憶本節(jié)開始講的第1個規(guī)則眶拉,因?yàn)楝F(xiàn)在接收緩存已經(jīng)增加了其空間的一半,因此接收方現(xiàn)在發(fā)送窗口更新憔儿。這意味著每次當(dāng)應(yīng)用進(jìn)程從TCP的接收緩存中讀取數(shù)據(jù)時忆植,接收的TCP將檢查是否需要更新發(fā)送窗口。
應(yīng)用進(jìn)程在時間51.99發(fā)出最后一個讀操作谒臼,然后收到一個文件結(jié)束標(biāo)志朝刊,因?yàn)榫彺嬉呀?jīng)變空。這就導(dǎo)致了最后兩個完成連接終止的報(bào)文段(報(bào)文段21和22)的發(fā)送屋休。
22.4 小結(jié)
在連接的一方需要發(fā)送數(shù)據(jù)但對方已通告窗口大小為0時坞古,就需要設(shè)置TCP的堅(jiān)持定時器。發(fā)送方使用與第21章類似的重傳間隔時間劫樟,不斷地探查已關(guān)閉的窗口痪枫。這個探查過程將一直持續(xù)下去。
當(dāng)運(yùn)行一個例子來觀察堅(jiān)持定時器時叠艳,我們還觀察到了TCP的避免出現(xiàn)糊涂窗口綜合癥的現(xiàn)象奶陈。這就是使TCP避免通告小的窗口大小或發(fā)送小的報(bào)文段。在我們的例子中附较,可以觀察到發(fā)送方和接收方為避免糊涂窗口綜合癥所使用的策略吃粒。