1. 程式人生 > >傳送緩衝區、接收緩衝區、滑動視窗協議之間的關係

傳送緩衝區、接收緩衝區、滑動視窗協議之間的關係

大家知道,TCP採用的是全雙工的工作模式,對每一個TCP的SOCKET來說,都有一個傳送緩衝區和接收緩衝區與之對應,TCP的流量(擁塞)控制便是依賴於這兩個獨立的buffer滑動視窗協議之間。我們可以通過一個簡單實驗體會三者之間關係。

一、recv端

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

二、send端
將套接字設定為阻塞,一次傳送的buffer大於傳送緩衝區所能容納的資料量。
通過檢視系統核心預設的支援的傳送緩衝區大小,cat/proc/sys/net/ipv4/tcp_wmem,最後一個引數為傳送緩衝區的最大值。接收緩衝區的配置檔案在tcp_rmen中。

測試結果:
通過抓包可以發現:

階段一:

接收端表現:在剛開始傳送資料時,傳送端處於慢啟動狀態,隨著不斷收到ack,擁塞視窗大小越來愈大,傳送資料量又開始越來越小,但是不一會,由於接收端不處理接收緩衝區內的資料,其滑動視窗越來越小(因為接收端迴應傳送端中的win大小表示接收端還能夠接收多少資料,傳送端下次傳送的資料大小不能超過迴應中win的大小),傳送資料量又開始越來越小,最後接收端迴應給傳送端的ACK中顯示的win大小為0,表示接收端不能夠再接收資料。傳送停止。

傳送端表現:傳送端一直不能返回,如果接收端一直迴應win為0的情況下,傳送端的send就會一直不能返回。

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

在接收端不recv,那麼接收端的接收緩衝區內會一直有資料,接收緩衝區滿,導致滑動視窗為0,導致傳送端不能傳送資料。但是send操作為何不能返回呢?send操作只是將應用緩衝區的資料拷貝到傳送緩衝區,但是傳送緩衝區的資料並沒有完全得到接收端的ACK迴應,所以暫時不能將傳送緩衝區中的資料丟棄,導致傳送緩衝區的被填滿,這樣應用層中的資料也就不能拷貝到核心傳送緩衝區內,也就會一直阻塞在這裡,直到可以繼續將應用層的資料拷貝到傳送緩衝區中,何時觸發這個操作呢?等到傳送端迴應win大於0時才有這樣的操作。

階段二;

     接收端:在sleep結束以後,開始呼叫recv系統呼叫。這個時候接收端的滑動視窗又開始大於零。那麼這樣就喚醒了傳送端繼續傳送資料。

     傳送端:傳送端接收到接收端win大於0的迴應,這個時候傳送端又可以將應用層buffer中的資料拷貝到核心的傳送緩衝區中。

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


開發中的注意事項:
一個socket有兩個滑動視窗(一個sendbuf、一個recvbuf),兩個視窗的大小是可以通過setsockopt函式設定的。

一、TCP的滑動視窗大小實際上就是socket的接收緩衝區大小的位元組數

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