zoukankan      html  css  js  c++  java
  • Unix网络编程 高级IO套接字设置超时

    我们知道。对于一个套接字的读写(read/write)操作默认是堵塞的。假设当前套接字还不可读/写,那么这个操作会一直堵塞下去,这样对于一个须要高性能的server来说,是不能接受的。所以,我们能够在进行读写操作的时候能够指定超时值,这样就读写操作就不至于一直堵塞下去。

    在涉及套接字的I/O操作上设置超时的方法有三种:

        1:调用alarm,它在指定的超时期满时产生SIGALRM信号。这种方法涉及信号处理,而信号处理在不同的实现上存在差异,并且可能干扰进程中现有的alarm调用。

        2:在select中堵塞等待I/O(select有内置的时间限制),依次取代直接堵塞在read或write调用上。(linux2.6以后的内核也能够使用epoll的epoll_wait)。



        3:使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这种方法的问题在于并不是全部的实现都支持这两个套接字选项。

      上述这三个技术都适用于输入和输出操作(read、write。及其变体recv/send, readv/writev, recvfrom, sendto)。

    只是我们也期待能够用于connect的技术,由于TCP内置的connect超时相当长(典型值为75秒),而我们在写server程序的时候,也不会希望一个连接的建立须要花费这么长时间。

    select可用来在connect上设置超时的先决条件是对应的套接字是非堵塞的。而那两个套接字选项对connect并不适用。同一时候也应当指出,前两个技术适用于不论什么描写叙述符。而第三个技术只适用于套接字描写叙述符。


    >>>>使用select对connect设置超时:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/select.h>
    #include <time.h>
    #define PORT 9900
    #define MAXDATASIZE 5000
    
    int main(int argc, char **argv)
    {
        int    sockfd, nbytes;
        char   buf[1024];
        struct hostent *he;
        struct sockaddr_in servaddr;
    
        if(argc != 2) {
            perror("Usage:client hostname
    ");
            return 0;
        }
        if((he = gethostbyname(argv[1])) == NULL) {
            perror("gethostbyname");
            return 0;
        }
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("create socket error");
            return 0;
        }
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORT);
        servaddr.sin_addr = *((struct in_addr *)he->h_addr);
        fcntl(sockfd, F_SETFL, O_NONBLOCK);
        timeval timeout = {3, 0};
        if(connect(sockfd, (SA*)&servaddr, sizeof(struct sockaddr)) == -1) {
            if(errno != EINPROGRESS) {
                close(sockfd);
                perror("connect error");
                return 0;
            }
        }
     
        fd_set readSet;
        FD_ZERO(&readSet);
        FD_ZERO(&writeSet);
        FD_SET(sockfd, &writeSet);
        int ret = select(sockfd+1, &readSet, &writeSet, NULL, &timeout);
        printf("%d", ret);    
    }  

    >>>>使用SIGALRM为connect设置超时:

    #include <unp.h>
    
    static void connect_alarm(int);
    
    int
    connect_timeo(int sockfd, const SA* saptr, socklen_t salen, int nsec)
    {
        Sigfunc* sigfunc;
        int      n;
        
        sigfunc = Signal(SIGALRM, connect_alarm);
        if(alarm(nsec) != 0) 
            err_msg("connect_timeo: alarm was already set");
        if((n = connect(sockfd, saptr, salen)) < 0) {
            close(sockfd);
            if(errno == EINTR)
                errno = ETIMEOUT;
        }
        alarm(0);     /* turn off the alarm */
        Signal(SIGALRM, sigfunc);   /* restore previous signal handler */
        
        return (n);
    }
    
    static void
    connect_alarm(int signo)
    {
        return ;   /* just interrupt the connect() */
    }

    >>>>使用SIGALRM为recvfrom设置超时:

    #include <unp.h>
    
    static void sig_alarm(int);
    
    void
    dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
    {
        int  n;
        char sendline[MAXLINE], recvline[MAXLINE+1];
        
        Signal(SIGALRM, sig_alarm);
        
        while(Fgets(sendline, MAXLINE, fp) != NULL) {
            Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
            alarm(5);   /* set TIMEOUT 5 seconds */
            if((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
                if(errno == EINTR)
                    fprintf(stderr, "socket timeout
    ");
                else 
                    err_sys("recvfrome error");
            }else {
                alarm(0);  /* if success then turn off the alarm */
                recvline[n] = 0;  /* null terminate */
                Fputs(recvline, stdout);
            }
        }
    }
    
    static void
    sig_alarm(int signo)
    {
        return ;   /* just interrupt the recvfrom() */
    }

    >>>>使用select为recvfrom设置超时:

    #include <unp.h>
    
    int
    readable_timeo(int fd, int sec)
    {
        fd_set   rset;
        struct   timeval tv;
        
        FD_ZERO(&rset);   /* reset the file discriptor set */
        FD_SET(fd, &rset);
        
        tv.tv_sec = sec;  /* set struct timeval */
        tv.tv_usec = 0;
        
        return (select(fd+1, &rset, NULL, NULL, &tv));
    }

    所以上面dig_cli函数结合readable_timeo函数设置超时的程序就可变为:(堵塞在select上)

    #include <unp.h>
    
    void
    dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
    {
        int  n;
        char sendline[MAXLINE], recvline[MAXLINE+1];
        
        while(Fgets(sendline, MAXLINE, fp) != NULL) {
            Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
            if(readable_timeo(sockfd, 5) == 0) {
                fprintf(stderr, "socket timeout
    ");
            } else {
                n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
                recvline[n] = 0;  /* null terminate */
                Fputs(recvline, stdout);
            }
        }
    }
    

    >>>>使用SO_RCVTIMEO套接字选项为recvfrom设置超时:

    #include <unp.h>
    
    void
    dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
    {
        int    n;
        char   sendline[MAXLINE], recvline[MAXLINE+1];
        struct timeval tv;
        
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        Setsockopt(sockfd, SOL_SOCKET, &tv, sizeof(tv));
        
        while(Fgets(sendline, MAXLINE, fp) != NULL) {
                
            Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen);
            
            n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
            if(n < 0) {
                if(errno == EWOULDBLOCK) {
                    fprintf(stderr, "socket timeout
    ");
                    continue;
                }else 
                    err_sys("recvfrom error");
            }
                
            recvline[n] = 0;  /* null terminate */
            Fputs(recvline, stdout);
        }
    }
    





  • 相关阅读:
    Zlib编译
    最新Webstrom, Idea 2019.2.3 的激活
    图像理解与深度学习开篇
    C# 反射(Reflection)
    SpringMVC中使用forward和redirect进行转发和重定向以及重定向时如何传参详解
    Navicat for Oracle中如何使用外键
    【数据库】主键,外键,主表,从表,关联表,父表,子表
    onclick事件没有反应的五种可能情况
    button小手设置 css的cursor
    Spring MVC F5刷新问题
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/6881635.html
Copyright © 2011-2022 走看看