tcp调用recv接口未判断接收缓冲区长度导致返回值为零误判为链路断开

    我们在调用recv(fd,buf,len,0)接口时,都要判断返回值,如果返回值为0,表示链路已断开。然而如果len设置为0,那么返回值也是0,此时如果判断链路断开,显然是不对的.在最近一次测试新功能的时候,测出了这样一个旧bug。追查bug的过程如下:

    为了测试一个新功能,有这样一个场景:A向B疯狂的发送异步消息,其中B调用了我的接口。现象:B没过多久发现recv()的返回值为0,判断链路断开,然后尝试重连A。这种高频发送异步消息的场景我之前也是测试过的,是没有问题的,只是没有到达“疯狂”的程度。这也导致我错误的认为自己的代码没有问题,能够撑住高频异步消息的场景,实际证明,我当时测试的“高频”,还不够高频,所以没有测出问题。

    解决过程:发现B断开链接之后,A和B的代码逻辑中,都认为是对方主动断开,于是进行万能的抓包。所幸A和B不在同一台机器上面,否则还真抓不到,如果他们的通信没有经过网卡的话。抓包发现:B给A发了一个RST的数据包。这个包代表的含义如下:接收缓冲区有数据,主动断开链路,就会给对方发送一个 RST 的包,说明异常断开连接。这就是说,B的接收缓冲区里面还有数据,但是B主动调用close(fd)函数断开链路,此时A就会收到B发过来的RST的包。也就是说,链路是B主动断开的。那么问题来了,B为何会主动断开链接呢,明明B判断是对方断开链路,然后才断开链路的啊?

    考虑到A疯狂的发送异步消息,会不会是B的tcp接收缓冲区小了,无法接受A发送的海量数据?但是将系统的tcp参数调整之后,发现问题依然存在。转念一想,tcp接收缓冲区的大小,并不影响数据的接收,因为tcp有流量控制,利用滑动窗口的机制进行拥塞控制,如果B接收不了这么多数据,A就会慢慢发,不会撑爆B的tcp缓冲区,导致链路断开。

    既然不是系统的原因,那么自然是代码的原因了。但是将代码捋了几遍之后,感觉还是没问题。这也正常,毕竟,自己看自己的代码,很难看出问题的。于是一个一个方向进行排查。

首先考虑,是不是非网络原因导致的,比如线程/锁等?想想也不对,其他线程没有主动断开链路的,但是我还是做了一个测试,将recv()函数注释掉,不去收数据了。这个结果自然是正常的,不会引发链路断开,因为B不收数据,就不会判断链路断开,也就不会销毁资源,同时也不会断开链路。

看来问题还是出在recv上面了。转念一想,既然A和B都认为自己没有主动断开链路,那么是不是链路本来就是正常的,压根就没有断开呢?于是我做了一个特殊处理,在B的逻辑中,如果B判断链路异常,则忽略这个异常,继续接收数据。果然,B可以继续接收数据,链路是正常的,B错误的判断链路断开!

得到这个测试结果,我就更加纳闷了,明明recv()函数的返回值为0,代表链路断开,查文档都是这样说的,为啥到我这里会这样,那我岂不是无法判断链路到底是正常还是断开?询问同事之后,同事一句话令我茅塞顿开:recv(fd,buf,len,0)中的len如果设置为0,返回值会是什么情况?还真有这个可能!我修改代码,在recv之前先打印len的值,果然当len为0时,recv的返回值也是0.原因找到了!原来是在调用recv函数的时候,不能让len设置为0.

    后记:其实在调用recv()函数之前,我是判断过了len是否为0的,只不过后来写代码的过程中,中间加了其他逻辑,修改过len的值,导致len有可能变为0,然而我又没有记起来,只记得自己判断过len的值了.以后写代码还是要注意,像这种值的判断,尽量写在临近调用的地方,防止自己修改过,然后又忘记了,还觉得自己已经处理过这种异常情况。另外,对于网络编程,多测试一下异常情况,比如疯狂发送。

 
 
G
M
T
 
 
检测语言世界语中文简体中文繁体丹麦语乌克兰语乌兹别克语乌尔都语亚美尼亚语伊博语俄语保加利亚语僧伽罗语克罗地亚语冰岛语加利西亚语加泰罗尼亚语匈牙利语南非祖鲁语卡纳达语印地语印尼巽他语印尼爪哇语印尼语古吉拉特语哈萨克语土耳其语塔吉克语塞尔维亚语塞索托语威尔士语孟加拉语宿务语尼泊尔语巴斯克语布尔语(南非荷兰语)希伯来语希腊语德语意大利语意第绪语拉丁语拉脱维亚语挪威语捷克语斯洛伐克语斯洛文尼亚语斯瓦希里语旁遮普语日语格鲁吉亚语毛利语法语波兰语波斯尼亚语波斯语泰卢固语泰米尔语泰语海地克里奥尔语爱尔兰语爱沙尼亚语瑞典语白俄罗斯语立陶宛语索马里语约鲁巴语缅甸语罗马尼亚语老挝语芬兰语苗语英语荷兰语菲律宾语葡萄牙语蒙古语西班牙语豪萨语越南语阿塞拜疆语阿尔巴尼亚语阿拉伯语韩语马其顿语马尔加什语马拉地语马拉雅拉姆语马来语马耳他语高棉语齐切瓦语
 
世界语中文简体中文繁体丹麦语乌克兰语乌兹别克语乌尔都语亚美尼亚语伊博语俄语保加利亚语僧伽罗语克罗地亚语冰岛语加利西亚语加泰罗尼亚语匈牙利语南非祖鲁语卡纳达语印地语印尼巽他语印尼爪哇语印尼语古吉拉特语哈萨克语土耳其语塔吉克语塞尔维亚语塞索托语威尔士语孟加拉语宿务语尼泊尔语巴斯克语布尔语(南非荷兰语)希伯来语希腊语德语意大利语意第绪语拉丁语拉脱维亚语挪威语捷克语斯洛伐克语斯洛文尼亚语斯瓦希里语旁遮普语日语格鲁吉亚语毛利语法语波兰语波斯尼亚语波斯语泰卢固语泰米尔语泰语海地克里奥尔语爱尔兰语爱沙尼亚语瑞典语白俄罗斯语立陶宛语索马里语约鲁巴语缅甸语罗马尼亚语老挝语芬兰语苗语英语荷兰语菲律宾语葡萄牙语蒙古语西班牙语豪萨语越南语阿塞拜疆语阿尔巴尼亚语阿拉伯语韩语马其顿语马尔加什语马拉地语马拉雅拉姆语马来语马耳他语高棉语齐切瓦语
 
 
 
 
 
 
 
 
 
文本转语音功能仅限200个字符
 
 
选项 : 历史 : 反馈 : Donate 关闭

猜你喜欢

转载自my.oschina.net/u/2447371/blog/1807796