网络编程——超时检测(select()、poll()和 setsockopt()的设置合理的超时检测机制)

网络编程中的超时检测

一、为什么需要超时检测?

在网络编程中,我们通常使用套接字(sockets)进行网络通信。当进行连接、发送和接收数据等操作时,可能会遇到以下情况导致阻塞:

1、网络故障:当网络出现问题时,连接、发送和接收数据可能会永远无法完成,导致程序长时间阻塞。
2、对端无响应:当尝试建立连接或发送数据时,对端可能没有响应,导致程序一直等待。
为了避免以上问题,我们需要在进行网络操作时设置合理的超时检测机制,使程序能够在合理的时间内做出响应,增加程序的健壮性。

二、超时检测方法

1. 使用 select() 函数

select() 函数是一种多路复用的系统调用,可以同时监视多个文件描述符(包括套接字)。通过设置超时时间,可以实现超时检测。


#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

超时检测:将需要监视的套接字添加到相应的 fd_set 中,然后调用 select() 函数。如果在指定的超时时间内有任何一个套接字就绪(可读、可写或发生异常),select() 函数将返回一个大于 0 的值,否则在超时后返回 0。

功能:select() 函数是一个多路复用函数,用于监视一组文件描述符的状
态,包括读、写和异常状态。
返回值:select() 函数返回就绪文件描述符的数量,若超时时间内没有任何文件描述符就绪,则返回 0。如果出现错误,返回 -1,并设置 errno。
参数说明:

nfds:文件描述符集合中最大文件描述符的值 + 1。
readfds: 指向包含待检测可读文件描述符的集合的地址。
writefds: 指向包含待检测可写文件描述符的集合的地址。
exceptfds: 指向包含待检测异常条件文件描述符的集合的地址。
timeout: 指定 select() 函数的超时时间结构体地址,即最大等待时间。

#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
    printf("Please enter the content:");
    int sockfd; // 假设已经创建并连接了一个套接字

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(sockfd, &read_fds);


    while(1){
        struct timeval timeout;
        timeout.tv_sec = 5; // 设置超时时间为 5.5 秒
        timeout.tv_usec = 500;

        int ready_fds = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
        if (ready_fds == -1) {
            perror("select");
            exit(-1);
        } else if (ready_fds == 0) {
            printf("Timeout occurred.\n");
            continue;
        }

        if (FD_ISSET(sockfd, &read_fds)) {
            // 套接字就绪,可以进行读取操作
            char buffer[1024];
            int num_bytes = read(sockfd, buffer, sizeof(buffer));
            if (num_bytes == -1) {
                perror("read");
                exit(-1);
            } else if (num_bytes == 0) {
                printf("Connection closed by remote host.\n");
                return 0;
            } else {
                buffer[num_bytes] = '\0';
                printf("Received data: %s\n", buffer);
            }
        }
    }

    return 0;
}

2. 使用 poll() 函数

poll() 函数也是一种多路复用的系统调用,类似于 select() 函数。通过设置超时时间,可以实现超时检测。

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

超时检测:设置好 pollfd 结构数组中的套接字信息,然后调用 poll() 函数。如果在指定的超时时间内有任何一个套接字就绪,poll() 函数将返回一个大于 0 的值,否则在超时后返回 0。

功能:poll() 函数也是一个多路复用函数,用于监视一组文件描述符的状态,类似于 select() 函数。
返回值:功能:poll() 函数也是一个多路复用函数,用于监视一组文件描述符的状态,类似于 select() 函数。
参数说明:

fds: 指向一个包含待检测文件描述符信息的结构体数组地址。
nfds: 文件描述符数组中的元素数量。
timeout: 指定 poll() 函数的超时时间,即最大等待时间(以毫秒为单位)。

3. 使用 setsockopt() 函数

使用 setsockopt() 函数可以设置套接字的选项,其中包括设置接收或发送超时时间。通过设置这些超时时间,可以在套接字操作时进行超时检测。

#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

超时检测:在进行数据接收或发送前,使用 setsockopt() 设置套接字的 SO_RCVTIMEOSO_SNDTIMEO 选项。如果在指定的超时时间内没有接收到数据或无法发送数据,相关操作将返回一个错误,通过检查错误码可以判断是否发生了超时。

功能:setsockopt() 函数用于设置套接字选项,包括超时时间、套接字缓冲区大小等等。
返回值:返回值:setsockopt() 函数返回 0 表示成功设置选项,返回 -1 表示出现错误,并设置 errno。
参数说明:

sockfd: 套接字描述符。
level: 选项所在的协议层,通常是 SOL_SOCKET。
optname: 选项名,例如 SO_RCVTIMEO 表示接收超时时间。
optval: 指向包含选项值的缓冲区的地址。
optlen: 缓冲区中选项值的长度的地址。

4. 使用时间戳

这种方法适用于需要控制超时精度的情况。在发送或接收数据前,记录当前时间戳(可以使用 time() 函数或 gettimeofday() 函数),然后在一定时间间隔后检查当前时间戳与记录的时间戳之间的差异。如果差异超过预定的超时时间,则认为发生了超时。

5. 使用定时器

在某些操作系统中,可以使用定时器来实现精确的超时检测。例如,在 Linux 系统中,可以使用 setitimer() 函数来设置定时器。当定时器超时时,系统会发送 SIGALRM 信号,我们可以在信号处理函数中进行超时处理。

6. 使用非阻塞套接字

将套接字设置为非阻塞模式(通过调用 fcntl() 函数设置套接字标志为 O_NONBLOCK),然后使用 select()、poll() 或类似的函数来检测套接字是否就绪。如果在一定时间内套接字没有就绪,相应的函数将返回 0,从而可以认为发生了超时。

总结

网络编程中的超时检测是确保程序高性能和可靠性的关键一环。本文介绍了多种超时检测方法,包括使用 select()、poll() 函数,设置 setsockopt() 选项,使用时间戳和定时器,以及使用非阻塞套接字等。具体的使用方法取决于编程需求和特定的情况。合理地选择并使用这些方法,可以有效地避免程序长时间阻塞,并保障系统在网络故障或其他异常情况下的稳定运行。在进行网络编程时,请务必重视超时检测,并根据实际情况选择适合的方法,从而提高网络应用的性能和健壮性。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小羊客栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值