首页 > 系统 > Linux > 正文

服务器编程入门(13) Linux套接字设置超时的三种方法

2024-06-28 13:27:03
字体:
来源:转载
供稿:网友
服务器编程入门(13) linux套接字设置超时的三种方法

摘要:

    本文介绍在套接字的I/O操作上设置超时的三种方法。


套接字超时

图片可能有点宽,看不到的童鞋可以点击图片查看完整图片。。


1 调用alarm

使用SIGALRM为connect设置超时

设置方法:

  1. 监听SIGALRM信号,
  2. 设置sig_alrm处理函数,
  3. 在阻塞函数前调用alarm函数设置超时时间,
  4. 正常返回后,重置超时事件为0
void handle_msg(int sockfd) {    char sendbuf[BUFSIZE];    char recvbuf[BUFSIZE];        signal(SIGALRM, sig_alrm);    //监听SIGALRM信号     while(1) {        memset( sendbuf, '/0', BUFSIZE );        memset( recvbuf, '/0', BUFSIZE );                PRintf("%s", "send msg:");        gets(sendbuf);        if (strlen(sendbuf) > 0)            send(sockfd,sendbuf,strlen(sendbuf),0);        if ( !strcmp(sendbuf, "exit"))            break;        alarm(5);       //设置超时事件为5s,同时设置服务器回射前sleep 10秒,以让recv函数超时        if (recv(sockfd,recvbuf,BUFSIZE,0) > 0) {            alarm(0);            printf("recv back:%s/n/n", recvbuf);        }        else {            if (errno == EINTR)                fprintf(stderr,                        "socket timeout/n");            else                fprintf(stderr,                        "receive error/n");        }    }    close( sockfd );    return;}static void sig_alrm(int signo) {    fprintf(stderr,            "recv SIGALRM, return./n");    return;}

运行截图:

image

虽然设置了SIGALRM信号处理函数,但是如图所示,本例依然可以等待读取回射信息,因为信号处理函数里只是return。


2 使用select阻塞等待I/O

设置方法:

使用select的内置时间限制,阻塞在select代替recv函数的阻塞。

void handle_msg(int sockfd) {    char sendbuf[BUFSIZE];    char recvbuf[BUFSIZE];    while(1) {        memset( sendbuf, '/0', BUFSIZE );        memset( recvbuf, '/0', BUFSIZE );                printf("%s", "send msg:");        gets(sendbuf);        if (strlen(sendbuf) > 0)             send(sockfd,sendbuf,strlen(sendbuf),0);        if ( !strcmp(sendbuf, "exit"))            break;                if (readable_timeo(sockfd, 5) == 0) {            fprintf(stderr,                    "socket timeout/n");        }        else{            recv(sockfd,recvbuf,BUFSIZE,0);            printf("recv back:%s/n/n", recvbuf);        }    }    close( sockfd );    return;}int readable_timeo(int fd, int sec) {    fd_set rset;    struct timeval tv;        FD_ZERO(&rset);    FD_SET(fd, &rset);    tv.tv_sec = sec;    tv.tv_usec = 0;    return select(fd+1, &rset, NULL, NULL, &tv);}

 

运行截图:

image

由运行截图可以看到,超时警告运行正常。

需要注意一点的是,虽然客户端在超时之后继续发送消息,但是服务器回射的消息(hello world)依然被接收,这导致我们再次发送消息时,从缓冲区中读出了延迟收到的hello world。

这里只是演示超时技术,因此对此并不做进一步处理。


3 使用SO_RCVTIMEO套接字选项

使用SO_RCVTIMEO套接字选项为recv设置超时

设置方法:

  • 使用setsockopt函数对套接字进行设置
  • 一旦设置了某个描述符,其超时设置将应用于该描述符上的所有读操作
  • SO_RCVTIMEO仅用于读操作,SO_SNDTIMEO仅用于写操作,两者都不能用于为connect设置超时
  • 如果套接字超时,被阻塞的函数将返回一个EWOULDBLOCK错误
void handle_msg(int sockfd) {    char sendbuf[BUFSIZE];    char recvbuf[BUFSIZE + 1];    int n;    struct timeval tv;    tv.tv_sec = 5;    tv.tv_usec = 0;    setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO,               &tv, sizeof(tv) );    while(1) {         memset( sendbuf, '/0', BUFSIZE );        memset( recvbuf, '/0', BUFSIZE );                printf("%s", "send msg:");        gets(sendbuf);        if (strlen(sendbuf) > 0)             send(sockfd,sendbuf,strlen(sendbuf),0);        if ( !strcmp(sendbuf, "exit"))            break;        if ( (n=recv(sockfd,recvbuf,BUFSIZE,0)) < 0 ) {            if (errno == EWOULDBLOCK) {                fprintf(stderr,                        "socket timeout/n");                continue;            }            else                fprintf(stderr,                        "recv error");        }        else{            printf("recv back:%s/n/n", recvbuf);        }    }    close( sockfd );    return;}

运行截图:

image

可以看到,超时警报成功运行,但依然有上一例中的延迟接收的情况发生,不作处理。


 

示例源码上传到了github上,地址:https://github.com/zs634134578/UNP/tree/tryTimeout

 

 

参考资料:

《UNIX网络编程 卷1:套接字联网API(第3版)》


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表