我正在编写一个服务器客户端程序,其中包含以下代码片段以接收数据。
ret_l = select(readfds+1, &readfds, NULL, NULL ,NULL);
if(ret_l != -1)
{
if(FD_ISSET(myfd, &readfds))
{
ret_l = recv(myfd, buf, size_of_buf_array, 0);
if(ret_l == -1)
return ;
}
}
据我所知,select()ed文件描述符上的recv应该没有失败地接收数据。但是我的代码中的recv因错误ETIMEDOUT而失败。有人请告诉我为什么会这样。还请告诉我一些解决方法,即使在ETIMEDOUT之后也能完全接收数据。
答案 0 :(得分:5)
查看ETIMEDOUT
有三个可能的原因:
recv
内超时,这种情况甚至不太可能发生一次(但肯定不会发生过几次)。connect
的成功,并且连接从未成功建立(防火墙可能正在丢弃连接尝试?)。这是可能的原因。 select
不会生成ETIMEDOUT
,只会生成connect
和recv
。虽然select
在极少数情况下可以在没有任何内容接收时报告准备就绪(较旧的Linux内核,这可能已被修复),但在这种情况下唯一会发生recv
阻塞。
recv
可能会产生此错误,但是一旦建立连接就不太可能超时 - 如果您没有拔出电缆,或者如nos所指出的那样,NAT网关可能会在某些情况下超时几分钟没做任何事。如果可以建立连接,则有一条路由,而另一端有人在监听,因此通常没有常规超时的原因(当然有可能,只是不太可能发生所有时间)。如果连接由于某种原因(无论阻塞)真的超时,这个错误 当然会最终发生,但是如果有的话,这是一个非常特殊的条件,而不是常规条件。
connect
失败是您可能会想到的一个条件,由于许多原因(无法访问,防火墙,服务器进程未运行等),并且每次尝试时都会经常发生这种情况,只要导致它的条件持续存在。
关于在ETIMEDOUT
之后完全接收数据的变通方法,没有。 read
将为您提供缓冲区中的内容(最多为您在函数调用中指定的最大值),阻塞或失败。这三件事之一,没有别的
一旦失败,你已经拥有了在失败之前可用的所有内容(没有什么可以在你的结尾阅读),现在连接已经消失,即套接字不再可用了。
你唯一能做的就是创建一个新套接字并建立一个新的连接,然后再试一次。
答案 1 :(得分:1)
呃,不应该是
select(myfd+1,&readfds,NULL,NULL,NULL)
答案 2 :(得分:1)
在套接字上启用TCP keepalive导致从 recv ()返回ETIMEDOUT错误.TCP keepalive是一种很好的机制,你一定要检查它。
如果另一端在一段时间后没有确认重传数据,则发送()可以返回ETIMEDOUT。还要检查TCP_USER_TIMEOUT套接字选项,它也会导致套接字上出现ETIMEDOUT错误。
您可以从着名的“Unix网络编程”中查看this chapter。
答案 3 :(得分:0)
只是一个疯狂的猜测。当TCP连接丢失时。 select
将返回并将此fd设置为可读。但recv
将因ETIMEDOUT错误而失败。
答案 4 :(得分:0)
一个可能的原因是套接字选项SO_RCVLOWAT
。
如果它的值大于1,则linux的select
返回,即使只有一个字节可用,并声称套接字是可读的。
在这种情况下调用recv
时,它将阻塞直到超时发生(使用SO_RCVTIMEO设置),因为可用字节数小于低水位线。
因此,请检查您的代码是否更改SO_RCVLOWAT
的值。默认值为1.
更多信息:here
select(2)和poll(2)系统调用当前不尊重 在Linux上设置SO_RCVLOWAT,并在偶数时标记套接字可读 一个字节的数据可用。随后从套接字读取 将阻塞直到SO_RCVLOWAT字节可用。