同样的服务端程序,在ubunut下面运行正常,在centos7下面会运行出错。出错的原因在于在调用recv,返回值变成0了,导致程序判断为网络断开。而抓包发现,也是服务端主动发起RST中断连接的。另外,在recv返回0的时候,errno有时候为0,有时候为9,也就是EBADF。EBADF的意思为错误的文件描述符,我主观上感觉是系统先把socket关闭了,从而导致文件描述符出错,因此花了很多的时间在查找为什么系统会把socket关闭了。看了防火墙是关闭,而且系统也没有什么报错信息
后来去翻了一下linux的文档,看看recv会在什么时候返回0
http://man7.org/linux/man-pages/man2/recv.2.html
When a stream socket peer has performed an orderly shutdown, the
return value will be 0 (the traditional "end-of-file" return).
Datagram sockets in various domains (e.g., the UNIX and Internet
domains) permit zero-length datagrams. When such a datagram is
received, the return value is 0.
The value 0 may also be returned if the requested number of bytes to
receive from a stream socket was 0.
其中第三个条件,提供给recv的缓冲区长度为0了。我看了下我的程序设计,感觉应该不会出现。因为我的缓冲区设置为两个数据包的大小,即使某一次recv把缓冲区塞满了,缓冲区之中肯定已经有一个完整的数据包了,完整的数据包会被解析掉,剩余的数据会往前挪动,为下一次recv留出空间。
这次的大意,导致又浪费了一些时间而没把问题找出来。直到后来,又在stackoverflow看到一篇文章
https://stackoverflow.com/questions/10526382/recv-returns-0
recv() returns 0 only when you request a 0-byte buffer or the other peer has gracefully disconnected.
再次提到了,recv返回0有可能是因为输入buffer的长度为0了。于是认真的把提供给recv的buffer长度打印了出来,结果果然是变成0了!
为什么recv的buffer长度会变成0呢?调试了一下程序。在我的程序的通信协议中,前面2个字节用来存放数据包的长度。发生异常的时候,数据包的长度会变成几千几万这种不正常的长度,导致程序一直认为没有接收到完整的一个数据包,从而不去解析数据,直至buffer填满。而不正常的根源,在于我解析完一个完整数据包之后,使用了memcpy把剩余的数据往前挪,从而导致数据出错了!把memcpy改成memmove,一切就正常了。
网友评论