网络超时检测方法

超时检测的必要性:避免进程在没有数据时无限制地阻塞,当设定的时间到时,进程从原操作返回继续运行。

方法(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() 函数,只对它后面的第一个正在发生阻塞的函数有效,使其不阻塞。

 

posted @ 2015-12-07 15:13  文纸  阅读(890)  评论(0编辑  收藏  举报