- 中文名
- 滑動窗口協議
- 外文名
- Sliding Window Protocol
- 類 型
- 可靠數據傳輸協議
- 層次結構
- 4層
工作原理
操作
發送方操作
接收方操作
現在假設接收器看到以下一系列數據包(均為模8):
0 1 2 3 4 5 6(暫停)0
由於wr = 2,接收方將接受並存儲最終的數據包0(在系列中認為它是數據包8),同時請求重發數據包7。.然而,發送方也不可能接收到任何確認,並且在后一種情況下,接收機將接收錯誤的分組作為分組8。解決方案是發送方限制wt≤6。通過這種限制,接收方在接收到分組6后知道發送方的na≥1,並且因此編號為0的后續分組必須是分組8。如果所有確認丟失,則發送方將不得不在分組5之后停止。
TCP滑動窗口機制

我們可以大概看一下上圖的模型:
首先是AB之間三次握手建立TCP連接。在報文的交互過程中,A將自己的緩沖區大小(窗口大小)3發送給B,B同理,這樣雙方就知道了對端的窗口大小。
A開始發送數據,A連續發送3個單位的數據,因為他知道B的緩沖區大小。在這一波數據發送完后,A就不能再發了,需等待B的確認。
A發送過來的數據逐漸將緩沖區填滿。
這時候緩沖區中的一個報文被進程讀取,緩沖區有了一個空位,於是B向A發送一個ACK,這個報文中指示窗口大小為1。
A收到B發過來的ACK消息,並且知道B將窗口大小調整為1,因此他只發送了一個單位的數據並且等待B的下一個確認報文。
如此反復。
什么是滑動窗口協議?
一圖勝千言,看下面的圖。簡單解釋下,發送和接受方都會維護一個數據幀的序列,這個序列被稱作窗口。發送方的窗口大小由接受方確定,目的在於控制發送速 度,以免接受方的緩存不夠大,而導致溢出,同時控制流量也可以避免網絡擁塞。下面圖中的4,5,6號數據幀已經被發送出去,但是未收到關聯的 ACK,7,8,9幀則是等待發送。可以看出發送端的窗口大小為6,這是由接受端告知的(事實上必須考慮擁塞窗口cwnd,這里暫且考慮 cwnd>rwnd)。此時如果發送端收到4號ACK,則窗口的左邊緣向右收縮,窗口的右邊緣則向右擴展,此時窗口就向前“滑動了”,即數據幀10 也可以被發送。
下面就滑動窗口協議做出更詳細的說明,這里為了簡單起見設定發送方窗口大小為2,接受方大小為1。看下面圖:
一:初始態,發送方沒有幀發出,發送窗口前后沿相重合。接收方0號窗口打開,等待接收0號幀;
二:發送方打開0號窗口,表示已發出0幀但尚確認返回信息。 此時接收窗口狀態不變;
三:發送方打開0、1號窗口,表示0、1號幀均在等待確認之列。至此,發送方打開的窗口數已達規定限度,在未收到新的確認返回幀之 前,發送方將暫停發送新的數據幀。接收窗口此時狀態仍未變;
四:接收方已收到0號幀,0號窗口關閉,1號窗口打開,表示准備接收1號幀。此時發送窗口狀態不 變;
五:發送方收到接收方發來的0號幀確認返回信息,關閉0號窗口,表示從重發表中刪除0號幀。此時接收窗口狀態仍不變
六:發送方繼續發送2號幀,2號窗口 打開,表示2號幀也納入待確認之列。至此,發送方打開的窗口又已達規定限度,在未收到新的確認返回幀之前,發送方將暫停發送新的數據幀,此時接收窗口狀態 仍不變;
七:接收方已收到1號幀,1號窗口關閉,2號窗口打開,表示准備接收2號幀。此時發送窗口狀態不變;
八:發送方收到接收方發來的1號幀收畢的確認信 息,關閉1號窗口,表示從重發表中刪除1號幀。此時接收窗口狀態仍不變。
1比特滑動窗口協議?
上面說的只是滑動窗口協議的理論,實際應用中又有不同。首先就是停等協議(stop-and-wait),這時接受方的窗口和發送方的窗口大小都是1,1 個比特就夠表示了,所以也叫1比特滑動窗口協議。發送方這時自然發送每次只能發送一個,並且必須等待這個數據包的ACK,才能發送下一個。雖然在效率上比 較低,帶寬利用率明顯較低,不過在網絡環境較差,或是帶寬本身很低的情況下,還是適用的。看下面的流程圖:
后退n協議?
停等協議雖然實現簡單,也能較好的適用惡劣的網絡環境,但是顯然效率太低。所以有了后退n協議,這也是滑動窗口協議真正的用處,這里發送的窗口大小為n,接受方的窗口仍然為1。具體看下面的圖,這里假設n=9:
首先發送方一口氣發送10個數據幀,前面兩個幀正確返回了,數據幀2出現了錯誤,這時發送方被迫重新發送2-8這7個幀,接受方也必須丟棄之前接受的3-8這幾個幀。
后退n協議的好處無疑是提高了效率,但是一旦網絡情況糟糕,則會導致大量數據重發,反而不如上面的停等協議,實際上這是很常見的,具體可以參考TCP擁塞控制。
選擇重傳協議?
后退n協議的另外一個問題是,當有錯誤幀出現后,總是要重發該幀之后的所有幀,毫無疑問在網絡不是很好的情況下會進一步惡化網絡狀況,重傳協議便是用來解 決這個問題。原理也很簡單,接收端總會緩存所有收到的幀,當某個幀出現錯誤時,只會要求重傳這一個幀,只有當某個序號后的所有幀都正確收到后,才會一起提 交給高層應用。重傳協議的缺點在於接受端需要更多的緩存。
TCP滑動窗口易錯處
前段時間研究分布式時寫了一個可擴展的服務器組程序,服務器組之間通信時老是達不到想要的性能。后來抓包排查,原來是TCP滑動窗口引起的問題,本來是很基礎的東西,奈何當初沒有太在意,導致錯誤的產生,現在詳細寫出來,忘不太清楚者警惕!
滑動窗口的基本情況我有必要廢話一下。TCP通信為了保證可靠性,每次發送的數據都需要得到對方的ACK才確認對方收到了(僅保證對方TCP接收緩沖收到數據了,但不保證對方應用程序取到數據了),這時如果每次發送一次就要停下來等着對方的ACK消息,顯然是一種極大的資源浪費和低下的效率,這時就有了滑動窗口的出現。
發送方的滑動窗口維持着當前發送的幀序號,已發出去幀的計時器,接收方當前的窗口大小(由接收方ACK通知,大體等於接收緩沖大小-未處理的消息包),接收方滑動窗口保存的有已接收的幀信息、期待的下一幀的幀號等,至於滑動窗口的具體工作原理這里就不說了。
一個socket有兩個滑動窗口(一個sendbuf、一個recvbuf),兩個窗口的大小是通過setsockopt函數設置的,現在問題就出在這里,通過抓包顯示,設置的窗口大小沒有生效,最后排查發現setsockopt函數是后來加上的,寫到了listen函數的后面,這樣每次accept出的socket並沒有繼承得到主socket設置的窗口大小,無語啊……
解決辦法:setsockopt函數提前到listen函數之前,這樣在服務器程序啟動監聽前recvbuf就已經有了,accept后的鏈接得到的就是recvbuf了,啟動程序運行,抓包顯示窗口已經是指定的大小了。
網絡編程其實很簡單,任何人都可以寫出一套自己的服務器框架,但是細節決定成敗,性能的高低有時候就是幾個小細節決定的(當然這里說的這個問題是個編程錯誤,不屬於可優化的細節問題)
前言
你現在的努力,是為了以后有更多的選擇。
在上一篇文章通過“表白”方式,讓我們快速了解網絡七層協議了解了網絡七層協議。
接下來我們要把重心放在網絡傳輸的可靠性上面。一起來看TCP協議,它是如何解決網絡傳輸不可靠的問題。這其中有個很關鍵的部分,就是我們的滑動窗口協議。
從工程學角度上,我們來看一看滑動窗口協議,它到底解決了一個怎樣的問題?
滑動窗口協議:
- TCP協議的使用
- 維持發送方/接收方緩沖區
緩沖區是 用來解決網絡之間數據不可靠的問題,例如丟包,重復包,出錯,亂序
在TCP協議中,發送方和接受方通過各自維護自己的緩沖區。通過商定包的重傳機制等一系列操作,來解決不可靠的問題。
問題一:如何保證次序?
提出問題:在我們滑動窗口協議之前,我們如何來保證發送方與接收方之間,每個包都能被收到。並且是按次序的呢?
發送方發送一個包1,這時候接收方確認包1。發送包2,確認包2。就這樣一直下去,知道把數據完全發送完畢,這樣就結束了。那么就解決了丟包,出錯,亂序等一些情況!同時也存在一些問題。問題:吞吐量非常的低。我們發完包1,一定要等確認包1.我們才能發送第二個包。
問題二:如何提高吞吐量?
提出問題:那么我們就不能先連發幾個包等他一起確認嗎?這樣的話,我們的速度會不會更快,吞吐量更高些呢?
如圖,這個就是我們把兩個包一起發送,然后一起確認。可以看出我們改進的方案比之前的好很多,所花的時間只是一個來回的時間。接下來,我們還有一個問題:改善了吞吐量的問題
問題三:如何實現最優解?
問題:我們每次需要發多少個包過去呢?發送多少包是最優解呢?
我們能不能把第一個和第二個包發過去后,收到第一個確認包就把第三個包發過去呢?而不是去等到第二個包的確認包才去發第三個包。這樣就很自然的產生了我們"滑動窗口"的實現。
在圖中,我們可看出灰色1號2號3號包已經發送完畢,並且已經收到Ack。這些包就已經是過去式。4、5、6、7號包是黃色的,表示已經發送了。但是並沒有收到對方的Ack,所以也不知道接收方有沒有收到。8、9、10號包是綠色的。是我們還沒有發送的。這些綠色也就是我們接下來馬上要發送的包。 可以看出我們的窗口正好是11格。后面的11-16還沒有被讀進內存。要等4號-10號包有接下來的動作后,我們的包才會繼續往下發送。
正常情況
可以看到4號包對方已經被接收到,所以被塗成了灰色。“窗口”就往右移一格,這里只要保證“窗口”是7格的。 我們就把11號包讀進了我們的緩存。進入了“待發送”的狀態。8、9號包已經變成了黃色,表示已經發送出去了。接下來的操作就是一樣的了,確認包后,窗口往后移繼續將未發送的包讀進緩存,把“待發送“狀態的包變為”已發送“。
丟包情況
有可能我們包發過去,對方的Ack丟了。也有可能我們的包並沒有發送過去。從發送方角度看就是我們沒有收到Ack。
發生的情況:一直在等Ack。如果一直等不到的話,我們也會把讀進緩存的待發送的包也一起發過去。但是,這個時候我們的窗口已經發滿了。所以並不能把12號包讀進來,而是始終在等待5號包的Ack。
如果我們這個Ack始終不來怎么辦呢?
超時重發
這時候我們有個解決方法:超時重傳
這里有一點要說明:這個Ack是要按順序的。必須要等到5的Ack收到,才會把6-11的Ack發送過去。這樣就保證了滑動窗口的一個順序。
這時候可以看出5號包已經接受到Ack,后面的6、7、8號包也已經發送過去已Ack。窗口便繼續向后移動。
參考:TCP 滑動窗口協議
參考:滑動窗口協議







