标签:
超时检测的必要性:避免进程在没有数据时无限制地阻塞,当设定的时间到时,进程从原操作返回继续运行。
方法(1):使用setsockopt函数
时间结构体 struct timeval tv;
可设定
tv.tv_sec = 5; // 设置5秒时间
tv.tv_usec = 0;
然后设置超时选项
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
注意:将进程中和sockfd相关的阻塞,变为非阻塞。
实例代码:
server.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<unistd.h> 6 #include<netinet/in.h> 7 #include<arpa/inet.h> 8 #include<string.h> 9 #include<errno.h> 10 11 #define N 64 12 #define err_log(log) do{ perror(log); exit(1);}while(0) 13 14 int main(int argc, const char *argv[]) 15 { 16 int sockfd, connectfd; 17 char buf[N]; 18 struct sockaddr_in serveraddr, clientaddr; 19 socklen_t len = sizeof(clientaddr); 20 struct timeval tv; 21 socklen_t optlen = sizeof(tv); 22 23 if(argc != 3) 24 { 25 fprintf(stderr, "Usage:%s serverip port", argv[0]); 26 return -1; 27 } 28 29 sockfd = socket(AF_INET, SOCK_STREAM, 0); 30 if(sockfd < 0) 31 err_log("fail to sockfd"); 32 33 serveraddr.sin_family = AF_INET; 34 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 35 serveraddr.sin_port = htons(atoi(argv[2])); 36 37 tv.tv_sec = 5; 38 tv.tv_usec = 0; 39 if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, optlen) < 0) 40 err_log("fail to setsockopt"); 41 42 if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 43 err_log("fail to bind"); 44 45 if(listen(sockfd, 5) < 0) 46 err_log("fail to listen"); 47 48 if((connectfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len)) < 0) 49 { 50 if(errno == 11) 51 { 52 printf("errno = %d--->%s\n", errno, strerror(errno)); 53 } 54 else 55 { 56 err_log("fail to accept"); 57 } 58 } 59 printf("client:%s--->%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 60 while(1) 61 { 62 if(recv(connectfd, buf, N, 0) < 0) 63 err_log("fail to recv"); 64 if(strncmp(buf, "quit", 4) == 0) 65 break; 66 buf[strlen(buf) - 1] = ‘\0‘; 67 printf("buf:%s\n", buf); 68 } 69 close(connectfd); 70 close(sockfd); 71 return 0; 72 }
client.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<unistd.h> 6 #include<arpa/inet.h> 7 #include<netinet/in.h> 8 #include<string.h> 9 #include<errno.h> 10 11 #define N 64 12 #define err_log(log) do{perror(log); exit(1);}while(0) 13 int main(int argc, const char *argv[]) 14 { 15 int sockfd; 16 char buf[N]; 17 struct sockaddr_in serveraddr; 18 19 if(argc != 3) 20 { 21 fprintf(stderr, "Usage:%s serverip port.", argv[0]); 22 return -1; 23 } 24 25 sockfd = socket(AF_INET, SOCK_STREAM, 0); 26 if(sockfd < 0) 27 err_log("fail to sockfd"); 28 29 serveraddr.sin_family = AF_INET; 30 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 31 serveraddr.sin_port = htons(atoi(argv[2])); 32 33 if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 34 err_log("fail to connect"); 35 36 while(1) 37 { 38 printf("<client>"); 39 fgets(buf, N, stdin); 40 if(send(sockfd, buf, N, 0) < 0) 41 err_log("fail to send"); 42 if(strncmp(buf, "quit", 4) == 0) 43 break; 44 printf("buf:%s\n", buf); 45 } 46 close(sockfd); 47 return 0; 48 }
方法(2)使用select函数
时间结构体 struct timeval tv;
可设定
tv.tv_sec = 5; // 设置5秒时间
tv.tv_usec = 0;
select(sockfd, maxfd+1, &readfds, NULL, NULL, &tv);
注意:每次超时检测执行完毕之后,tv就会被清零,需要重新赋值。
示例代码:
server.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<arpa/inet.h> 6 #include<netinet/in.h> 7 #include<string.h> 8 #include<fcntl.h> 9 #include<unistd.h> 10 #include<sys/select.h> 11 #include<sys/time.h> 12 13 #define N 64 14 int main(int argc, const char *argv[]) 15 { 16 int sockfd, connectfd, maxfd; 17 char buf[N]; 18 struct sockaddr_in serveraddr, clientaddr; 19 fd_set readfds; 20 socklen_t len = sizeof(clientaddr); 21 struct timeval tv; 22 int ret, i = 0; 23 24 if(argc != 3) 25 { 26 fprintf(stderr, "Usage:%s serverip port", argv[1]); 27 return -1; 28 } 29 sockfd = socket(AF_INET, SOCK_STREAM, 0); 30 if(sockfd < 0) 31 { 32 perror("fail to sockfd"); 33 return -1; 34 } 35 serveraddr.sin_family = AF_INET; 36 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 37 serveraddr.sin_port = htons(atoi(argv[2])); 38 if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 39 { 40 perror("fail to bind"); 41 return -1; 42 } 43 if(listen(sockfd, 5) < 0) 44 { 45 perror("fail to listen"); 46 return -1; 47 } 48 printf("sockfd = %d\n", sockfd); 49 maxfd = sockfd; 50 while(1) 51 { 52 FD_ZERO(&readfds); 53 FD_SET(0, &readfds); 54 FD_SET(sockfd, &readfds); 55 56 tv.tv_sec = 5; 57 tv.tv_usec = 0; 58 59 ret = select(maxfd + 1, &readfds, NULL, NULL, &tv); 60 switch(ret) 61 { 62 case -1: 63 perror("fail to select"); 64 break; 65 case 0: 66 printf("timeout--->ret = %d\n", ret); 67 break; 68 default: 69 for(i = 0; i < maxfd + 1; i++) 70 { 71 if(FD_ISSET(i, &readfds)) 72 { 73 if(i == 0) 74 { 75 read(i, buf, N); 76 printf("buf:%s\n", buf); 77 } 78 else 79 { 80 if((connectfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len)) < 0) 81 { 82 perror("fail to connectfd"); 83 return -1; 84 } 85 printf("client:%s--->%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 86 close(connectfd); 87 } 88 } 89 } 90 } 91 } 92 return 0; 93 }
cient.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<arpa/inet.h> 6 #include<netinet/in.h> 7 #include<string.h> 8 #include<fcntl.h> 9 #include<unistd.h> 10 11 #define N 64 12 int main(int argc, const char *argv[]) 13 { 14 int sockfd; 15 struct sockaddr_in serveraddr; 16 17 if(argc != 3) 18 { 19 fprintf(stderr, "Usage:%s serverip port.", argv[0]); 20 return -1; 21 } 22 sockfd = socket(AF_INET, SOCK_STREAM, 0); 23 if(sockfd < 0) 24 { 25 perror("fail to sockfd"); 26 return -1; 27 } 28 serveraddr.sin_family = AF_INET; 29 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 30 serveraddr.sin_port = htons(atoi(argv[2])); 31 if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 32 { 33 perror("fail to connect"); 34 return -1; 35 } 36 printf("client exit...\n"); 37 close(sockfd); 38 return 0; 39 }
方法(3)使用sigaction函数
示例代码:
server.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<string.h> 6 #include<signal.h> 7 #include<netinet/in.h> 8 #include<arpa/inet.h> 9 #include<unistd.h> 10 #include<errno.h> 11 #include<fcntl.h> 12 13 #define N 64 14 #define err_log(log) do{ perror(log); exit(1);}while(0) 15 void handler(int signo) 16 { 17 printf("timeout.\n"); 18 } 19 int main(int argc, const char *argv[]) 20 { 21 int sockfd, connectfd; 22 char buf[N]; 23 struct sockaddr_in serveraddr, clientaddr; 24 struct sigaction act; 25 socklen_t len = sizeof(clientaddr); 26 27 if(argc != 3) 28 { 29 fprintf(stderr, "Usage:%s serverip port.", argv[0]); 30 return -1; 31 } 32 33 sockfd = socket(AF_INET, SOCK_STREAM, 0 ); 34 if(sockfd < 0) 35 err_log("fail to sockfd"); 36 37 serveraddr.sin_family = AF_INET; 38 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 39 serveraddr.sin_port = htons(atoi(argv[2])); 40 41 if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 42 err_log("fail to bind"); 43 44 if(listen(sockfd, 5) < 0) 45 err_log("fail to listen"); 46 47 printf("sockfd = %d\n", sockfd); 48 49 sigaction(SIGALRM, NULL, &act); 50 act.sa_handler = handler; 51 act.sa_flags &= ~SA_RESTART; 52 sigaction(SIGALRM, &act, NULL); 53 54 memset(&clientaddr, 0, sizeof(clientaddr)); 55 56 if((connectfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len)) < 0) 57 { 58 if(errno == 4) 59 { 60 printf("errno = %d--->%s\n", errno, strerror(errno)); 61 } 62 else 63 { 64 err_log("fail to accept"); 65 } 66 } 67 68 printf("client:%s--->%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 69 while(1) 70 { 71 alarm(5); 72 if(recv(connectfd, buf, N, 0) < 0) 73 { 74 if(errno == 4) 75 { 76 printf("errno = %d--->%s\n", errno, strerror(errno)); 77 } 78 else 79 { 80 err_log("fail to recv"); 81 } 82 } 83 84 if(recv(connectfd, buf, N, 0) < 0) 85 { 86 if(errno == 4) 87 { 88 printf("errno = %d--->%s\n", errno, strerror(errno)); 89 } 90 else 91 { 92 err_log("fail to recv"); 93 } 94 } 95 96 } 97 close(connectfd); 98 return 0; 99 }
client.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<unistd.h> 6 #include<string.h> 7 #include<arpa/inet.h> 8 #include<netinet/in.h> 9 10 #define N 64 11 #define err_log(log) do{perror(log); exit(1);}while(0) 12 int main(int argc, const char *argv[]) 13 { 14 int sockfd; 15 struct sockaddr_in serveraddr; 16 char buf[N]; 17 18 if(argc != 3) 19 { 20 fprintf(stderr, "Usage:%s serverip port", argv[0]); 21 return -1; 22 } 23 24 sockfd = socket(AF_INET, SOCK_STREAM, 0); 25 if(sockfd < 0) 26 err_log("fail to sockfd"); 27 28 serveraddr.sin_family = AF_INET; 29 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 30 serveraddr.sin_port = htons(atoi(argv[2])); 31 32 if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 33 err_log("fail to connect"); 34 35 while(1) 36 { 37 printf("<client>"); 38 fgets(buf, N, stdin); 39 if(send(sockfd, buf, N, 0) < 0) 40 err_log("fail to send"); 41 } 42 printf("client exit...\n"); 43 close(sockfd); 44 return 0; 45 }
对于sa_flags选项的SA_RESTART是自重启属性,默认为真。
注意:
SA_RESTART 自重启属性为真。进程执行时,信号产生了,执行信号处理函数,处理完毕之后,回到程序原来的地方,接着往下执行。
当进程正在执行系统调用(阻塞),此时信号产生了,执行信号处理函数,完毕之后,回到进程原来执行的地方(系统调用)继续执行,那么又来执行系统调用就阻塞。不能实现超时检测。
自重启属性为假,当进程正在执行系统调用(阻塞),此时信号产生了,执行信号处理函数,完毕之后,回到进程原来执行的地方(系统调用),
系统调用失败了,进程向下执行(不阻塞)。
alarm() 函数,只对它后面的第一个正在发生阻塞的函数有效,使其不阻塞。
标签:
原文地址:http://www.cnblogs.com/yangziwen0709/p/5026158.html