Linux检测网络故障以及恢复网络的方法

2019-07-13 05:36发布

原文地址:http://www.linuxidc.com/Linux/2011-08/40336.htm 在项目中遇到一个问题,嵌入式Linux设备工作一段时间后网络会出现故障,网线虽然连着,但却不能与外部主机通信。此时用串口调试内核,用ifconfig eth0 up命令可以再度启动网络。所以现在的需要在网络故障时检测出来,然后复位网络。 如何检测网络故障是一个问题,在网上搜索了半天也没有找到好的解决方法。突然想到可以自己实现linux中的ping命令,然后定时ping网关,依据是否能ping通网关就可以判断出网络是否故障。 参考自定义ping的代码,见http://www.linuxidc.com/Linux/2011-08/40337.htm 然后把这个程序嵌入我的程序中,实现了功能。下面是ping函数部分的代码:
  1. //自定义ping函数参数   
  2. #define PACKET_SIZE                         4096   
  3. #define ERROR                               0   
  4. #define SUCCESS                             1   
  5. #define PING_TIME                           10000                               //10S   
  6. #define PING_TIME_OUT                       1000                                //1S
  7. // 效验算法   
  8. unsigned short cal_chksum(unsigned short *addr, int len)  
  9. {  
  10.     int nleft=len;  
  11.     int sum=0;  
  12.     unsigned short *w=addr;  
  13.     unsigned short answer=0;  
  14.   
  15.     while(nleft > 1)  
  16.     {  
  17.            sum += *w++;  
  18.         nleft -= 2;  
  19.     }  
  20.   
  21.     if( nleft == 1)  
  22.     {  
  23.         *(unsigned char *)(&answer) = *(unsigned char *)w;  
  24.            sum += answer;  
  25.     }  
  26.   
  27.     sum = (sum >> 16) + (sum & 0xffff);  
  28.     sum += (sum >> 16);  
  29.     answer = ~sum;  
  30.   
  31.     return answer;  
  32. }  
  33.   
  34. // Ping函数   
  35. int ping( char *ips, int timeout)  
  36. {  
  37.     struct timeval timeo;  
  38.     int sockfd;  
  39.     struct sockaddr_in addr;  
  40.     struct sockaddr_in from;  
  41.   
  42.     struct timeval *tval;  
  43.     struct ip *iph;  
  44.     struct icmp *icmp;  
  45.   
  46.     char sendpacket[PACKET_SIZE];  
  47.     char recvpacket[PACKET_SIZE];  
  48.   
  49.     int n;  
  50.     pid_t pid;  
  51.     int maxfds = 0;  
  52.     fd_set readfds;  
  53.   
  54.     // 设定Ip信息   
  55.     bzero(&addr,sizeof(addr));  
  56.     addr.sin_family = AF_INET;  
  57.     addr.sin_addr.s_addr = inet_addr(ips);  
  58.   
  59.     // 取得socket   
  60.     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  
  61.     if (sockfd < 0)  
  62.     {  
  63.         printf("ip:%s,socket error",ips);  
  64.         return ERROR;  
  65.     }  
  66.   
  67.     // 设定TimeOut时间   
  68.     timeo.tv_sec = timeout / 1000;  
  69.     timeo.tv_usec = timeout % 1000;  
  70.   
  71.     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)) == -1)  
  72.     {  
  73.         printf("ip:%s,setsockopt error",ips);  
  74.         return ERROR;  
  75.     }  
  76.   
  77.     // 设定Ping包   
  78.     memset(sendpacket, 0, sizeof(sendpacket));  
  79.   
  80.     // 取得PID,作为Ping的Sequence ID   
  81.     pid=getpid();  
  82.     int i,packsize;  
  83.     icmp=(struct icmp*)sendpacket;  
  84.     icmp->icmp_type=ICMP_ECHO;  
  85.     icmp->icmp_code=0;  
  86.     icmp->icmp_cksum=0;  
  87.     icmp->icmp_seq=0;  
  88.     icmp->icmp_id=pid;  
  89.     packsize=8+56;  
  90.     tval= (struct timeval *)icmp->icmp_data;  
  91.     gettimeofday(tval,NULL);  
  92.     icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,packsize);  
  93.   
  94.     // 发包   
  95.     n = sendto(sockfd, (char *)&sendpacket, packsize, 0, (struct sockaddr *)&addr, sizeof(addr));  
  96.     if (n < 1)  
  97.     {  
  98.         printf("ip:%s,sendto error",ips);  
  99.         return ERROR;  
  100.     }  
  101.   
  102.     // 接受   
  103.     // 由于可能接受到其他Ping的应答消息,所以这里要用循环   
  104.     while(1)  
  105.     {  
  106.         // 设定TimeOut时间,这次才是真正起作用的   
  107.         FD_ZERO(&readfds);  
  108.         FD_SET(sockfd, &readfds);  
  109.         maxfds = sockfd + 1;  
  110.         n = select(maxfds, &readfds, NULL, NULL, &timeo);  
  111.         if (n <= 0)  
  112.         {  
  113.             printf("ip:%s,Time out error",ips);  
  114.             close(sockfd);  
  115.             return ERROR;  
  116.         }  
  117.   
  118.         // 接受   
  119.         memset(recvpacket, 0, sizeof(recvpacket));  
  120.         int fromlen = sizeof(from);  
  121.         n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);  
  122.         if (n < 1) {  
  123.             break;  
  124.         }  
  125.   
  126.         // 判断是否是自己Ping的回复   
  127.         char *from_ip = (char *)inet_ntoa(from.sin_addr);  
  128.         printf("fomr ip:%s",from_ip);  
  129.          if (strcmp(from_ip,ips) != 0)  
  130.          {  
  131.             printf("ip:%s,Ip wang",ips);  
  132.             break;  
  133.          }  
  134.   
  135.         iph = (struct ip *)recvpacket;  
  136.   
  137.         icmp=(struct icmp *)(recvpacket + (iph->ip_hl<<2));  
  138.   
  139.         printf("ip:%s,icmp->icmp_type:%d,icmp->icmp_id:%d",ips,icmp->icmp_type,icmp->icmp_id);  
  140.        // 判断Ping回复包的状态   
  141.         if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid)  
  142.         {  
  143.             // 正常就退出循环   
  144.             break;  
  145.         }  
  146.         else  
  147.         {  
  148.             // 否则继续等   
  149.             continue;  
  150.         }  
  151.     }  
  152.   
  153.     // 关闭socket   
  154.     close(sockfd);  
  155.   
  156.     printf("ip:%s,Success",ips);  
  157.     return SUCCESS;  
  158. }
以上是ping函数的实现。然后我开了个定时器每隔10S启动一次,一旦检测到网络故障则自动恢复:
  1. //ping定时器槽函数   
  2. void alarmInterface::slot_ping_timer()  
  3. {  
  4.     //查看网络是否正常,否则重启网络   
  5.     if (ping(Gate_Way_Ip.data(),PING_TIME_OUT))  
  6.     {  
  7.         //cout << "wang luo zheng chang" << endl;   
  8.     }  
  9.     else  
  10.     {  
  11.         //cout << "wang luo gu zhang!!!!!!!!!!!!" << endl;   
  12.         system("ifconfig eth0 down");  
  13.         sleep(2);  
  14.         system("ifconfig eth0 up");  
  15.     }  
  16. }  
工作完成。