Unix网络编程 高级IO套接字设置超时
我们知道。对于一个套接字的读写(read/write)操作默认是堵塞的。假设当前套接字还不可读/写,那么这个操作会一直堵塞下去,这样对于一个须要高性能的server来说,是不能接受的。所以,我们能够在进行读写操作的时候能够指定超时值,这样就读写操作就不至于一直堵塞下去。
在涉及套接字的I/O操作上设置超时的方法有三种:
1:调用alarm,它在指定的超时期满时产生SIGALRM信号。这种方法涉及信号处理,而信号处理在不同的实现上存在差异,并且可能干扰进程中现有的alarm调用。
2:在select中堵塞等待I/O(select有内置的时间限制),依次取代直接堵塞在read或write调用上。(linux2.6以后的内核也能够使用epoll的epoll_wait)。
>>>>使用SIGALRM为connect设置超时:
>>>>使用SIGALRM为recvfrom设置超时:
>>>>使用select为recvfrom设置超时:
所以上面dig_cli函数结合readable_timeo函数设置超时的程序就可变为:(堵塞在select上)
>>>>使用SO_RCVTIMEO套接字选项为recvfrom设置超时:
在涉及套接字的I/O操作上设置超时的方法有三种:
1:调用alarm,它在指定的超时期满时产生SIGALRM信号。这种方法涉及信号处理,而信号处理在不同的实现上存在差异,并且可能干扰进程中现有的alarm调用。
2:在select中堵塞等待I/O(select有内置的时间限制),依次取代直接堵塞在read或write调用上。(linux2.6以后的内核也能够使用epoll的epoll_wait)。
3:使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这种方法的问题在于并不是全部的实现都支持这两个套接字选项。
上述这三个技术都适用于输入和输出操作(read、write。及其变体recv/send, readv/writev, recvfrom, sendto)。
只是我们也期待能够用于connect的技术,由于TCP内置的connect超时相当长(典型值为75秒),而我们在写server程序的时候,也不会希望一个连接的建立须要花费这么长时间。
select可用来在connect上设置超时的先决条件是对应的套接字是非堵塞的。而那两个套接字选项对connect并不适用。同一时候也应当指出,前两个技术适用于不论什么描写叙述符。而第三个技术只适用于套接字描写叙述符。
>>>>使用select对connect设置超时:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <sys/select.h> #include <time.h> #define PORT 9900 #define MAXDATASIZE 5000 int main(int argc, char **argv) { int sockfd, nbytes; char buf[1024]; struct hostent *he; struct sockaddr_in servaddr; if(argc != 2) { perror("Usage:client hostname\n"); return 0; } if((he = gethostbyname(argv[1])) == NULL) { perror("gethostbyname"); return 0; } if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("create socket error"); return 0; } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); servaddr.sin_addr = *((struct in_addr *)he->h_addr); fcntl(sockfd, F_SETFL, O_NONBLOCK); timeval timeout = {3, 0}; if(connect(sockfd, (SA*)&servaddr, sizeof(struct sockaddr)) == -1) { if(errno != EINPROGRESS) { close(sockfd); perror("connect error"); return 0; } } fd_set readSet; FD_ZERO(&readSet); FD_ZERO(&writeSet); FD_SET(sockfd, &writeSet); int ret = select(sockfd+1, &readSet, &writeSet, NULL, &timeout); printf("%d", ret); }
>>>>使用SIGALRM为connect设置超时:
#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); 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 = ETIMEOUT; } alarm(0); /* turn off the alarm */ Signal(SIGALRM, sigfunc); /* restore previous signal handler */ return (n); } static void connect_alarm(int signo) { return ; /* just interrupt the connect() */ }
>>>>使用SIGALRM为recvfrom设置超时:
#include <unp.h> static void sig_alarm(int); void dig_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE+1]; Signal(SIGALRM, sig_alarm); while(Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen); alarm(5); /* set TIMEOUT 5 seconds */ if((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { if(errno == EINTR) fprintf(stderr, "socket timeout\n"); else err_sys("recvfrome error"); }else { alarm(0); /* if success then turn off the alarm */ recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); } } } static void sig_alarm(int signo) { return ; /* just interrupt the recvfrom() */ }
>>>>使用select为recvfrom设置超时:
#include <unp.h> int readable_timeo(int fd, int sec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); /* reset the file discriptor set */ FD_SET(fd, &rset); tv.tv_sec = sec; /* set struct timeval */ tv.tv_usec = 0; return (select(fd+1, &rset, NULL, NULL, &tv)); }
所以上面dig_cli函数结合readable_timeo函数设置超时的程序就可变为:(堵塞在select上)
#include <unp.h> void dig_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, preservaddr, serlen); if(readable_timeo(sockfd, 5) == 0) { fprintf(stderr, "socket timeout\n"); } else { n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); } } }
>>>>使用SO_RCVTIMEO套接字选项为recvfrom设置超时:
#include <unp.h> void dig_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, &tv, sizeof(tv)); while(Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, preservaddr, serlen); n = 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; /* null terminate */ Fputs(recvline, stdout); } }