1. 套接字超时
在涉及到套接字的I/O操作上设置超时的方法有以下三种:(1)调用alarm,它在指定超时期满时产生SIGALRM信号。这种方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
(2)在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上。
(3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
1)使用SIGALRM为connect设置超时
static void connect_alarm(int);
int connect_timeo(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec)
{
Sigfunc *sigfunc;
int n;
sigfunc = signal(SIGALRM, connect_alarm);
//说明已经超时了,则直接调用connect_alarm函数,返回
if (alarm(nsec) != 0){
printf("connect_time:alarm was already set");
exit(-1);
}
if ((n = connect(sockfd, saptr, salen)) < 0){
close(sockfd);
//如果是中断,则将错误设置为超时
if (errno == EINTR)
errno = ETIMEDOUT;
}
alarm(0);//关闭超时
signal(SIGALRM,sigfunc);//原始的超时函数(即当nsec的超时时间设置大于connect本身的超时时间,而connect本身产生超时的时候,调用此函数)
return (n);
}
static void connect_alarm(int signo)
{
return;
}
2)使用SIGALRM为recvfrom设置超时
static void sig_alrm(int);
void dg_cli(FILE *fp, int sockfd, const struct sockaddr *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{
printf("recvfrom error\n");
}
} else {
alarm(0);
recvline[n] = 0;
fputs(recvline, stdout);
}
}
}
static void sig_alrm(int)
{
return;
}
3)使用select为recvfrom设置超时
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)); //select函数也可简单的用作定时器函数
}
void dg_cli(FILE *fp, int sockfd, const struct sockaddr *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{
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0;
fputs(recvline, stdout);
}
}
}
4)使用SO_RCVTIMEO套接字选项为recvfrom设置超时
void dg_cli(FILE *fp, int sockfd, const struct sockaddr *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);
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
if (n < 0){
if (errno == EWOULDBLOCK){
fprintf(stderr, "socket timeout\n")l
continue;
} else{
printf("recvfrom error\n");
exit(-1);
}
}
}
recvline[n] = 0;
fputs(recvline, stdout);
}
2. read和write函数的三大变体
recv和send允许通过第四个参数从进程到内核传递标志;readv和writev允许指定往其中输入数据或从其中输出数据的缓冲区向量;recvmsg和sendmsg结合了其他I/O函数的所有特性。(函数具体说明请参考APUE和UNP卷1)