20.1 引言
在第15章我們看到TFTP使用了停止等待協(xié)議莉钙。數(shù)據(jù)發(fā)送方在發(fā)送下一個數(shù)據(jù)塊之前需要等待接收對已發(fā)送數(shù)據(jù)的確認。本章我們將介紹TCP所使用的被稱為滑動窗口協(xié)議的另一種形式的流量控制方法础废。該協(xié)議允許發(fā)送方在停止并等待確認前可以連續(xù)發(fā)送多個分組。由于發(fā)送方不必每發(fā)一個分組就停下來等待確認谣膳,因此該協(xié)議可以加速數(shù)據(jù)的傳輸智亮。
我們還將介紹TCP的PUSH標志,該標志在前面的許多例子中都出現(xiàn)過宏怔。此外勾哩,我們還要介紹慢啟動,TCP使用該技術(shù)在一個連接上建立數(shù)據(jù)流举哟,最后介紹成塊數(shù)據(jù)流的吞吐量。
20.2 正常數(shù)據(jù)流
我們以從主機svr4單向傳輸8192個字節(jié)到主機bsdi開始迅矛。在bsdi上運行sock程序作為服務(wù)器:
bsdi % sock -i -s 7777
其中妨猩,標志-i和-s指示程序作為一個“吸收(sink)”服務(wù)器運行(從網(wǎng)絡(luò)上讀取并丟棄數(shù)據(jù)),服務(wù)器端口指明為7777秽褒。相應(yīng)的客戶程序運行為:
svr4 % sock -i -n8 bsdi 7777
該命令指示客戶向網(wǎng)絡(luò)發(fā)送8個1024字節(jié)的數(shù)據(jù)壶硅。圖20-1顯示了這個過程的時間系列。我們在輸出的前3個報文段中顯示了每一端MSS的值销斟。
發(fā)送方首先傳送3個數(shù)據(jù)報文段(4~6)庐椒。下一個報文段(7)僅確認了前兩個數(shù)據(jù)報文段,這可以從其確認序號為2048而不是3073看出來蚂踊。
報文段7的ACK的序號之所以是2048而不是3073是由以下原因造成的:當一個分組到達時约谈,它首先被設(shè)備中斷例程進行處理,然后放置到IP的輸入隊列中犁钟。三個報文段4棱诱、5和6依次到達并按接收順序放到IP的輸入隊列。IP將按同樣順序?qū)⑺鼈兘唤oTCP涝动。當TCP處理報文段4時迈勋,該連接被標記為產(chǎn)生一個經(jīng)受時延的確認。TCP處理下一報文段(5)醋粟,由于TCP現(xiàn)在有兩個未完成的報文段需要確認靡菇,因此產(chǎn)生一個序號為2048的ACK(報文段7),并清除該連接產(chǎn)生經(jīng)受時延的確認標志米愿。TCP處理下一個報文段(6)厦凤,而連接又被標志為產(chǎn)生一個經(jīng)受時延的確認。在報文段9到來之前育苟,由于時延定時器溢出泳唠,因此產(chǎn)生一個序號為3073的ACK(報文段8)。報文段8中的窗口大小為3072宙搬,表明在TCP的接收緩存中還有1024個字節(jié)的數(shù)據(jù)等待被應(yīng)用程序讀取笨腥。
報文段11~16說明了通常使用的“隔一個報文段確認”的策略拓哺。報文段11、12和13到達并被放入IP的接收隊列脖母。當報文段11被處理時士鸥,連接被標記為產(chǎn)生一個經(jīng)受時延的確認。當報文段12被處理時谆级,它們的ACK(報文段14)被產(chǎn)生且連接的經(jīng)受時延的確認標志被清除烤礁。報文段13使得連接再次被標記為產(chǎn)生經(jīng)受時延。但在時延定時器溢出之前肥照,報文段15處理完畢脚仔,因此該確認立刻被發(fā)送。
注意到報文段7舆绎、14和16中的ACK確認了兩個收到的報文段是很重要的鲤脏。使用TCP的滑動窗口協(xié)議時,接收方不必確認每一個收到的分組吕朵。在TCP中猎醇,ACK是累積的—它們表示接收方已經(jīng)正確收到了一直到確認序號減1的所有字節(jié)。在本例中努溃,三個確認的數(shù)據(jù)為2048字節(jié)而兩個確認的數(shù)據(jù)為1024字節(jié)(忽略了連接建立和終止中的確認)硫嘶。
用tcpdump看到的是TCP的動態(tài)活動情況。我們在線路上看到的分組順序依賴于許多無法控制的因素:發(fā)送方TCP的實現(xiàn)梧税、接收方TCP的實現(xiàn)沦疾、接收進程讀取數(shù)據(jù)(依賴于操作系統(tǒng)的調(diào)度)和網(wǎng)絡(luò)的動態(tài)性(如以太網(wǎng)的沖突和退避等)。對這兩個TCP而言第队,沒有一種單一的曹鸠、正確的方法來交換給定數(shù)量的數(shù)據(jù)。
為顯示情況可能怎樣變化斥铺,圖20-2顯示了在同樣兩個主機之間交換同樣數(shù)據(jù)時的另一個時間系列彻桃,它們是在圖20-1所示的幾分鐘之后截獲的。
一些情況發(fā)生了變化晾蜘。這一次接收方?jīng)]有發(fā)送一個序號為3073的ACK邻眷,而是等待并發(fā)送序號為4097的ACK。接收方僅發(fā)送了4個ACK(報文段7剔交、10肆饶、12和15):三個確認了2 0 4 8字節(jié),另一個確認了1024字節(jié)岖常。最后1024字節(jié)數(shù)據(jù)的ACK出現(xiàn)在報文段17中驯镊,它與FIN的ACK一道發(fā)送(比較該圖中的報文段17與圖20-1中的報文段16和18)。
快的發(fā)送方和慢的接收方
圖20-3顯示了另外一個時間系列。這次是從一個快的發(fā)送方(一個Sparc工作站)到一個慢的接收方(配有慢速以太網(wǎng)卡的80386機器)板惑。它的動態(tài)活動情況又有所不同橄镜。
發(fā)送方發(fā)送4個背靠背(back-to-back)的數(shù)據(jù)報文段去填充接收方的窗口,然后停下來等待一個ACK冯乘。接收方發(fā)送ACK(報文段8)洽胶,但通告其窗口大小為0,這說明接收方已收到所有數(shù)據(jù)裆馒,但這些數(shù)據(jù)都在接收方的TCP緩沖區(qū)姊氓,因為應(yīng)用程序還沒有機會讀取這些數(shù)據(jù)。另一個ACK(稱為窗口更新)在17.4ms后發(fā)送喷好,表明接收方現(xiàn)在可以接收另外的4096個字節(jié)的數(shù)據(jù)翔横。雖然這看起來像一個ACK,但由于它并不確認任何新數(shù)據(jù)梗搅,只是用來增加窗口的右邊沿禾唁,因此被稱為窗口更新。
發(fā)送方發(fā)送最后4個報文段(10~13)些膨,再次填充了接收方的窗口。注意到報文段13中包括兩個比特標志:PUSH和FIN钦铺。隨后從接收方傳來另外兩個ACK订雾,它們確認了最后的4096字節(jié)的數(shù)據(jù)(從4097到8192字節(jié))和FIN(標號為8192)。
20.3 滑動窗口
圖20-4用可視化的方法顯示了我們在前一節(jié)觀察到的滑動窗口協(xié)議矛洞。
在這個圖中洼哎,我們將字節(jié)從1至11進行標號。接收方通告的窗口稱為提出的窗口(offered window)沼本,它覆蓋了從第4字節(jié)到第9字節(jié)的區(qū)域噩峦,表明接收方已經(jīng)確認了包括第3字節(jié)在內(nèi)的數(shù)據(jù),且通告窗口大小為6抽兆∈恫梗回顧第17章,我們知道窗口大小是與確認序號相對應(yīng)的辫红。發(fā)送方計算它的可用窗口凭涂,該窗口表明多少數(shù)據(jù)可以立即被發(fā)送。
當接收方確認數(shù)據(jù)后贴妻,這個滑動窗口不時地向右移動切油。窗口兩個邊沿的相對運動增加或減少了窗口的大小。我們使用三個術(shù)語來描述窗口左右邊沿的運動:
1:稱窗口左邊沿向右邊沿靠近為窗口合攏名惩。這種現(xiàn)象發(fā)生在數(shù)據(jù)被發(fā)送和確認時澎胡。
2:當窗口右邊沿向右移動時將允許發(fā)送更多的數(shù)據(jù),我們稱之為窗口張開。這種現(xiàn)象發(fā)生在另一端的接收進程讀取已經(jīng)確認的數(shù)據(jù)并釋放了TCP的接收緩存時攻谁。
3:當右邊沿向左移動時稚伍,我們稱之為窗口收縮。Host Requirements RFC強烈建議不要使用這種方式巢株。但TCP必須能夠在某一端產(chǎn)生這種情況時進行處理槐瑞。第22.3節(jié)給出了這樣的一個例子,一端希望向左移動右邊沿來收縮窗口阁苞,但沒能夠這樣做困檩。
圖20-5表示了這三種情況。因為窗口的左邊沿受另一端發(fā)送的確認序號的控制那槽,因此不可能向左邊移動悼沿。如果接收到一個指示窗口左邊沿向左移動的ACK,則它被認為是一個重復(fù)ACK骚灸,圖20-5窗口邊沿的移動并被丟棄糟趾。
如果左邊沿到達右邊沿,則稱其為一個零窗口甚牲,此時發(fā)送方不能夠發(fā)送任何數(shù)據(jù)义郑。
一個例子
圖20-6顯示了在圖20-1所示的數(shù)據(jù)傳輸過程中滑動窗口協(xié)議的動態(tài)性。
以該圖為例可以總結(jié)如下幾點:
1:發(fā)送方不必發(fā)送一個全窗口大小的數(shù)據(jù)丈钙。
2:來自接收方的一個報文段確認數(shù)據(jù)并把窗口向右邊滑動非驮。這是因為窗口的大小是相對于確認序號的。
3:正如從報文段7到報文段8中變化的那樣雏赦,窗口的大小可以減小劫笙,但是窗口的右邊沿卻不能夠向左移動。
4:接收方在發(fā)送一個ACK前不必等待窗口被填滿星岗。在前面我們看到許多實現(xiàn)每收到兩個報文段就會發(fā)送一個ACK填大。
下面我們可以看到更多的滑動窗口協(xié)議動態(tài)變化的例子。
20.4 窗口大小
由接收方提供的窗口的大小通城伍伲可以由接收進程控制允华,這將影響TCP的性能。
4.2BSD默認設(shè)置發(fā)送和接受緩沖區(qū)的大小為2048個字節(jié)寥掐。在4.3BSD中雙方被增加為4096個字節(jié)例获。正如我們在本書中迄今為止所看到的例子一樣,SunOS 4.1.3曹仗、BSD/386和SVR4仍然使用4096字節(jié)的默認大小榨汤。其他的系統(tǒng),如Solaris 2.2怎茫、4.4BSD和AIX3.2則使用更大的默認緩存大小收壕,如8192或16384等妓灌。
插口API允許進程設(shè)置發(fā)送和接收緩存的大小。接收緩存的大小是該連接上所能夠通告的最大窗口大小。有一些應(yīng)用程序通過修改插口緩存大小來增加性能。
[Mogul 1993]顯示了在改變發(fā)送和接收緩存大锌昵(在單向數(shù)據(jù)流的應(yīng)用中肴熏,如文件傳輸待德,只需改變發(fā)送方的發(fā)送緩存和接收方的接收緩存大小)的情況下,位于以太網(wǎng)上的兩個工作站之間進行文件傳輸時的一些結(jié)果。它表明對以太網(wǎng)而言斧散,默認的4096字節(jié)并不是最理想的大小,將兩個緩存增加到16384個字節(jié)可以增加約40%左右的吞吐量摊聋。在[Papadopoulos和Parulkar 1993]中也有相似的結(jié)果鸡捐。
在20.7節(jié)中,我們將看到在給定通信媒體帶寬和兩端往返時間的情況下麻裁,如何計算最小的緩存大小箍镜。
一個例子
可以使用sock程序來控制這些緩存的大小。我們以如下方式調(diào)用服務(wù)器程序:
bsdi % sock -i -s -R6144 5555
該命令設(shè)置接收緩存為6144個字節(jié)(-R選項)煎源。接著我們在主機sun上啟動客戶程序并使之發(fā)送8192個字節(jié)的數(shù)據(jù):
sun % sock -i -n1 -w8192 bsdi 5555
圖20-7顯示了結(jié)果色迂。
首先注意到的是在報文段2中提供的窗口大小為6144字節(jié)。由于這是一個較大的窗口手销,因此客戶立即連續(xù)發(fā)送了6個報文段(4~9)歇僧,然后停止。報文段10確認了所有的數(shù)據(jù)(從第1到6144字節(jié))原献,但提供的窗口大小卻為2048馏慨,這很可能是接收程序沒有機會讀取多于2048字節(jié)的數(shù)據(jù)埂淮。報文段11和12完成了客戶的數(shù)據(jù)傳輸姑隅,且最后一個報文段帶有FIN標志。
報文段13包含與報文段10相同的確認序號倔撞,但通告了一個更大的窗口大小讲仰。報文段14確認了最后的2048字節(jié)的數(shù)據(jù)和FIN,報文段15和16僅用于通告一個更大的窗口大小痪蝇。報文段17和18完成通常的關(guān)閉過程鄙陡。
20.5 PUSH標志
在每一個TCP例子中,我們都看到了PUSH標志躏啰,但一直沒有介紹它的用途趁矾。發(fā)送方使用該標志通知接收方將所收到的數(shù)據(jù)全部提交給接收進程。這里的數(shù)據(jù)包括與PUSH一起傳送的數(shù)據(jù)以及接收方TCP已經(jīng)為接收進程收到的其他數(shù)據(jù)给僵。
在最初的TCP規(guī)范中毫捣,一般假定編程接口允許發(fā)送進程告訴它的TCP何時設(shè)置PUSH標志详拙。例如,在一個交互程序中蔓同,當客戶發(fā)送一個命令給服務(wù)器時饶辙,它設(shè)置PUSH標志并停下來等待服務(wù)器的響應(yīng)(在習題19.1中我們假定當發(fā)送12字節(jié)的請求時客戶設(shè)置PUSH標志)。通過允許客戶應(yīng)用程序通知其TCP設(shè)置PUSH標志斑粱,客戶進程通知TCP在向服務(wù)器發(fā)送一個報文段時不要因等待額外數(shù)據(jù)而使已提交數(shù)據(jù)在緩存中滯留弃揽。類似地,當服務(wù)器的TCP接收到一個設(shè)置了PUSH標志的報文段時则北,它需要立即將這些數(shù)據(jù)遞交給服務(wù)器進程而不能等待判斷是否還會有額外的數(shù)據(jù)到達矿微。
然而,目前大多數(shù)的API沒有向應(yīng)用程序提供通知其TCP設(shè)置PUSH標志的方法咒锻。的確冷冗,許多實現(xiàn)程序認為PUSH標志已經(jīng)過時,一個好的TCP實現(xiàn)能夠自行決定何時設(shè)置這個標志惑艇。
如果待發(fā)送數(shù)據(jù)將清空發(fā)送緩存蒿辙,則大多數(shù)的源于伯克利的實現(xiàn)能夠自動設(shè)置PUSH標志。這意味著我們能夠觀察到每個應(yīng)用程序?qū)懙臄?shù)據(jù)均被設(shè)置了PUSH標志滨巴,因為數(shù)據(jù)在寫的時候就立即被發(fā)送思灌。
代碼中的注釋表明該算法對那些只有在緩存被填滿或收到一個PUSH標志時才向應(yīng)用程序提交數(shù)據(jù)的TCP實現(xiàn)有效。
使用插口API通知TCP設(shè)置正在接收數(shù)據(jù)的PUSH標志或得到該數(shù)據(jù)是否被設(shè)置PUSH標志的信息是不可能的恭取。
由于源于伯克利的實現(xiàn)一般從不將接收到的數(shù)據(jù)推遲交付給應(yīng)用程序泰偿,因此它們忽略所接收的PUSH標志。
舉例
在圖20-1中我們觀察到所有8個數(shù)據(jù)報文段(46蜈垮、9耗跛、1113和15)的PUSH標志均被置1,這是因為客戶進行了8次1024字節(jié)數(shù)據(jù)的寫操作攒发,并且每次寫操作均清空了發(fā)送緩存调塌。
再次觀察圖20-7,我們預(yù)計報文段12中的PUSH標志被置1惠猿,因為它是最后一個報文段羔砾。為什么發(fā)送方知道有更多的數(shù)據(jù)需要發(fā)送還設(shè)置報文段7中的PUSH標志呢?這是因為雖然我們指定寫的是8192個字節(jié)的數(shù)據(jù)偶妖,但發(fā)送方的發(fā)送緩存卻是4096個字節(jié)姜凄。
值得注意的另外一點是在圖20-7中的第14、15和16這三個連續(xù)的確認報文段趾访。在圖20-3中我們也觀察到了兩個連續(xù)的ACK态秧,但那是因為接收方已經(jīng)通告其窗口為0(使發(fā)送方停止)。當窗口張開時扼鞋,需要發(fā)送另一個窗口非0的ACK來使發(fā)送方重新啟動申鱼】赵可是,在圖20-7中润讥,窗口的大小從來沒有達到過0转锈。然而,當窗口大小增加了2048個字節(jié)的時候楚殿,另一個ACK(報文段15和16)被發(fā)送以通知對方窗口被更新(在報文段15和16中撮慨,這兩個窗口更新是不需要的,因為已經(jīng)收到了對方的FIN脆粥,表明它不會再發(fā)送任何數(shù)據(jù))砌溺。許多TCP實現(xiàn)在窗口大小增加了兩個最大報文段長度(本例中為2048字節(jié),因為MSS為1024字節(jié))或者最大可能窗口的50%(本例中為2048字節(jié)变隔,因為最大窗口大小為4096字節(jié))時發(fā)送這個窗口更新规伐。在第22.3節(jié)詳細考察糊涂窗口綜合癥的時候,我們還會看到這種現(xiàn)象匣缘。
作為PUSH標志的另一個例子猖闪,再次回到圖20-3。我們之所以看到前4個報文段(4~7)的標志被設(shè)置肌厨,是因為它們每一個均使TCP產(chǎn)生了一個報文段并提交給IP層培慌。但是隨后,TCP停下來等待一個確認來移動4096字節(jié)的窗口柑爸。在此期間吵护,TCP又得到了應(yīng)用程序的最后4096個字節(jié)的數(shù)據(jù)。當窗口張開時(報文段9)表鳍,發(fā)送方TCP知道它有4個可立即發(fā)送的報文段馅而,因此它只設(shè)置了最后一個報文段(13)的PUSH標志。
20.6 慢啟動
迄今為止譬圣,在本章所有的例子中瓮恭,發(fā)送方一開始便向網(wǎng)絡(luò)發(fā)送多個報文段,直至達到接收方通告的窗口大小為止胁镐。當發(fā)送方和接收方處于同一個局域網(wǎng)時偎血,這種方式是可以的诸衔。但是如果在發(fā)送方和接收方之間存在多個路由器和速率較慢的鏈路時盯漂,就有可能出現(xiàn)一些問題。一些中間路由器必須緩存分組笨农,并有可能耗盡存儲器的空間就缆。[Jacobson 1988]證明了這種連接方式是如何嚴重降低了TCP連接的吞吐量的。
現(xiàn)在谒亦,TCP需要支持一種被稱為“慢啟動(slow start)”的算法竭宰。該算法通過觀察到新分組進入網(wǎng)絡(luò)的速率應(yīng)該與另一端返回確認的速率相同而進行工作空郊。
慢啟動為發(fā)送方的TCP增加了另一個窗口:擁塞窗口(congestion window),記為cwnd切揭。當與另一個網(wǎng)絡(luò)的主機建立TCP連接時狞甚,擁塞窗口被初始化為1個報文段(即另一端通告的報文段大小)廓旬。每收到一個ACK哼审,擁塞窗口就增加一個報文段(cwnd以字節(jié)為單位,但是慢啟動以報文段大小為單位進行增加)孕豹。發(fā)送方取擁塞窗口與通告窗口中的最小值作為發(fā)送上限涩盾。擁塞窗口是發(fā)送方使用的流量控制,而通告窗口則是接收方使用的流量控制励背。
發(fā)送方開始時發(fā)送一個報文段春霍,然后等待ACK。當收到該ACK時叶眉,擁塞窗口從1增加為2址儒,即可以發(fā)送兩個報文段。當收到這兩個報文段的ACK時衅疙,擁塞窗口就增加為4离福。這是一種指數(shù)增加的關(guān)系。
在某些點上可能達到了互聯(lián)網(wǎng)的容量炼蛤,于是中間路由器開始丟棄分組妖爷。這就通知發(fā)送方它的擁塞窗口開得過大。當我們在下一章討論TCP的超時和重傳機制時理朋,將會看到它們是怎樣對擁塞窗口起作用的⌒跏叮現(xiàn)在,我們來觀察一個實際中的慢啟動嗽上。
一個例子
圖20-8表示的是將從主機sun發(fā)送到主機vangogh.cs.berkeley.edu的數(shù)據(jù)次舌。這些數(shù)據(jù)將通過一個慢的SLIP鏈路,該鏈路是TCP連接上的瓶頸(我們已經(jīng)在時間系列上去掉了連接建立的過程)兽愤。
我們觀察到發(fā)送方發(fā)送一個長度為512字節(jié)的報文段彼念,然后等待ACK。該ACK在716 ms后收到浅萧。這個時間是一個往返時間的指示逐沙。于是擁塞窗口增加了2個報文段,且又發(fā)送了兩個報文段洼畅。當收到報文段5的ACK后吩案,擁塞窗口增加為3。此時盡管可發(fā)送多達3個報文段帝簇,可是在下一個ACK收到之前徘郭,只發(fā)送了2個報文段靠益。
在21.6節(jié)中我們將再次討論慢啟動,并介紹怎樣采用另一種被稱為“擁塞避免”的技術(shù)來作為通常的實現(xiàn)残揉。
20.7 成塊數(shù)據(jù)的吞吐量
讓我們看一看窗口大小胧后、窗口流量控制以及慢啟動對傳輸成塊數(shù)據(jù)的TCP連接的吞吐量的相互作用。
圖20-9顯示了左邊的發(fā)送方和右邊的接收方之間的一個TCP連接上的時間系列抱环,共顯示了16個時間單元绩卤。為簡單起見,本圖只顯示離散的時間單元江醇。每個粗箭頭線的上半部分顯示的是從左到右的攜帶數(shù)據(jù)的報文段濒憋,標記為1,2,3,等等。在粗線箭頭下面表示的是反向傳輸?shù)腁CK陶夜。我們把ACK用細箭頭線表示凛驮,并標注了被確認的報文段號。
在時間0条辟,發(fā)送方發(fā)送了一個報文段黔夭。由于發(fā)送方處于慢啟動中(其擁塞窗口為1個報文段),因此在繼續(xù)發(fā)送以前它必須等待該數(shù)據(jù)段的確認羽嫡。
在時間1,2和3本姥,報文段從左向右移動一個時間單元。在時間4接收方讀取這個報文段并產(chǎn)生確認杭棵。經(jīng)過時間5婚惫、6和7,ACK移動到左邊的發(fā)送方魂爪。我們有了一個8個時間單元的往返時間RTT(Round-Trip Time)先舷。
我們有意把ACK報文段畫得比數(shù)據(jù)報文段小,這是因為它通常只有一個IP首部和一個TCP首部滓侍。這里顯示僅僅是一個單向的數(shù)據(jù)流動蒋川,并且假定ACK的移動速率與數(shù)據(jù)報文段的移動速率相等。實際上并不總是這樣撩笆。
通常發(fā)送一個分組的時間取決于兩個因素:傳播時延(由光的有限速率捺球、傳輸設(shè)備的等待時間等引起)和一個取決于媒體速率(即媒體每秒可傳輸?shù)谋忍財?shù))的發(fā)送時延。對于一個給定的兩個接點之間的通路夕冲,傳播時延一般是固定的氮兵,而發(fā)送時延則取決于分組的大小。在速率較慢的情況下發(fā)送時延起主要作用(例如耘擂,在習題7.2中我們甚至沒有考慮傳播時延)胆剧,而在千兆比特速率下傳播時延則占主要地位(見圖24-6)絮姆。
當發(fā)送方收到ACK后醉冤,在時間8和9發(fā)送兩個報文段(我們標記為2和3)秩霍。此時它的擁塞窗口為2個報文段。這兩個報文段向右傳送到接收方蚁阳,在時間12和13接收方產(chǎn)生兩個ACK铃绒。這兩個返回到發(fā)送方的ACK之間的間隔與報文段之間的間隔一致,被稱為TCP的自計時(self-clocking)行為螺捐。由于接收方只有在數(shù)據(jù)到達時才產(chǎn)生ACK颠悬,因此發(fā)送方接收到的ACK之間的間隔與數(shù)據(jù)到達接收方的間隔是一致的(然而在實際中,返回路徑上的排隊會改變ACK的到達率)定血。
圖20-10表示的是后面16個時間單位赔癌。2個ACK的到達使得擁塞窗口從2個報文段增加為4個,而這4個報文段在時間1619時被發(fā)送澜沟。第1個ACK在時間23到達灾票。4個ACK的到達使得擁塞窗口從4個報文段增加為8個,并在時間2431發(fā)送8個報文段茫虽。
在時間31及其后續(xù)時間刊苍,發(fā)送方和接收方之間的管道(pipe)被填滿。此時不論擁塞窗口和通告窗口是多少濒析,它都不能再容納更多的數(shù)據(jù)正什。每當接收方在某一個時間單位從網(wǎng)絡(luò)上移去一個報文段,發(fā)送方就再發(fā)送一個報文段到網(wǎng)絡(luò)上号杏。但是不管有多少報文段填充了這個管道婴氮,返回路徑上總是具有相同數(shù)目的ACK。這就是連接的理想穩(wěn)定狀態(tài)盾致。
20.7.1 帶寬時延乘積
現(xiàn)在來回答窗口應(yīng)該設(shè)置為多大的問題莹妒。在我們的例子中,作為最大的吞吐量绰上,發(fā)送方在任何時候有8個已發(fā)送的報文段未被確認旨怠。接收方的通告窗口必須不小于這個數(shù)目,因為通告窗口限制了發(fā)送方能夠發(fā)送的段的數(shù)目蜈块。
可以計算通道的容量為:
capacity (bit) = bandwidth (b/s) × round-trip time (s)
一般稱之為帶寬時延乘積鉴腻。這個值依賴于網(wǎng)絡(luò)速度和兩端的RT T,可以有很大的變動百揭。例如爽哎,一條穿越美國(RT T約為60 ms)的T1的電話線路(1544 000 b/s)的帶寬時延乘積為11 580字節(jié)。對于20.4節(jié)中討論的緩存大小而言器一,這個結(jié)果是合理的课锌。但是一條穿越美國的T3電話線路(45 000 000 b/s)的帶寬時延乘積則為337 500字節(jié),這個數(shù)值超過了最大所允許的TCP通告窗口的大小(65535字節(jié))渺贤。在24.4節(jié)我們將討論能夠避免當前TCP限制的新的TCP窗口大小選項雏胃。
T1電話線的1544 000 b/s是原始比特率。由于每193個bit使用1個作為幀同步志鞍,因此實際數(shù)據(jù)率為1536 000 b/s瞭亮。一個T3電話線的原始比特率實際上是44 736 000 b/s,其數(shù)據(jù)率可達到44 210 000 b/s固棚。在討論中我們使用1.544 Mb/s和45 Mb/s统翩。
不論是帶寬還是時延均會影響發(fā)送方和接收方之間通路的容量。在圖20-11中我們顯示了一個增加了一倍的RT T會使通路容量也增加一倍此洲。
在圖20-11底下的說明部分厂汗,通過使用一個較長的RT T,這個管道能夠容納8個報文段而不是4個呜师。
類似地面徽,圖20-12表示了增加一倍的帶寬也可使該管道的容量增加一倍。
在圖20-12的下部匣掸,假定網(wǎng)絡(luò)速率已經(jīng)加倍趟紊,使得我們能夠只使用上面一半的時間來發(fā)送4個報文段。這樣碰酝,該管道的容量再次加倍(假定該圖的上半部分與下半部分中的報文段具有同樣大小霎匈,即具有相同的比特數(shù))。
20.7.2 擁塞
當數(shù)據(jù)到達一個大的管道(如一個快速局域網(wǎng))并向一個較小的管道(如一個較慢的廣域網(wǎng))發(fā)送時便會發(fā)生擁塞送爸。當多個輸入流到達一個路由器铛嘱,而路由器的輸出流小于這些輸入流的總和時也會發(fā)生擁塞。
圖20-13顯示了一個典型的大管道向小管道發(fā)送報文的情況袭厂。之所以說它典型墨吓,是因為大多數(shù)的主機都連接在局域網(wǎng)上,并通過一個路由器與速率相對較低的廣域網(wǎng)相連(我們再次假定圖中上半部分的報文段(9~20)都是相同的纹磺,而圖中下半部分的ACK也都是相同的)帖烘。
在該圖中,我們已經(jīng)標記路由器R1為“瓶頸”橄杨,因為它是擁塞發(fā)生的地方秘症。它從左側(cè)速率較高的局域網(wǎng)接收數(shù)據(jù)并向右側(cè)速率較低的廣域網(wǎng)發(fā)送(通常R1與R3是同樣的路由器,如同R2與R4一樣式矫。但這并不是必需的乡摹,有時也會使用不對稱的路徑)。當路由器R2將所接收到的分組發(fā)送到右側(cè)的局域網(wǎng)時采转,這些分組之間維持與其左側(cè)廣域網(wǎng)上同樣的間隔聪廉,盡管局域網(wǎng)具有更高的帶寬。類似地,返回的確認之間的間隔也與其在路徑中最慢的鏈路上的間隔一致板熊。
在圖20-13中已經(jīng)假定發(fā)送方不使用慢啟動框全,它按照局域網(wǎng)的帶寬盡可能快地發(fā)送編號為1~20的報文段(假定接收方的通告窗口至少為20個報文段)。正如我們看到的那樣邻邮,ACK之間的間隔與在最慢鏈路上的一致竣况。假定瓶頸路由器具有足夠的容納這20個分組的緩存克婶。如果這個不能保證筒严,就會引起路由器丟棄分組。在21.6節(jié)討論避免擁塞時會看到怎樣避免這種情況情萤。
20.8 緊急方式
TCP提供了“緊急方式(urgent mode)”鸭蛙,它使一端可以告訴另一端有些具有某種方式的“緊急數(shù)據(jù)”已經(jīng)放置在普通的數(shù)據(jù)流中。另一端被通知這個緊急數(shù)據(jù)已被放置在普通數(shù)據(jù)流中筋岛,由接收方?jīng)Q定如何處理娶视。
可以通過設(shè)置TCP首部(圖17-2)中的兩個字段來發(fā)出這種從一端到另一端的緊急數(shù)據(jù)已經(jīng)被放置在數(shù)據(jù)流中的通知。URG比特被置1睁宰,并且一個16bit的緊急指針被置為一個正的偏移量肪获,該偏移量必須與TCP首部中的序號字段相加,以便得出緊急數(shù)據(jù)的最后一個字節(jié)的序號柒傻。
仍有許多關(guān)于緊急指針是指向緊急數(shù)據(jù)的最后一個字節(jié)還是指向緊急數(shù)據(jù)最后一個字節(jié)的下一個字節(jié)的爭論孝赫。最初的TCP規(guī)范給出了兩種解釋,但Host RequirementsRFC確定指向最后一個字節(jié)是正確的红符。
然而青柄,問題在于大多數(shù)的實現(xiàn)(包括源自伯克利的實現(xiàn))繼續(xù)使用錯誤的解釋。所有符合Host Requirements RFC的實現(xiàn)都是可兼容的预侯,但很有可能無法與其他大多數(shù)主機正確通信致开。
TCP必須通知接收進程,何時已接收到一個緊急數(shù)據(jù)指針以及何時某個緊急數(shù)據(jù)指針還不在此連接上萎馅,或者緊急指針是否在數(shù)據(jù)流中向前移動双戳。接著接收進程可以讀取數(shù)據(jù)流,并必須能夠被告知何時碰到了緊急數(shù)據(jù)指針糜芳。只要從接收方當前讀取位置到緊急數(shù)據(jù)指針之間有數(shù)據(jù)存在拣技,就認為應(yīng)用程序處于“緊急方式”。在緊急指針通過之后耍目,應(yīng)用程序便轉(zhuǎn)回到正常方式膏斤。
TCP本身對緊急數(shù)據(jù)知之甚少。沒有辦法指明緊急數(shù)據(jù)從數(shù)據(jù)流的何處開始邪驮。TCP通過連接傳送的唯一信息就是緊急方式已經(jīng)開始(TCP首部中的URG比特)和指向緊急數(shù)據(jù)最后一個字節(jié)的指針莫辨。其他的事情留給應(yīng)用程序去處理。
不幸的是,許多實現(xiàn)不正確地稱TCP的緊急方式為帶外數(shù)據(jù)(out-of-band data)沮榜。如果一個應(yīng)用程序確實需要一個獨立的帶外信道盘榨,第二個TCP連接是達到這個目的的最簡單的方法(許多運輸層確實提供許多人認為的那種真正的帶外數(shù)據(jù):使用同一個連接的獨立的邏輯數(shù)據(jù)通道作為正常的數(shù)據(jù)通道。這是TCP所沒有提供的)蟆融。
TCP的緊急方式與帶外數(shù)據(jù)之間的混淆草巡,也是因為主要的編程接口(插口API)將TCP的緊急方式映射為稱為帶外數(shù)據(jù)的插口。
緊急方式有什么作用呢型酥?兩個最常見的例子是Telnet和Rlogin山憨。當交互用戶鍵入中斷鍵時,我們在第26章將看到使用緊急方式來完成這個功能的例子弥喉。另一個例子是FTP郁竟,當交互用戶放棄一個文件的傳輸時,我們將在第27章看到這樣的一個例子由境。
Telnet和Rlogin從服務(wù)器到客戶使用緊急方式是因為在這個方向上的數(shù)據(jù)流很可能要被客戶的TCP停止(也即棚亩,它通告了一個大小為0的窗口)。但是如果服務(wù)器進程進入了緊急方式虏杰,盡管它不能夠發(fā)送任何數(shù)據(jù)讥蟆,服務(wù)器TCP也會立即發(fā)送緊急指針和URG標志。當客戶TCP接收到這個通知時就會通知客戶進程纺阔,于是客戶可以從服務(wù)器讀取其輸入瘸彤、打開窗口并使數(shù)據(jù)流動。
如果在接收方處理第一個緊急指針之前州弟,發(fā)送方多次進入緊急方式會發(fā)生什么情況呢钧栖?在數(shù)據(jù)流中的緊急指針會向前移動,而其在接收方的前一個位置將丟失婆翔。接收方只有一個緊急指針拯杠,每當對方有新的值到達時它將被覆蓋。這意味著如果發(fā)送方進入緊急方式時所寫的內(nèi)容對接收方非常重要啃奴,那么這些字節(jié)數(shù)據(jù)必須被發(fā)送方用某種方式特別標記潭陪。我們將看到Telnet通過在數(shù)據(jù)流中加入一個值為255的字節(jié)作為前綴來標記它所有的命令。
一個例子
讓我們觀察一下即使是在接收方窗口關(guān)閉的情況下最蕾,TCP是如何發(fā)送緊急數(shù)據(jù)的依溯。在主機bsdi上啟動sock程序,并使之在連接建立后和從網(wǎng)絡(luò)讀取前暫停10秒種(通過使用-P選項)瘟则,這將使另一端填滿發(fā)送窗口:
bsdi % sock -i -s -P10 5555
接著我們在主機sun上啟動客戶黎炉,使之使用一個8192字節(jié)的發(fā)送緩存(使用-S選項)并進行6個向網(wǎng)絡(luò)寫1024字節(jié)數(shù)據(jù)的操作(使用-n選項)。還指明-U5選項醋拧,告知它向網(wǎng)絡(luò)寫第5個緩存之前要寫1個字節(jié)的數(shù)據(jù)慷嗜,并進入緊急數(shù)據(jù)方式淀弹。我們指明詳細標志來觀察寫的順序:
我們設(shè)置發(fā)送緩存為8192個字節(jié),以便讓發(fā)送應(yīng)用程序能夠立即寫所有的數(shù)據(jù)庆械。圖20-14顯示了tcpdump輸出的這個交換過程的結(jié)果(刪去了連接建立的過程)薇溃。第1~5行表示發(fā)送方用4個1024字節(jié)的報文段去填充接收方的窗口。然后由于接收方的窗口被填滿(第4行的ACK確認了數(shù)據(jù)缭乘,但并沒有移動窗口的右邊沿)沐序,所以發(fā)送方停止發(fā)送。
在寫了第4個正常數(shù)據(jù)之后堕绩,應(yīng)用進程寫了1個字節(jié)并進入緊急方式策幼。第6行是該應(yīng)用進程寫的結(jié)果,緊急指針被設(shè)置為4098逛尚。盡管發(fā)送方不能發(fā)送任何數(shù)據(jù)垄惧,但緊急指針和URG標志一起被發(fā)送刁愿。
5個這樣的ACK在13 ms內(nèi)被發(fā)送(第6~10行)绰寞。第1個ACK在應(yīng)用進程寫1個字節(jié)并進入緊急方式時被發(fā)送,后面兩個在應(yīng)用進程寫最后兩個1024字節(jié)的數(shù)據(jù)時被發(fā)送(盡管TCP不能發(fā)送這2048個字節(jié)的數(shù)據(jù)铣口,可每次當應(yīng)用程序執(zhí)行寫操作的時候滤钱,TCP的輸出功能被調(diào)用。當TCP看到正處于緊急方式時脑题,它會發(fā)送其他的緊急通知)件缸。第4個ACK在應(yīng)用進程關(guān)閉其TCP連接時被發(fā)送(TCP的輸出功能再次被調(diào)用)。發(fā)送應(yīng)用程序在啟動幾毫秒后終止—在接收方應(yīng)用進程已經(jīng)發(fā)出其第一個寫操作之前叔遂。TCP將所有的數(shù)據(jù)進行排隊他炊,并在可能時發(fā)送出去(這就是為何指明發(fā)送緩存為8192字節(jié)的原因,因此只有這樣才能夠把所有的數(shù)據(jù)都放置在緩存中)已艰。第5個ACK很可能是在接收第4行的ACK時產(chǎn)生的痊末。發(fā)送TCP很可能在這個ACK到達前便已將其第4個報文段放入隊列以便輸出(第5行)。另一端接收到這個ACK也會引起TCP輸出例程被調(diào)用哩掺。
接著凿叠,接收方確認最后的1024字節(jié)的數(shù)據(jù)(第11行),但同時通告窗口為0嚼吞。發(fā)送方用一個包含緊急通知的報文段進行了響應(yīng)盒件。
在第13行,當應(yīng)用進程被喚醒舱禽、并從接收緩存讀取一些數(shù)據(jù)時炒刁,接收方通告窗口為2048字節(jié)。于是后面又發(fā)送了兩個1024字節(jié)的報文段(第14和15行)誊稚。其中翔始,由于緊急指針在第1個報文段的范圍內(nèi)飒筑,因此這個報文段被設(shè)置了緊急通知標志,而第2個報文段則關(guān)閉了該標志绽昏。
當接收方再次打開窗口(第16行)時协屡,發(fā)送方傳輸最后的數(shù)據(jù)(序號為6145)并發(fā)起正常的連接關(guān)閉。
圖20-15顯示了發(fā)送的6145個字節(jié)數(shù)據(jù)的序號全谤》粝可以看到當進入緊急方式時所發(fā)送的字節(jié)的序號是4097,但在圖20-14中緊急指針指向4098认然,這證明了該實現(xiàn)(SunOS 4.1.3)將緊急指針設(shè)置為緊急數(shù)據(jù)最后字節(jié)的下一個字節(jié)补憾。
該圖還可以讓我們觀察TCP是如何對應(yīng)用進程寫的數(shù)據(jù)進行重新分組化的卷员。當進入緊急方式時待輸出的1個字節(jié)是與在緩存中的后面1023個字節(jié)一同發(fā)送的盈匾。下一個報文段也包含1024字節(jié)的數(shù)據(jù),而最后一個報文段則只包含一個字節(jié)毕骡。
20.9 小結(jié)
正如我們在本章一開始時講的那樣削饵,沒有一種單一的方法可以使用TCP進行成塊數(shù)據(jù)的交換。這是一個依賴于許多因素的動態(tài)處理過程未巫,有些因素我們可以控制(如發(fā)送和接收緩存的大辛恕),而另一些我們則沒有辦法控制(如網(wǎng)絡(luò)擁塞叙凡、與實現(xiàn)有關(guān)的特性等)劈伴。在本章,我們已經(jīng)考察了許多TCP的傳輸過程握爷,介紹了所有我們能夠看到的特點和算法跛璧。
進行成塊數(shù)據(jù)有效傳輸?shù)淖钪匾姆椒ㄊ荰CP的滑動窗口協(xié)議。我們考察了TCP為使發(fā)送方和接收方之間的管道充滿來獲得最可能快的傳輸速度而采用的方法新啼。我們用帶寬時延乘積衡量管道的容量追城,并分析了該乘積與窗口大小之間的關(guān)系。在24.8節(jié)介紹TCP性能的時候?qū)⒃俅紊婕斑@個概念师抄。
我們還介紹了TCP的PUSH標志漓柑,因為在跟蹤結(jié)果中總是觀察到它,但我們無法對它的設(shè)置與否進行控制叨吮。本章最后一個主題是TCP的緊急數(shù)據(jù)辆布,人們常常錯誤地稱其為“帶外數(shù)據(jù)”。TCP的緊急方式只是一個從發(fā)送方到接收方的通知茶鉴,該通知告訴接收方緊急數(shù)據(jù)已被發(fā)送锋玲,并提供該數(shù)據(jù)最后一個字節(jié)的序號。應(yīng)用程序使用的有關(guān)緊急數(shù)據(jù)部分的編程接口常常都不是最佳的涵叮,從而導致更多的混亂惭蹂。