zoukankan      html  css  js  c++  java
  • 服务器编程入门(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, '', BUFSIZE );
            memset( recvbuf, '', 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
    
    ", recvbuf);
            }
            else {
                if (errno == EINTR)
                    fprintf(stderr,
                            "socket timeout
    ");
                else
                    fprintf(stderr,
                            "receive error
    ");
            }
        }
        close( sockfd );
        return;
    }
    
    
    static void sig_alrm(int signo) {
        fprintf(stderr,
                "recv SIGALRM, return.
    ");
        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, '', BUFSIZE );
            memset( recvbuf, '', 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
    ");
            }
            else{
                recv(sockfd,recvbuf,BUFSIZE,0);
                printf("recv back:%s
    
    ", 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, '', BUFSIZE );
            memset( recvbuf, '', 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
    ");
                    continue;
                }
                else
                    fprintf(stderr,
                            "recv error");
            }
            else{
                printf("recv back:%s
    
    ", recvbuf);
            }
        }
        close( sockfd );
        return;
    }

    运行截图:

    image

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


     

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

     

     

    参考资料:

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

  • 相关阅读:
    [SAM学习笔记]
    CF513G3 Inversions problem
    AtCoder Beginner Contest 204
    [SDOI2017]序列计数
    CF993E Nikita and Order Statistics
    多项式板子
    多项式杂学笔记
    「雅礼集训 2017 Day5」远行
    Mysql备份恢复工具
    个人选择上网的流量方式对比
  • 原文地址:https://www.cnblogs.com/suzhou/p/serv13socktimeout.html
Copyright © 2011-2022 走看看