如何在Unix上的c++套接字上使用带有read()的超时

how to use timeouts with read() on a socket in c++ on Unix

本文关键字:read 超时 Unix 上的 c++ 套接字      更新时间:2023-10-16

我想将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秒的延迟,但它没有超时。。。

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

您可以通过设置警报来中断系统调用来执行您想要的操作。您需要在主程序或程序初始化过程的早期进行一些基本设置:

#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

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

您确定它在读取时被阻塞,还是可能在connect()上被阻塞?我不认为SO_RCVTIMEO(和SO_SNDTIMEO)选项会影响connect()行为。

通常,我喜欢使用非阻塞套接字(fcntl with O_NONBLOCK),然后捕获EWOULDBLOCK错误号,并使用读取集中的套接字和所需的超时值执行select()。