計算機網路 TCP 滑動視窗協議 詳解
滑動視窗機制解析:
1.視窗機制
滑動視窗協議的基本原理就是在任意時刻,傳送方都維持了一個連續的允許傳送的幀的序號,稱為傳送視窗;同時,接收方也維持了一個連續的允許接收的幀的序號,稱為接收視窗。傳送視窗和接收視窗的序號的上下界不一定要一樣,甚至大小也可以不同。不同的滑動視窗協議視窗大小一般不同。傳送方視窗內的序列號代表了那些已經被髮送,但是還沒有被確認的幀,或者是那些可以被髮送的幀。下面舉一個例子(假設傳送視窗尺寸為2,接收視窗尺寸為1):
若從滑動視窗的觀點來統一看待1位元滑動視窗、後退n及選擇重傳三種協議,它們的差別僅在於各自視窗尺寸的大小不同而已。1位元滑動視窗協議:傳送視窗=1,接收視窗=1;後退n協議:發視窗>1,接收視窗>1;選擇重傳協議:傳送視窗>1,接收視窗>1。
2. 1位元滑動視窗協議
當傳送視窗和接收視窗的大小固定為1時,滑動視窗協議退化為停等協議(stop-and-wait)。該協議規定傳送方每傳送一幀後就要停下來,等待接收方已正確接收的確認(acknowledgement)返回後才能繼續傳送下一幀。由於接收方需要判斷接收到的幀是新發的幀還是重新發送的幀,因此傳送方要為每一個幀加一個序號。由於停等協議規定只有一幀完全傳送成功後才能傳送新的幀,因而只用一位元來編號就夠了。其傳送方和接收方執行的流程圖如圖所示。
下面是1位元滑動視窗協議不執行時的通訊過程,三元組( i , j , k )中 i 表示傳送端(可以是A也可是B)所傳送訊息的編號,j 表示傳送端上一次從對方那邊接受到了的編號,k 表示資料。下面是滑動視窗協議的兩種執行過程:
3. 後退n協議
由於停等協議要為每一個幀進行確認後才繼續傳送下一幀,大大降低了通道利用率,因此又提出了後退n協議。後退n協議中,傳送方在發完一個數據幀後,不停下來等待應答幀,而是連續傳送若干個資料幀,即使在連續傳送過程中收到了接收方發來的應答幀,也可以繼續傳送。且傳送方在每傳送完一個數據幀時都要設定超時定時器。只要在所設定的超時時間內仍收到確認幀,就要重發相應的資料幀。如:當傳送方傳送了N個幀後,若發現該N幀的前一個幀在計時器超時後仍未返回其確認資訊,則該幀被判為出錯或丟失,此時傳送方就不得不重新發送出錯幀及其後的N幀。
從這裡不難看出,後退n協議一方面因連續傳送資料幀而提高了效率,但另一方面,在重傳時又必須把原來已正確傳送過的資料幀進行重傳(僅因這些資料幀之前有一個數據幀出了錯),這種做法又使傳送效率降低。由此可見,若傳輸通道的傳輸質量很差因而誤位元速率較大時,連續測協議不一定優於停止等待協議。此協議中的傳送視窗的大小為k,接收視窗仍是1。
4. 選擇重傳協議
在後退n協議中,接收方若發現錯誤幀就不再接收後續的幀,即使是正確到達的幀,這顯然是一種浪費。另一種效率更高的策略是當接收方發現某幀出錯後,其後繼續送來的正確的幀雖然不能立即遞交給接收方的高層,但接收方仍可收下來,存放在一個緩衝區中,同時要求傳送方重新傳送出錯的那一幀。一旦收到重新傳來的幀後,就可以原已存於緩衝區中的其餘幀一併按正確的順序遞交高層。這種方法稱為選擇重發(SELECTICE REPEAT),其工作過程如圖所示。顯然,選擇重發減少了浪費,但要求接收方有足夠大的緩衝區空間。
滑動視窗實現:
注意,在這個例子中,接收方可以在幀7剛一到達時就為幀6傳送一個認幀N A K(negative acknowl edgment)。然而,由於傳送方的超時機制足以發現這種情況,傳送N A K反而為傳送方增加了複雜性,因此不必這樣做。正如我們已提到的,當幀7和8到達時為幀5傳送一個額外的A C K是合理的;在某些情況下,傳送方可以使用重複的A C K作為一個幀丟失的線索。這兩種方法都允許早期的分組丟失檢測,有助於改進效能。
關於這個方案的另一個變種是使用選擇確認(selective acknowledgements)。即,接收方能夠準確地確認那些已收到的幀,而不只是確認按順序收到最高序號的幀。因此,在上例中,接收方能夠確認幀7、8的接收。如果給傳送方更多的資訊,就能使其較容易地保持管道滿載,但增加了實現的複雜性。 傳送視窗大小是根據一段給定時間內鏈路上有多少待確認的幀來選擇的;對於一個給定的延遲與頻寬的乘積,S W S是容易計算的。另一方面,接收方可以將RW S設定為任何想要的值。通常的兩種設定是:RW S= 1,表示接收方不儲存任何錯序到達的幀; RW S=S W S,表示接收方能夠快取傳送方傳輸的任何幀。由於錯序到達的幀的數目不可能超過S W S個,所以設定RWS >S W S沒有意義。
2. 有限順序號和滑動視窗 現在我們再來討論演算法中做過的一個簡化,即假設序號是可以無限增大的。當然,實際上是在一個有限的頭部欄位中說明一個幀的序號。例如,一個3位元欄位意味著有8個可用序號0 ~ 7。因此序號必須可重用,或者說序號能迴繞。這就帶來了一個問題:要能夠區別同一序號的不同次傳送例項,這意味著可用序號的數目必須大於所允許的待確認幀的數目。例如,停止等待演算法允許一次有1個待確認幀,並有2個不同的序號。
假設序號空間中的序號數比待確認的幀數大1,即S W S ≤ M A a x S e q N u m -1 ,其中M a x Seq N u m 是可用序號數。這就夠了嗎?答案取決於RW S 。如果RW S = 1,那麼MaxSeqNum≥SWS+1是足夠了。如果RW S等於S W S,那麼有一個只比傳送視窗尺寸大1的M a x S e q N u m是不夠的。為看清這一點,考慮有8個序號0 ~ 7的情況,並且S W S = RW S = 7。假設傳送方傳輸幀0 ~ 6,並且接收方成功接收,但A C K丟失。接收方現在希望接收幀7,0 ~ 5,但傳送方超時,然後傳送幀0 ~ 6。不幸的是,接收方期待的是第二次的幀0 ~ 5,得到的卻是第一次的幀0 ~ 5。這正是我們想避免的情況。
結果是,當RW S = S W S時,傳送視窗的大小不能大於可用序號數的一半,或更準確地說,SWS<(Maxseqnum+1)/2直觀地,這說明滑動視窗協議是在序號空間的兩半之間變換,就像停止等待協議的序號是在0和1之間變換一樣。唯一的區別是,它在序號空間的兩半之間連續滑動而不是離散的變換。
注意,這條規則是特別針對RW S = S W S的。我們把確定適用於RW S和S W S的任意值的更一般的規則留做一個練習。還要注意,視窗的大小和序號空間之間的關係依賴於一個很明顯以至於容易被忽略的假設,即幀在傳輸中不重新排序。這在直連的點到點鏈路上不能發生,因為在傳輸過程中一個幀不可能趕上另一個幀。然而,我們將在第5章看到用在一個不同環境中的滑動視窗演算法,並且需要設計另一條規則。 3. 滑動視窗的實現 下面的例程說明我們如何實現滑動視窗演算法的傳送和接收的兩個方面。該例程取自一個正在使用的協議,稱為滑動視窗協議S W P(Sliding Window Pro t o c o l)。為了不涉及協議圖中的鄰近協議,我們用H L P(高層協議)表示S W P上層的協議,用L I N K(鏈路層協議)表示S W P下層的協議。我們先定義一對資料結構。首先,幀頭部非常簡單:它包含一個序號( S e q N u m)和一個確認號( A c k N u m)。它還包含一個標誌( F l a g s)欄位,表明幀是一個A C K幀還是攜帶資料的幀。 其次,滑動視窗演算法的狀態有如下結構。對於協議傳送方,該狀態包括如上所述的變數L A R和L F S,以及一個存放已發出但尚未確認的幀的佇列( s e n d Q)。傳送方狀態還包含一個計數訊號量( counting semaphore),稱為s e n d Wi n d o w N o t F u l l。下面我們將會看到如何使用它,但一般來說,訊號量是一個支援s e m Wa i t和s e m S i g n a l操作的同步原語。每次呼叫S e m S i g n a l,訊號量加1,每次呼叫S e m Wa i t,訊號量減1。如果訊號量減小,導致它的值小於0,那麼呼叫程序阻塞(掛起)。一旦執行了足夠的s e m S i g n a l操作而使訊號量的值增大到大於0,在呼叫s e m Wa i t的過程中阻塞的程序就允許被恢復。
對於協議的接收方,如前所述,該狀態包含變數L F R ,加上一個存放已收到的錯序幀的佇列(r e c v Q)。最後,雖然未顯示,傳送方和接收方的滑動視窗的大小分別由常量S W S和RW S表示。 S W P的傳送方是由s e n d S W P過程實現的。這個例程很簡單。首先, s e m Wa i t使這個程序在一個訊號量上阻塞,直到它可以發另一幀。一旦允許繼續, s e n d S W P設定幀頭部中的順序號,將此幀的拷貝儲存在傳送佇列( s e n d Q)中,排程一個超時事件以便處理幀未被確認的情況,並將幀發給低層協議。 值得注意的一個細節是剛好在呼叫m s g A d d H d r之前呼叫s t o r e _ s w p _ h d r。該例程將存有S W P頭部的C語言結構( s t a t e - > h d r)轉化為能夠安全放在訊息前面的位元組串( h b u f)。該例程(未給出)必須將頭部中的每一個整數字段轉化為網路位元組順序,並且去掉編譯程式加入C語言結構中的任意填充。7 . 1節將詳細討論位元組順序的問題,但現在,假設該例程將多字整數中最高有效位放在最高地址位元組就足夠了。
這個例程的另一個複雜性是使用s e m Wa i t 和s e n dW i n d o w N o t F u l l 訊號量。S e n dWi n d o w N o t F u l l被初始化為傳送方滑動視窗的大小S W S(未給出這一初始化)。傳送方每傳輸一幀, s e m Wa i t操作將這個數減1,如果減小到0,則阻塞傳送方程序。每收到一個A C K,在d e l i v e r S W P中呼叫s e m S i g n a l操作(見下面)將此數加1,從而啟用正在等待的傳送方程序。
在繼續介紹S W P的接收方之前,需要調整一個看上去不一致的地方。一方面,我們說過,高層協議通過呼叫s e n d操作來請求低層協議的服務,所以我們就希望通過S W P傳送訊息的協議能夠呼叫s e n d(S W P, p a c k e t)。另一方面,用來實現S W P的傳送操作的過程叫做s e n d S W P,並且它的第一個引數是一個狀態變數( S w p S t a t e)。結果怎樣呢?答案是,作業系統提供了粘結程式碼將對s
e n d的一般呼叫轉化為對s e n d S W P的特定協議呼叫的粘結程式碼。這個粘結程式碼將s e n d的第一個引數(協議變數S W P)對映為一個指向s e n d S W P的函式指標和一個指向S W P工作時所需的協議狀態的指標。我們之所以通過一般函式呼叫使高層協議間接呼叫特定協議函式,是因為我們想限制高層協議中對低層協議編碼的資訊量。這使得將來能夠比較容易地改變協議圖的配置。現在來看d e l i v e r操作的S W P的特定協議實現,它在過程d e l i v e r S W P中實現。這個例程實際上處理兩種不同型別的輸入訊息:本結點已發出幀的A
C K和到達這個結點的資料幀。在某種意義上,這個例程的ACK部分是與send SWP中所給演算法的傳送方相對應的。通過檢驗頭部的F l a g s欄位可以確定輸入的訊息是ACK還是一個數據幀。注意,這種特殊的實現不支援資料幀中捎帶A C K。當輸入幀是一個ACK時,delive rSWP僅僅在傳送佇列(send Q)中找到與此ACK相應的位置(slot),取消超時事件,並且釋放儲存在那一位置的幀。由於A C K可能是累積的,所以這項工作實際上是在一個迴圈中進行的。對於這種情況值得注意的另一個問題是子例程swp
In Wind o w的呼叫。這個子例程在下面給出,它確保被確認幀的序號是在傳送方當前希望收到的A C K的範圍之內。
當輸入幀包含資料時, d e l i v e r S W P首先呼叫m s g S t r i p H d r和l o a d _ s w p _ h d r以便從幀中提取頭部。例程l o a d _ s w p _ h d r對應著前面討論的s t o r e _ s w p _ h d r,它將一個位元組串轉化為容納S W P頭部的C語言資料結構。然後d e l i v e r S W P呼叫s w p I n Wi n d o w以確保幀序號在期望的序號範圍內。如果是這樣,例程在已收到的連續的幀的集合上迴圈,並通過呼叫d
e l i v e r H L P例程將它們傳給上層協議。它也要向傳送方傳送累積的A C K,但卻是通過在接收佇列上迴圈來實現的(它沒有使用本節前面給出的s e q N u m To A c k變數)。
滑動視窗演算法的第三個功能是,它有時支援流量控制(f l o w c o n t ro l),它是一種接收方能夠控制傳送方使其降低速度的反饋機制。這種機制用於抑制傳送方傳送速度過快,即抑制傳輸比接收方所能處理的更多的資料。這通常通過擴充套件滑動視窗協議完成,使接收方不僅確認收到的幀,而且通知傳送方它還可接收多少幀。可接收的幀數對應著接收方空閒的緩衝區數。在按序傳遞的情況下,在將流量控制併入滑動視窗協議之前,我們應該確信流量控制在鏈路層是必要的。
尚未討論的一個重要概念是系統設計原理,我們稱其為相關性分離(separation of concerns)。即,你必須小心區別有時交織在一種機制中的不同功能,並且你必須確定每一個功能是必要的,而且是被最有效的方式支援的。在這種特定的情況下,可靠傳輸、按序傳輸和流量控制有時組合在一個滑動視窗協議裡,我們應該問問自己,在鏈路層這樣做是否正確。帶著這樣的疑問,我們將在第3章(說明X. 2 5網如何用它實現跳到跳的流量控制)和第5章(描述T C P如何用它實現可靠的位元組流通道)重新考慮滑動視窗演算法。