调用alarm,它在指定超时期满时产生SIGALRM信号。
(1)使用SIGALRM为connect设置超时:因为在多线程程序中处理信号非常困难,因此建议只是在未线程化程序 或 单线程化程序中使用该技术。
/* include connect_timeo */
#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); //为SIGALRM建立一个信号处理函数
if (alarm(nsec) != 0) //调用alarm,设置调用者指定的秒数
err_msg("connect_timeo: alarm was already set");
if ( (n = connect(sockfd, saptr, salen)) < 0) { //调用connect
close(sockfd);
if (errno == EINTR)
errno = ETIMEDOUT;
}
alarm(0); /* turn off the alarm */ //关闭本进程的报警时钟
Signal(SIGALRM, sigfunc); /* restore previous signal handler */ // 恢复原来的信号处理函数
return(n);
}
static void connect_alarm(int signo) //为SIGALRM建立的信号处理函数
{
return; /* just interrupt the connect() */
}
void Connect_timeo_demo(int fd, const SA *sa, socklen_t salen, int sec)
{
if (connect_timeo(fd, sa, salen, sec) < 0) //调用connect_timeo
err_sys("connect_timeo error");
}
#include "unp.h"
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); // 为SIGALRM建立一个信号处理函数
while (Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
alarm(5); // 设置一个5秒钟超时
if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { //如果recvfrom被我们的信号处理函数中断
if (errno == EINTR)
fprintf(stderr, "socket timeout\n");
else
err_sys("recvfrom error");
} else { //如果读到来自服务器的一行文本
alarm(0); //关闭本进程的报警时钟
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
static void sig_alrm(int signo) // 为SIGALRM建立的信号处理函数
{
return; /* just interrupt the recvfrom() */
}
将I/O阻塞在select调用上,以此代替直接阻塞在read或者write调用上,并设置select内置时间限制。
#include "unp.h"
int
readable_timeo(int fd, int sec)
{
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(fd, &rset); // 在描述符集只中打开给定的描述符
tv.tv_sec = sec; // 把要设置的等待秒数设置在timeval结构中
tv.tv_usec = 0;
return(select(fd+1, &rset, NULL, NULL, &tv)); //等待描述符变为可读,或者发生超时
/* 4> 0 if descriptor is readable */
}
/* end readable_timeo */
int
Readable_timeo(int fd, int sec)
{
int n;
if ( (n = readable_timeo(fd, sec)) < 0) // 调用readable_timeo
err_sys("readable_timeo error");
return(n);
}
//使用、调用示例:
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"); //调用Readable_timeo,直到Readable_timeo告知所关注的描述符变为可读,才调用recvfrom,用来保证recvfrom不会被阻塞
} else {
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
}
}
(1)使用SO_RCVTIMEO套接字选项为recvfrom设置超时
本方法的优势在于一次设置,一旦设置到某个描述符,其超时设置将应用于该描述符上的所有读操作。
#include "unp.h"
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)); //设置SO_RCVTIMEO套接字选项,并指向timeval结构tv
while (Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
if (n < 0) {
if (errno == EWOULDBLOCK) { //如果I/O操作超时,则recvfrom将返回一个EWOULDBLOCK错误
fprintf(stderr, "socket timeout\n");
continue;
} else
err_sys("recvfrom error");
}
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
}