【问题标题】:how to use timeouts with read() on a socket in c++ on Unix如何在 Unix 上的 c++ 中的套接字上使用 read() 超时
【发布时间】:2012-02-28 02:47:59
【问题描述】:

我想将 read() 与 ioctl() 一起使用,但想通过使用超时来控制读取应等待的时间。 关于如何做到这一点的任何想法?

目前我知道的是:

//CLIENT.cpp
struct timeval tv={1,0};
setsockopt( mysocket, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(tv));
connect(mysocket, &sock_dest, sizeof(struct sockaddr));
len = read(mysocket, buffer, 10);

我尝试在服务器上使用 5 秒延迟,但没有超时...

【问题讨论】:

  • 您可以尝试将O_NONBLOCK 设置为fcntl(2),然后使用select(2) 来确定套接字何时可以读取(选择允许您指定超时)。
  • 同意。顺便说一句,如果您关心read 需要多长时间,您可能也应该关心connect 需要多长时间(如果超时,可能需要一段时间)。您可以在调用connect 之前使套接字变为非阻塞,并通过selectpoll 进行处理。

标签: c++ sockets unix timeout


【解决方案1】:

ioctl() 不会做你想做的事。要对读取使用超时,您需要使用poll() 或旧接口select()(我会使用poll())。使用SO_RCVTIMEO 设置的超时可能会在每次接收到新数据时重置。因此,对于您的示例,它可能会等待长达 10 秒。 poll() 在指定超时后返回,告诉您是否有任何数据。一旦出现这种情况,您就可以使用非阻塞 read() 读取任何内容。

【讨论】:

  • 我正在使用 ioctl 来检查可以读取多少字节,如果还没有准备好,则再次循环,但读取是阻塞调用,使用超时继续。目的是替换 SELECT 调用。
  • 好吧,将SO_RCTTIMEO 设置为timeout / size(其中timeout 以秒为单位,size 以字节为单位)的效果应该与此类似。然而,这个标志似乎不太便携,我仍然会使用poll()(或者select(),如果poll()不知何故不可用)。为什么要删除这些调用?他们很好地处理超时。
  • 使用相同超时值的选择调用太多,导致未定义的行为,这样我想实现重试,并管理超时......
  • 您是否考虑过一次处理多个文件描述符?如果您同时有许多 select()s 处于活动状态并且这会导致问题,我会假设使用 SO_RCVTIMEO 实际上更有可能导致问题,然后删除一些...
【解决方案2】:

您可以通过设置警报来中断系统调用来做您想做的事。您需要在程序初始化过程的 main 或早期的某个地方进行一些基本设置:

#include <signal.h>

sig_atomic_t alarm_counter;

void alarm_handler(int signal) {
    alarm_counter++;
}

void setup_alarm_handler() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = alarm_handler;
    sa.flags = 0;
    if (sigaction(SIGALRM, &sa, 0) < 0)
        die("Can't establish signal handler");
}
// call setup_alarm_handler in main

然后,你可以像这样使用它:

alarm(10); // set a 10 second timeout
(len = connect(mysocket, &sock_dest, sizeof(struct sockaddr))) < 0 ||
(len = read(mysocket, buffer, 10));
alarm(0); // cancel alarm if it hasn't happened yet
if (len == -1 && errno == EINTR)
    // timed out before any data read
else if (len == -1)
    // other error

通过这种方式,您可以为一系列调用设置超时(如果连接或读取单独或总共花费的时间过长,这将超时),而不必计算每个调用花费了多长时间,所以您知道每次后续调用要等待多长时间。

【讨论】:

    【解决方案3】:

    您确定它在读取时阻塞了,还是在 connect() 时阻塞了?我不相信 SO_RCVTIMEO(和 SO_SNDTIMEO)选项会影响 connect() 行为。

    一般来说,我喜欢使用非阻塞套接字(带有 O_NONBLOCK 的 fcntl),然后捕获 EWOULDBLOCK errno 并使用读取集中的套接字和所需的超时执行 select()。

    【讨论】:

      猜你喜欢
      • 2015-02-12
      • 2018-06-21
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2015-01-10
      • 2015-11-29
      • 2023-03-21
      • 2017-11-29
      相关资源
      最近更新 更多