3

我正在编写一个带有以下代码片段的服务器-客户端程序来接收数据。

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

4

5 回答 5

7

看到的可能原因有以下三种ETIMEDOUT

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

select不产生ETIMEDOUT, 只有connectrecv可能。尽管select在极少数情况下可以在没有接收任何内容时报告准备就绪(较旧的 Linux 内核,这可能已被修复),但在这种情况下唯一会发生的事情就是recv阻塞。

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

connect失败是由于多种原因(无法访问、防火墙、服务器进程未运行等)您可能会看到的一种情况,并且每次您尝试时都会定期发生这种情况,只要导致它的条件持续存在。

关于之后完全接收数据的解决方法ETIMEDOUT,没有。read将为您提供其缓冲区中的内容(最多为您在函数调用中指定的最大值)、阻塞或失败。这三件事之一,没有别的,永远。
一旦它失败了,你已经拥有了失败之前可用的所有东西(你没有更多的东西可以阅读),现在连接已经消失,即套接字不再可用。
你唯一能做的就是创建一个新的套接字并建立一个新的连接,然后再试一次。

于 2013-05-27T12:31:34.083 回答
4

在套接字上启用 TCP keepalive 会导致从recv () 返回 ETIMEDOUT errno。TCP keepalive 是一个很好的机制,您一定要检查它。

如果对方在一段时间后没有确认重新传输的数据,则可以通过send () 返回 ETIMEDOUT。还要检查 TCP_USER_TIMEOUT 套接字选项,这也可能导致套接字上的 ETIMEDOUT errno。

你可以从着名的书“Unix网络编程”中查看这一章。

于 2016-08-05T15:05:30.057 回答
1

呃,不应该

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

?

于 2013-07-28T21:12:27.580 回答
0

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

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

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

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

更多信息:这里

select(2) 和 poll(2) 系统调用当前不遵守 Linux 上的 SO_RCVLOWAT 设置,并且即使只有一个字节的数据可用,也会将套接字标记为可读。来自套接字的后续读取将阻塞,直到 SO_RCVLOWAT 字节可用。

于 2013-05-27T12:28:49.043 回答
0

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

于 2013-05-27T12:20:02.307 回答