2013-05-27 109 views
3

我正在写一个服务器端客户端程序与下面的代码片段接收数据。套接字recv上select()编辑套接字失败与etimedout

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 ; 
     } 
    } 

据我所知,recv在select()ed文件描述符应该接收数据没有失败。但是,在我的代码中收回错误ETIMEDOUT失败。有人请告诉我为什么会发生这种情况。还请告诉我一些解决方法,即使在ETIMEDOUT之后也能完全接收数据。

+1

代码中'readfds'的类型是什么?从我在文档中看到的,它应该是一个'fd_set',一个struct {int n,int []},所以在第一个参数中没有什么可以安全地使用'+ 1'。你有没有黑客入侵系统,偶然使用'int readfds'? – quetzalcoatl

+1

这里没有代码在适当的地方检查errno。您是如何决定获得ETIMEDOUT的? – nos

+0

当我使用我的公司框架时,我删除了一些框架细节并复制了需要理解的代码。 readfds是fd_set类型,select的第一个参数是max(socket函数返回的fds)+1 – syam

回答

0

只是一个疯狂的猜测。当TCP连接丢失时。 select会返回并将此fd设置为可读。但是recv将失败,并显示错误ETIMEDOUT。

0

一个可能的原因是套接字选项SO_RCVLOWAT

如果它的值大于1,那么linux的select返回即使只有一个字节可用,并声称该套接字是可读的。

当在这种情况下调用recv时,它会阻塞,直到发生超时(由SO_RCVTIMEO设置),因为可用字节数小于低水位标记。

因此,检查您的代码是否更改SO_RCVLOWAT的值。默认值是1。

的更多信息:here

的选择(2)和轮询(2)目前SYS-TEM调用不尊重Linux上的 SO_RCVLOWAT设置,并且标记可读在套接字即使是 也只有一个字节的数据可用。从套接字 后面的读取将阻塞,直到SO_RCVLOWAT字节可用。

+0

为什么在这种情况下会得到ETIMEDOUT? – nos

+0

很明显,SO_RCVTIMEO也发生了变化。如果没有改变,那么我的回答并不能解决OP的问题。 – SKi

+0

嗯,根本不清楚SO_RCVTIMEO是否发生了变化,在这种情况下,errno应该是EAGAIN。 ETIMEDOUT也可能在其他情况下发生。 – nos

4

有用于看到ETIMEDOUT三种可能原因:

  1. 连接内recv超时,这是不太可能甚至一度(但肯定不是几次)发生。
  2. 您没有检查成功connect,并且连接从未成功建立(也许防火墙正在删除连接尝试?)。这是可能的原因。
  3. 你的套接字实现被破坏,这是不太可能的。

select不会产生ETIMEDOUT,只有connectrecv可能。虽然select在极少数情况下可以在没有任何东西可以收到时报告准备情况(较早的Linux内核,这大概已经修复),但是在这种情况下唯一会发生的情况是阻止recv

recv可能会产生此错误,但一旦连接建立后连接就不会超时 - 如果您没有拉电缆,或者正如nos所指出的那样,NAT网关可能会在几分钟后做任何事情。如果可以建立连接,则有一条路由,另一端正在收听,所以通常没有超时的原因(当然,这可能,只是不可能一直发生)。这个错误当然最终会发生如果由于某种原因连接真的超时(不管是否阻塞),但是如果有的话,这是一个非常特殊的情况,而不是一个普通的情况。

connect失败是由于许多原因(不可到达,防火墙,服务器进程未运行等)而可能会看到的情况,并且每次尝试时都会经常发生,只要导致它持续的条件。

至于在ETIMEDOUT之后完全接收数据的解决方法,没有。 read会给你什么它的缓冲区(直到你在函数调用中指定的最大值),或阻塞或失败。这三件事之一,没有别的,永远。
一旦它失败了,你已经拥有了在失败之前可用的所有东西(在你的末端没有更多东西需要读取),现在连接消失了,即套接字不再可用。
您可以做的唯一事情就是创建一个新的套接字并建立一个新的连接,然后重试。

+0

请注意,在建立连接后,连接超时(即,您正在建立的连接上发送数据,但没有收到TCP响应,通常为ACK,TCP将超时连接,因为重新传输计时器已过期)非常常见例如由于NAT网关和防火墙悄无声息地将连接超时,所以长时间连接一段时间只能传输很少或没有数据。 – nos

+0

是的,一个NAT网关或状态防火墙超时后,说5分钟无所事事可能是一个合理的理由,如果你真的没有发送这么长的时间。尽管对我来说,这个问题更像是OP发送了一些东西(大概是在局域网中,至少这是我在编写服务器和客户端时测试的地方),并且无法接收。 – Damon

1

呃,应该不会是

select(myfd+1,&readfds,NULL,NULL,NULL) 

1

在套接字上启用TCP keepalive导致ETIMEDOUT errno从recv()返回。

ETIMEDOUT可以返回发送()如果另一端在一段时间后没有确认重传的数据。还请检查TCP_USER_TIMEOUT套接字选项,这也会导致套接字上的ETIMEDOUT错误。

您可以从着名的“Unix网络编程”中查到this chapter