TCP/IP詳解--舉例明白髮送/接收緩衝區、滑動窗口協議之間的關係

https://blog.csdn.net/yusiguyuan/article/details/21439633

一個例子明白髮送緩衝區、接受緩衝區、滑動窗口協議之間的關係。

在上面的幾篇文章中簡單介紹了上述幾個概念在TCP網絡編程中的關係,也對應了幾個基本socket系統調用的幾個行爲,這裏再列舉一個例子,由於對於每一個TCP的SOCKET來說,都有一個發送緩衝區和接受緩衝區與之對應,所以這裏只做單方向交流,不做互動,在recv端不send,在send端不recv。細細揣摩其中的含義。

一、recv端

在監聽套接字上準備accept,在accept結束以後不做什麼操作,直接sleep很久,也就是在recv端並不做接受數據的操作,在sleep結束之後再recv數據。

二、send端

通過查看本系統內核默認的支持的最大發送緩衝區大小,cat/proc/sys/net/ipv4/tcp_wmem,最後一個參數爲發送緩衝區的最大大小。接受緩衝區最大的配置文件在tcp_rmen中。

將套接字設置爲阻塞,一次發送的buffer大於最大發送緩衝區所能容納的數據量,一次send結束,在發送返回後接着答應發送的數據長度

測試結果:

階段一:

         接受端表現:在剛開始發送數據時,接收端處於慢啓動狀態,滑動窗口大小越來愈大,但是由於接收端不處理接受緩衝區內的數據,其滑動窗口越來越小(因爲接受端迴應發送端中的win大小表示接受端還能夠接受多少數據,發送端下次發送的數據大小不能超過迴應中win的大小),最後發送端迴應給接受端的ACK中顯示的win大小爲0,表示接收端不能夠再接受數據。

發送端表現:發送端一直不能返回,如果接受端一直迴應win爲0的情況下,發送端的send就會一直不能返回,這種僵局一直持續到接收端的sleep結束。

原因分析:首先需要明白幾個事實,阻塞式I/O會一直等待,直達這個操作完成;發送端接受到接收端的迴應後才能將發送緩衝區中的數據進行清空。

在接收端不recv,那麼接收端的接受緩衝區內會一直有數據,接受緩衝區滿,導致滑動窗口爲0,導致發送端不能發送數據。但是send操作爲何不能反悔呢?send操作只是將應用緩衝區的數據拷貝到發送緩衝區,但是發送緩衝區的數據並沒有完全得到接收端的ACK迴應,所以暫時不能將發送緩衝區中的數據丟棄,導致發送緩衝區的被填滿,這樣應用層中的數據也就不能拷貝到內核發送緩衝區內,也就會一直阻塞在這裏,直到可以繼續講應用層的數據拷貝到發送緩衝區中,何時觸發這個操作呢?等到發送端迴應win大於0時纔有這樣的操作。

階段二;

         接受端:在sleep結束以後,開始調用recv系統調用。這個時候接受端的滑動窗口又開始大於零。那麼這樣就喚醒了發送端繼續發送數據。

         發送端:發送端接受到接收端win大於0的迴應,這個時候發送端又可以將應用層buffer中的數據拷貝到內核的發送緩衝區中。

原因分析:由於接受端調用recv將內核接受緩衝區的數據拷貝到應用層中,這樣滑動窗口又大於0了所以激發了發送端繼續發送數據,由於發送端可以發送數據了,內核協議棧便將發送緩衝區中的數據發送給接受端,這樣發送緩衝區又有空間了,那麼send操作就可以將應用層的數據拷貝到發送緩衝區了!這樣的操作一直保持到send操作返回,這樣代表着將應用層的數據全部拷貝到發送緩衝區內,但不代表將數據發送給對端。發送給對端成功的標誌是接受到對端的ACK迴應,這個時候發送端纔可以將發送緩衝區的數據丟棄。不丟棄的原因是時刻準備重發丟失/出錯的數據!

Ps: TCP通信爲了保證可靠性,每次發送的數據都需要得到對方的ACK才確認對方收到了(僅保證對方TCP接收緩衝收到 數據了,但不保證對方應用程序取到數據了),這時如果每次發送一次就要停下來等着對方的ACK消息,顯然是一種極大的資源浪費和低下的效率,這時就有了滑動窗口的出現。
發送方的滑動窗口維持着當前發送的幀序號,已發出去幀的計時器,接收方當前的窗口大小(由接收方ACK通知,大體等於接收緩衝大小-未處理的消息包),接收方滑動窗口保存的有已接收的幀信息、期待的下一幀的幀號等,至於滑動窗口的具體工作原理這裏就不說了。
   一 個socket有兩個滑動窗口(一個sendbuf、一個recvbuf),兩個窗口的大小是通過setsockopt函數設置的,現在問題就出在這裏, 通過抓包顯示,設置的窗口大小沒有生效,最後排查發現setsockopt函數是後來加上的,寫到了listen函數的後面,這樣每次accept出的 socket並沒有繼承得到主socket設置的窗口大小,無語啊……
   解決辦法:setsockopt函數提前到listen函數之前,這樣在服務器程序啓動監聽前recvbuf就已經有了,accept後的鏈接得到的就是recvbuf了,啓動程序運行,抓包顯示窗口已經是指定的大小了。

一、TCP的滑動窗口大小實際上就是socket的接收緩衝區大小的字節數

 

二、 對於server端的socket一定要在listen之前設置緩衝區大小,因爲,accept時新產生的socket會繼承監聽socket的緩衝區大 小。對於client端的socket一定要在connet之前設置緩衝區大小,因爲connet時需要進行三次握手過程,會通知對方自己的窗口大小。在 connet之後再設置緩衝區,已經沒有什麼意義。

 

三、由於緩衝區大小在TCP頭部只有16位來表示,所以它的最大值是65536,但是對於一些情況來說需要使用更大的滑動窗口,這時候就要使用擴展的滑動窗口,如光纖高速通信網絡,或者是衛星長連接網絡,需要窗口儘可能的大。這時會使用擴展的32位的滑動窗口大小。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章