recv什么时候返回

原文 : http://blog.csdn.net/zlzlei/article/details/7689409

以前老在网上找别人说recv什么时候返回,要么说的很笼统,要么完全觉得不靠谱,最近还是自己做个试验分析一下吧:

测试1. 
每次发送大小:1024
每次接收大小:32
结果:pack1
每send发送一个包,包中数据大小1024,带PUSH标志
每次接收满32后recv函数返回。


测试2.
每次发送大小:1024
每次接收大小:2048
结果:pack2
每send发送一个包,包中数据大小1024,带PUSH标志
每次接收满1024后recv函数返回。


测试3. 
每次发送大小:20480
每次接收大小:10240
结果:pack3
每send发送两个包,包中数据大小为16384(TCP分片大小,建立连接时商定)与4096,仅最后一个包带PUSH标志
第一次接收满10240后recv函数返回
第二次接收6144后recv函数返回(6144+10240=16384)
第三次接收4096后recv函数返回
Recv >>> [1]10240:10240
Recv >>> [2]10240:6144
Recv >>> [3]10240:4096
Recv >>> [4]10240:10240
Recv >>> [5]10240:6144
Recv >>> [6]10240:4096




测试4. 
每次发送大小:20480
每次接收大小:20480
结果:pack4
每send发送两个包,包中数据大小为16384(TCP分片大小,建立连接时商定)与4096,仅最后一个包带PUSH标志
第一次接收16384后recv函数返回
第二次接收4096后recv函数返回
Recv >>> [1]20480:16384
Recv >>> [2]20480:4096
Recv >>> [3]20480:16384
Recv >>> [4]20480:4096


测试5.
每次发送大小:400000
每次接收大小:10240
结果:pack5
发送时除最后一个包不够意外,数据都是按照16384的包大小发送,发送过程中经常性接收窗口满。
接收过程初期按照16384包大小接收,后期无规律:
Recv >>> [1]10240:10240
Recv >>> [2]10240:6144 //A//满16384
Recv >>> [3]10240:10240
Recv >>> [4]10240:6144 //B//满16384,从开始到此满32768(滑动窗口总大小)
Recv >>> [5]10240:10240
Recv >>> [6]10240:10240
Recv >>> [7]10240:10240
Recv >>> [8]10240:2048 //C//从开始到此处满65536(两个滑动窗口总大小)
Recv >>> [9]10240:10240
Recv >>> [10]10240:6144 //D//满16384
Recv >>> [11]10240:10240
Recv >>> [12]10240:6144 //E//满16384
Recv >>> [13]10240:10240 //F//从此往后均能收满整个buffer
Recv >>> [14]10240:10240
。。。。。。都是10240:10240
Recv >>> [40]10240:10240
Recv >>> [41]10240:8192 //E//
Recv >>> [42]10240:6784


测试6.
每次发送大小:400000
每次接收大小:10000
结果:pack6
发送时除最后一个包不够以外,数据都是按照16384的包大小发送,发送过程中经常性接收窗口满。
接收过程初期按照16384包大小接收,后期无规律:
Recv >>> [1]10000:10000
Recv >>> [2]10000:6384
Recv >>> [3]10000:10000
Recv >>> [4]10000:6384
Recv >>> [5]10000:10000
Recv >>> [6]10000:10000
Recv >>> [7]10000:10000
Recv >>> [8]10000:2768
Recv >>> [9]10000:10000
Recv >>> [10]10000:6384
Recv >>> [11]10000:10000
Recv >>> [12]10000:6384
Recv >>> [13]10000:10000
。。。。。。都是10000:10000
Recv >>> [41]10000:10000
Recv >>> [42]10000:4912
Recv >>> [43]10000:6784


测试7.
每次发送大小:32,发10000次
每次接收大小:102400
结果:pack7
发送过程中大部分包都以32大小发送,但也有部分包被合在了一起发送,发送的每个包都带PUSH标志
接收过程中大部分包都以32大小接收,但也有接收大于32的时候,并且与发送大于32的包并无任何关系。
Recv >>> [1]102400:32
Recv >>> [2]102400:32
Recv >>> [3]102400:32
。。。。。。都是102400:32
Recv >>> [92]102400:32
Recv >>> [93]102400:49184
Recv >>> [94]102400:32
。。。。。。都是102400:32
Recv >>> [151]102400:32
Recv >>> [152]102400:32
Recv >>> [153]102400:22720
Recv >>> [154]102400:32
。。。。。。都是102400:32
Recv >>> [268]102400:32
Recv >>> [271]102400:47840
Recv >>> [272]102400:32
。。。。。。都是102400:32
Recv >>> [397]102400:32
Recv >>> [398]102400:32
Recv >>> [399]102400:59776
Recv >>> [400]102400:32
。。。。。。都是102400:32
Recv >>> [523]102400:32
Recv >>> [526]102400:54208
Recv >>> [527]102400:32
。。。。。。都是102400:32
Recv >>> [630]102400:32
Recv >>> [631]102400:32
Recv >>> [632]102400:65568
。。。。。。都是102400:32
Recv >>> [652]102400:32
Recv >>> [653]102400:32


分析:用户从接收缓存区读取内容与kernel向接收缓存区填充内容这两个过程是互斥的,
recv只从接收缓存区中获取一次内容,并且也只关心自己获取时接收缓存区有多少内容:
a、如果当时缓存区中没有数据,则recv进入阻塞状态,等待kernel向缓存区被填入数据后重新激活。
b、如果当时缓存区中的数据比用户接收使用的buffer大,则填满buffer后recv函数返回。
c、如果当时缓存区中的数据比用户接收使用的buffer小,则取走缓存区中的所有数据后recv函数返回。
kernel向接收缓存区填充则是收到数据包后到自己的时间片并且缓存区不在被读,则将拿到的包内容全部放入缓存区中。



这样就可以解释测试5中的现象了:
a、kernel第一二次都只收到了一个16384大小的包,所以接收的A处与B处都收到了整个数据包
b、慢慢的包陆陆续续的到来kernel第三次收到了两个16384大小的包,填满了整个缓存区,所以接收的C处,
在用户的时间片内用户收走了缓存区的全部数据。
c、接下来数据蜂拥而至,导致缓存区一直处于满的状态,所以后来的接收都能收满用户buffer。
d、接收的E处接收完整个缓存区,随后接收收尾的包
为了验证以上推断,进行了测试6。


测试7验证了快速发送时的发送接收情况,证明了send发送与recv接收过程完全没关系,
即使发送的包带有PUSH标记,也只能保证每一个包被单独放入接收缓存区中,无法使recv接收每收到一个包返回。
整个接收过程遵循上面的规则,何时返回,返回多少数据均要看当时缓存区内的数据多少。
像测试7中大部分包每个包都返回只是因为接收比较快导致kernel每次只来得及向接收缓存区中放一个包而已。


结论:
a、不要期待利用TCP协议一方send一次,另一方recv一次的逻辑,这种逻辑是不可靠,也没有理论依据的
b、在本机进行消息逻辑控制,或者说信令级传输,建议还是使用UDP
c、若必须要使用TCP消息传输时一定要加同步头,例如一个固定的值,用来分割数据包或者验证是否包乱序或越界

还有一个别人分析的地址,挺不错的:

http://www.vckbase.com/index.PHP/wv/10


抓去的数据包的下载地址:

http://www.xlpan.com/home/9343823/057665c9-503e-4c44-8216-f1aef91e9819


后面想到什么了再补上!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值