转一篇关于滑动窗口的讲解,挺详细的
?原文地址:http://blog.csdn.net/yusiguyuan/article/details/21439633#1536434-tsina-1-74921-66a1f5d8f89e9ad52626f6f40fdeadaa
?TCP/IP詳解--舉例明白發(fā)送/接收緩沖區(qū)、滑動窗口協(xié)議之間的關(guān)系.
一個例子明白發(fā)送緩沖區(qū)、接受緩沖區(qū)、滑動窗口協(xié)議之間的關(guān)系。
在上面的幾篇文章中簡單介紹了上述幾個概念在TCP網(wǎng)絡編程中的關(guān)系,也對應了幾個基本socket系統(tǒng)調(diào)用的幾個行為,這里再列舉一個例子,由于對于每一個TCP的SOCKET來說,都有一個發(fā)送緩沖區(qū)和接受緩沖區(qū)與之對應,所以這里只做單方向交流,不做互動,在recv端不send,在send端不recv。細細揣摩其中的含義。
一、recv端
在監(jiān)聽套接字上準備accept,在accept結(jié)束以后不做什么操作,直接sleep很久,也就是在recv端并不做接受數(shù)據(jù)的操作,在sleep結(jié)束之后再recv數(shù)據(jù)。
二、send端
通過查看本系統(tǒng)內(nèi)核默認的支持的最大發(fā)送緩沖區(qū)大小,cat/proc/sys/net/ipv4/tcp_wmem,最后一個參數(shù)為發(fā)送緩沖區(qū)的最大大小。接受緩沖區(qū)最大的配置文件在tcp_rmen中。
將套接字設置為阻塞,一次發(fā)送的buffer大于最大發(fā)送緩沖區(qū)所能容納的數(shù)據(jù)量,一次send結(jié)束,在發(fā)送返回后接著答應發(fā)送的數(shù)據(jù)長度
測試結(jié)果:
階段一:
???????? 接受端表現(xiàn):在剛開始發(fā)送數(shù)據(jù)時,接收端處于慢啟動狀態(tài),滑動窗口大小越來愈大,但是由于接收端不處理接受緩沖區(qū)內(nèi)的數(shù)據(jù),其滑動窗口越來越小(因為接受端回應發(fā)送端中的win大小表示接受端還能夠接受多少數(shù)據(jù),發(fā)送端下次發(fā)送的數(shù)據(jù)大小不能超過回應中win的大小),最后發(fā)送端回應給接受端的ACK中顯示的win大小為0,表示接收端不能夠再接受數(shù)據(jù)。
發(fā)送端表現(xiàn):發(fā)送端一直不能返回,如果接受端一直回應win為0的情況下,發(fā)送端的send就會一直不能返回,這種僵局一直持續(xù)到接收端的sleep結(jié)束。
原因分析:首先需要明白幾個事實,阻塞式I/O會一直等待,直達這個操作完成;發(fā)送端接受到接收端的回應后才能將發(fā)送緩沖區(qū)中的數(shù)據(jù)進行清空。
在接收端不recv,那么接收端的接受緩沖區(qū)內(nèi)會一直有數(shù)據(jù),接受緩沖區(qū)滿,導致滑動窗口為0,導致發(fā)送端不能發(fā)送數(shù)據(jù)。但是send操作為何不能反悔呢?send操作只是將應用緩沖區(qū)的數(shù)據(jù)拷貝到發(fā)送緩沖區(qū),但是發(fā)送緩沖區(qū)的數(shù)據(jù)并沒有完全得到接收端的ACK回應,所以暫時不能將發(fā)送緩沖區(qū)中的數(shù)據(jù)丟棄,導致發(fā)送緩沖區(qū)的被填滿,這樣應用層中的數(shù)據(jù)也就不能拷貝到內(nèi)核發(fā)送緩沖區(qū)內(nèi),也就會一直阻塞在這里,直到可以繼續(xù)講應用層的數(shù)據(jù)拷貝到發(fā)送緩沖區(qū)中,何時觸發(fā)這個操作呢?等到發(fā)送端回應win大于0時才有這樣的操作。
階段二;
???????? 接受端:在sleep結(jié)束以后,開始調(diào)用recv系統(tǒng)調(diào)用。這個時候接受端的滑動窗口又開始大于零。那么這樣就喚醒了發(fā)送端繼續(xù)發(fā)送數(shù)據(jù)。
???????? 發(fā)送端:發(fā)送端接受到接收端win大于0的回應,這個時候發(fā)送端又可以將應用層buffer中的數(shù)據(jù)拷貝到內(nèi)核的發(fā)送緩沖區(qū)中。
原因分析:由于接受端調(diào)用recv將內(nèi)核接受緩沖區(qū)的數(shù)據(jù)拷貝到應用層中,這樣滑動窗口又大于0了所以激發(fā)了發(fā)送端繼續(xù)發(fā)送數(shù)據(jù),由于發(fā)送端可以發(fā)送數(shù)據(jù)了,內(nèi)核協(xié)議棧便將發(fā)送緩沖區(qū)中的數(shù)據(jù)發(fā)送給接受端,這樣發(fā)送緩沖區(qū)又有空間了,那么send操作就可以將應用層的數(shù)據(jù)拷貝到發(fā)送緩沖區(qū)了!這樣的操作一直保持到send操作返回,這樣代表著將應用層的數(shù)據(jù)全部拷貝到發(fā)送緩沖區(qū)內(nèi),但不代表將數(shù)據(jù)發(fā)送給對端。發(fā)送給對端成功的標志是接受到對端的ACK回應,這個時候發(fā)送端才可以將發(fā)送緩沖區(qū)的數(shù)據(jù)丟棄。不丟棄的原因是時刻準備重發(fā)丟失/出錯的數(shù)據(jù)!
Ps: TCP通信為了保證可靠性,每次發(fā)送的數(shù)據(jù)都需要得到對方的ACK才確認對方收到了(僅保證對方TCP接收緩沖收到 數(shù)據(jù)了,但不保證對方應用程序取到數(shù)據(jù)了),這時如果每次發(fā)送一次就要停下來等著對方的ACK消息,顯然是一種極大的資源浪費和低下的效率,這時就有了滑動窗口的出現(xiàn)。
發(fā)送方的滑動窗口維持著當前發(fā)送的幀序號,已發(fā)出去幀的計時器,接收方當前的窗口大小(由接收方ACK通知,大體等于接收緩沖大小-未處理的消息包),接收方滑動窗口保存的有已接收的幀信息、期待的下一幀的幀號等,至于滑動窗口的具體工作原理這里就不說了。
???一 個socket有兩個滑動窗口(一個sendbuf、一個recvbuf),兩個窗口的大小是通過setsockopt函數(shù)設置的,現(xiàn)在問題就出在這里, 通過抓包顯示,設置的窗口大小沒有生效,最后排查發(fā)現(xiàn)setsockopt函數(shù)是后來加上的,寫到了listen函數(shù)的后面,這樣每次accept出的 socket并沒有繼承得到主socket設置的窗口大小,無語啊……
? ?解決辦法:setsockopt函數(shù)提前到listen函數(shù)之前,這樣在服務器程序啟動監(jiān)聽前recvbuf就已經(jīng)有了,accept后的鏈接得到的就是recvbuf了,啟動程序運行,抓包顯示窗口已經(jīng)是指定的大小了。
一、TCP的滑動窗口大小實際上就是socket的接收緩沖區(qū)大小的字節(jié)數(shù)
?
二、 對于server端的socket一定要在listen之前設置緩沖區(qū)大小,因為,accept時新產(chǎn)生的socket會繼承監(jiān)聽socket的緩沖區(qū)大 小。對于client端的socket一定要在connet之前設置緩沖區(qū)大小,因為connet時需要進行三次握手過程,會通知對方自己的窗口大小。在 connet之后再設置緩沖區(qū),已經(jīng)沒有什么意義。
?
三、由于緩沖區(qū)大小在TCP頭部只有16位來表示,所以它的最大值是65536,但是對于一些情況來說需要使用更大的滑動窗口,這時候就要使用擴展的滑動窗口,如光纖高速通信網(wǎng)絡,或者是衛(wèi)星長連接網(wǎng)絡,需要窗口盡可能的大。這時會使用擴展的32位的滑動窗口大小。
轉(zhuǎn)載于:https://www.cnblogs.com/daimadebanyungong/p/5300790.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的转一篇关于滑动窗口的讲解,挺详细的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HorizontalScrollView
- 下一篇: 在开发游戏过程中遇到的一些错误(很基础的