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套接字選項。這種方法的問題在於並非所有的實現都支持這兩個套接字選項。

linux

  上述這三個技術都適用於輸入和輸出操做(read、write。及其變體recv/send, readv/writev, recvfrom, sendto)。socket

只是咱們也期待可以用於connect的技術,因爲TCP內置的connect超時至關長(典型值爲75秒),而咱們在寫server程序的時候,也不會但願一個鏈接的創建需要花費這麼長時間。函數

select可用來在connect上設置超時的先決條件是對應的套接字是非堵塞的。而那兩個套接字選項對connect並不適用。同一時候也應當指出,前兩個技術適用於不論什麼描寫敘述符。而第三個技術只適用於套接字描寫敘述符。post


>>>>使用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\n");
        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\n");
            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\n");
        } 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\n");
                continue;
            }else 
                err_sys("recvfrom error");
        }
            
        recvline[n] = 0;  /* null terminate */
        Fputs(recvline, stdout);
    }
}
相關文章
相關標籤/搜索