本章讨论我们笼统地归为“高级I/O”的各个函数和技术
套接字超时
有3种方法在涉及套接字的I/O操作上设置超时
1.调用alarm,它在指定超时时期满时产生SIGALRM信号
2.在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上
3.使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
使用SIGALRM为connect设置超时
下面给出我们的connect_timeo函数,它以调用者指定的超时上限调用connect
1 /* include connect_timeo */ 2 #include "unp.h" 3 4 static void connect_alarm(int); 5 6 int 7 connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec) 8 { 9 Sigfunc *sigfunc; 10 int n; 11 12 sigfunc = Signal(SIGALRM, connect_alarm); 13 if (alarm(nsec) != 0) 14 err_msg("connect_timeo: alarm was already set"); 15 16 if ( (n = connect(sockfd, saptr, salen)) < 0) { 17 close(sockfd); 18 if (errno == EINTR) 19 errno = ETIMEDOUT; 20 } 21 alarm(0); /* turn off the alarm */ 22 Signal(SIGALRM, sigfunc); /* restore previous signal handler */ 23 24 return(n); 25 } 26 27 static void 28 connect_alarm(int signo) 29 { 30 return; /* just interrupt the connect() */ 31 } 32 /* end connect_timeo */
如果connect被中断就会返回EINTR错误。
使用SIGALRM为recvfrom设置超时
1 #include "unp.h" 2 3 static void sig_alrm(int); 4 5 void 6 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 7 { 8 int n; 9 char sendline[MAXLINE], recvline[MAXLINE + 1]; 10 11 Signal(SIGALRM, sig_alrm); 12 13 while (Fgets(sendline, MAXLINE, fp) != NULL) { 14 15 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); 16 17 alarm(5); 18 if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { 19 if (errno == EINTR) 20 fprintf(stderr, "socket timeout "); 21 else 22 err_sys("recvfrom error"); 23 } else { 24 alarm(0); 25 recvline[n] = 0; /* null terminate */ 26 Fputs(recvline, stdout); 27 } 28 } 29 } 30 31 static void 32 sig_alrm(int signo) 33 { 34 return; /* just interrupt the recvfrom() */ 35 }
使用select为recvfrom设置超时
1 /* include readable_timeo */ 2 #include "unp.h" 3 4 int 5 readable_timeo(int fd, int sec) 6 { 7 fd_set rset; 8 struct timeval tv; 9 10 FD_ZERO(&rset); 11 FD_SET(fd, &rset); 12 13 tv.tv_sec = sec; 14 tv.tv_usec = 0; 15 16 return(select(fd+1, &rset, NULL, NULL, &tv)); 17 /* 4> 0 if descriptor is readable */ 18 } 19 /* end readable_timeo */
该函数等待一个描述符变为可读或者发生超时(超时返回0)
我们可以使用该函数来改写上面的dg_cli函数
1 #include "unp.h" 2 3 void 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 5 { 6 int n; 7 char sendline[MAXLINE], recvline[MAXLINE + 1]; 8 9 while (Fgets(sendline, MAXLINE, fp) != NULL) { 10 11 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); 12 13 if (Readable_timeo(sockfd, 5) == 0) { 14 fprintf(stderr, "socket timeout "); 15 } else { 16 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); 17 recvline[n] = 0; /* null terminate */ 18 Fputs(recvline, stdout); 19 } 20 } 21 }
直到readable_timeo告知所关注的描述符已变为可读后我们才调用recvfrom(超时就打印错误)
使用SO_RCVTIMEO套接字选项为recvfrom设置超时
下面是使用SO_RCVTIMEO套接字选项的另一个版本的dg_cli函数
1 #include "unp.h" 2 3 void 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 5 { 6 int n; 7 char sendline[MAXLINE], recvline[MAXLINE + 1]; 8 struct timeval tv; 9 10 tv.tv_sec = 5; 11 tv.tv_usec = 0; 12 Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 13 14 while (Fgets(sendline, MAXLINE, fp) != NULL) { 15 16 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); 17 18 n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); 19 if (n < 0) { 20 if (errno == EWOULDBLOCK) { 21 fprintf(stderr, "socket timeout "); 22 continue; 23 } else 24 err_sys("recvfrom error"); 25 } 26 27 recvline[n] = 0; /* null terminate */ 28 Fputs(recvline, stdout); 29 } 30 }
如果I/O操作超时,recvfrom将返回一个EWOULDBLOCK错误
read和write函数的变体
recv和send允许通过第四个参数从进程到内核传递标志;
readv和writev允许指定往其中输入数据或从其中输出数据的缓冲区向量;
recvmsg和sendmsg结合了其他I/O的所有特性,并具有接收和发送辅助数据的新能力
可以查看之前apue的学习笔记 http://www.cnblogs.com/runnyu/p/4648678.html