select()ed socket上的socket recv与etimedout失败

时间:2013-05-27 11:46:14

标签: linux sockets

我正在编写一个服务器客户端程序,其中包含以下代码片段以接收数据。

    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之后也能完全接收数据。

5 个答案:

答案 0 :(得分:5)

查看ETIMEDOUT有三个可能的原因:

  1. 连接在recv内超时,这种情况甚至不太可能发生一次(但肯定不会发生过几次)。
  2. 您没有检查connect的成功,并且连接从未成功建立(防火墙可能正在丢弃连接尝试?)。这是可能的原因。
  3. 你的套接字实现被破坏了,这是不太可能的。
  4. select不会生成ETIMEDOUT,只会生成connectrecv。虽然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字节可用。