UNP卷1:第十四章(高级I/O函数)

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)


你可能感兴趣的:(unix,UNIX网络编程卷1)