给套接字设置超时检测

在涉及套接字I/O操作上设置超时的方法有以下3种。

  1. 调用alarm, 它在指定超时期满时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
  2. 在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上。
  3. 使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于并非所有实现都支持这两个套接字选项。

select可用在connect上设置超时的先决条件是相应套接字处于非阻塞模式,而那两个套接字选项对connect并不适用。前两个技术适用于任何描述符,而第三个技术仅使用于套接字描述符。

 

使用SIGALRM为connect设置超时

先给出源码:

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 ETIMEDOUT;

    }

    alarm(0); 

    Signal(SIGALRM, sigfunc); 

 

    return(n);

}

 

static void

connect_alarm(int signo)

{

    return; 

}

 

本进程的报警时钟设置成由调用者指定的秒数。如果当前已经给本进程设置过报警时钟,那么alarm的返回值是这个报警时钟的当前剩余秒数,且返回一个警告消息,因为我们推翻了先前设置的报警时钟,否则alarm的返回值为0。

调用connect,如果本调用被中断(即返回EINTR错误),那就把errno的值改设为ETIMEOUT,同时关闭套接字,以防三路握手继续进行。

给alarm函数指定参数0,关闭本进程的报警时钟,同时恢复原来的信号处理函数(如果有的话)。

 

使用SIGALRM为recvfrom设置超时

static void sig_alrm(int);

 

void

dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)

{

    int n;

    char sendline[MAXLINE], recvline[MAXLINE 1];

 

    Signal(SIGALRM, sig_alrm);

 

    while (Fgets(sendline, MAXLINE, fp) != NULL) {

 

        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

 

        alarm(5);

        if (n recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) 0) {

            if (errno == EINTR)

                fprintf(stderr, "socket timeout\n");

            else

                err_sys("recvfrom error");

        else {

            alarm(0);

            recvline[n] 0; 

            Fputs(recvline, stdout);

        }

    }

}

 

static void

sig_alrm(int signo)

{

    return; 

}

 

为SIGALRM建立一个信号处理函数,并在每次调用recvfrom前调用alarm设置一个5秒钟的超时。如果recvfrom被我们的信号处理函数中断了,那就输出一个信息并继续执行。如果读到一行来自服务器的文本,那就关掉报警时钟并输出服务器的应答。

 

使用select为recvfrom设置超时

下面这个readable_timeo函数等待一个描述符最多在指定的秒数内变为可读。

 

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));

    

}

 

int

Readable_timeo(int fd, int sec)

{

    int n;

 

    if (n readable_timeo(fd, sec)) 0)

        err_sys("readable_timeo error");

    return(n);

}

 

select等待该描述符变为可读,或者超时。本函数的返回值就是select的返回值;

出错为-1,超时发生时为0,否则返回的正值给出已就绪描述符的数目。

 

下面是dg_cli函数的原型:

void

dg_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, pservaddr, servlen);

 

        if (Readable_timeo(sockfd, 5) == 0) {

            fprintf(stderr, "socket timeout\n");

        else {

            Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

            recvline[n] 0; 

            Fputs(recvline, stdout);

        }

    }

}

该函数调用Readable_timeo函数,当返回值大于0时才调用recvfrom,确保recvfrom不会阻塞。

 

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

本选项一旦设置到某个描述符(包括指定超时值),其超时设置将应用于该描述符的所有读操作。本方法的优势就体现在一次性设置选项上,而前两个方法总是要求我们在欲设置时间限制的每个操作发生之前做些工作。本套接字选项仅仅应用于读操作,类似的SO_SNDTIMEO选项则仅仅应用于写操作,两者都不能用于为connect设置超时。

下面是使用SO_RCVTIMEO套接字选项的另一个版本的dg_cli函数。

void

dg_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, SO_RCVTIMEO, &tv, sizeof(tv));

 

    while (Fgets(sendline, MAXLINE, fp) != NULL) {

 

        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

 

        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; 

        Fputs(recvline, stdout);

    }

}

如果I/O操作超时,其函数(这里是recvfrom)将返回一个EWOULDBLOCK错误。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值