zoukankan      html  css  js  c++  java
  • Linux用ICMP协议实现简单Ping网络监测功能

    ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
    ICMP协议是一种面向无连接的协议,用于传输出错报告控制信息。它是一个非常重要的协议,它对于网络安全具有极其重要的意义。
     
    折腾半天,原来ICMP也是TCP/IP其中一种协议.那么监测网络是否ping的通,就跟TCP协议差不多了。
    步骤简单归纳为:1.绑定套接字,2.发送数据包 3.接收数据包 4.解析数据包
    [cpp] view plain copy
     
     print?
    1. #include <stdio.h>  
    2. #include <sys/socket.h>  
    3. #include <netinet/in.h>  
    4. #include <netinet/ip.h>  
    5. #include <netinet/ip_icmp.h>  
    6. #include <netdb.h>  
    [cpp] view plain copy
     
     print?
    1. #define PACKET_SIZE     4096  
    2. #define ERROR           0  
    3. #define SUCCESS         1  
    4.   
    5. //效验算法(百度下有注释,但是还是看不太明白)  
    6. unsigned short cal_chksum(unsigned short *addr, int len)  
    7. {  
    8.     int nleft=len;  
    9.     int sum=0;  
    10.     unsigned short *w=addr;  
    11.     unsigned short answer=0;  
    12.       
    13.     while(nleft > 1)  
    14.     {  
    15.         sum += *w++;  
    16.         nleft -= 2;  
    17.     }  
    18.       
    19.     if( nleft == 1)  
    20.     {         
    21.         *(unsigned char *)(&answer) = *(unsigned char *)w;  
    22.         sum += answer;  
    23.     }  
    24.       
    25.     sum = (sum >> 16) + (sum & 0xffff);  
    26.     sum += (sum >> 16);  
    27.     answer = ~sum;  
    28.       
    29.     return answer;  
    30. }  
    31. // Ping函数  
    32. int ping( char *ips, int timeout)    
    33. {        
    34.     struct timeval *tval;          
    35.     int maxfds = 0;    
    36.     fd_set readfds;    
    37.       
    38.     struct sockaddr_in addr;    
    39.     struct sockaddr_in from;    
    40.     // 设定Ip信息    
    41.     bzero(&addr,sizeof(addr));    
    42.     addr.sin_family = AF_INET;    
    43.     addr.sin_addr.s_addr = inet_addr(ips);    
    44.       
    45.     int sockfd;    
    46.     // 取得socket    
    47.     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);    
    48.     if (sockfd < 0)    
    49.     {    
    50.         printf("ip:%s,socket error ",ips);    
    51.         return ERROR;    
    52.     }    
    53.       
    54.     struct timeval timeo;    
    55.     // 设定TimeOut时间    
    56.     timeo.tv_sec = timeout / 1000;    
    57.     timeo.tv_usec = timeout % 1000;    
    58.       
    59.     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)) == -1)    
    60.     {    
    61.         printf("ip:%s,setsockopt error ",ips);    
    62.         return ERROR;    
    63.     }    
    64.       
    65.     char sendpacket[PACKET_SIZE];    
    66.     char recvpacket[PACKET_SIZE];    
    67.     // 设定Ping包    
    68.     memset(sendpacket, 0, sizeof(sendpacket));    
    69.       
    70.     pid_t pid;    
    71.     // 取得PID,作为Ping的Sequence ID    
    72.     pid=getpid();    
    73.       
    74.     struct ip *iph;    
    75.     struct icmp *icmp;    
    76.       
    77.     
    78.     icmp=(struct icmp*)sendpacket;    
    79.     icmp->icmp_type=ICMP_ECHO;  //回显请求  
    80.     icmp->icmp_code=0;    
    81.     icmp->icmp_cksum=0;    
    82.     icmp->icmp_seq=0;    
    83.     icmp->icmp_id=pid;   
    84.     tval= (struct timeval *)icmp->icmp_data;    
    85.     gettimeofday(tval,NULL);    
    86.     icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,sizeof(struct icmp));  //校验  
    87.       
    88.     int n;    
    89.     // 发包    
    90.     n = sendto(sockfd, (char *)&sendpacket, sizeof(struct icmp), 0, (struct sockaddr *)&addr, sizeof(addr));    
    91.     if (n < 1)    
    92.     {    
    93.         printf("ip:%s,sendto error ",ips);    
    94.         return ERROR;    
    95.     }    
    96.       
    97.     // 接受    
    98.     // 由于可能接受到其他Ping的应答消息,所以这里要用循环    
    99.     while(1)    
    100.     {    
    101.         // 设定TimeOut时间,这次才是真正起作用的    
    102.         FD_ZERO(&readfds);    
    103.         FD_SET(sockfd, &readfds);    
    104.         maxfds = sockfd + 1;    
    105.         n = select(maxfds, &readfds, NULL, NULL, &timeo);    
    106.         if (n <= 0)    
    107.         {    
    108.             printf("ip:%s,Time out error ",ips);    
    109.             close(sockfd);    
    110.             return ERROR;    
    111.         }    
    112.           
    113.         // 接受    
    114.         memset(recvpacket, 0, sizeof(recvpacket));    
    115.         int fromlen = sizeof(from);    
    116.         n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);    
    117.         if (n < 1) {    
    118.             break;    
    119.         }    
    120.           
    121.        
    122.         char *from_ip = (char *)inet_ntoa(from.sin_addr);    
    123.             // 判断是否是自己Ping的回复    
    124.         if (strcmp(from_ip,ips) != 0)    
    125.         {    
    126.             printf("NowPingip:%s Fromip:%s NowPingip is not same to Fromip,so ping wrong! ",ips,from_ip);    
    127.             return ERROR;  
    128.         }    
    129.           
    130.         iph = (struct ip *)recvpacket;    
    131.           
    132.         icmp=(struct icmp *)(recvpacket + (iph->ip_hl<<2));    
    133.           
    134.         printf("ip:%s ,icmp->icmp_type:%d ,icmp->icmp_id:%d ",ips,icmp->icmp_type,icmp->icmp_id);    
    135.         // 判断Ping回复包的状态    
    136.         if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid)   //ICMP_ECHOREPLY回显应答  
    137.         {    
    138.             // 正常就退出循环    
    139.             break;    
    140.         }    
    141.         else    
    142.         {    
    143.             // 否则继续等    
    144.             continue;    
    145.         }    
    146.     }    
    147.       
    148.     int main()  
    149. {  
    150.     char cPing[16];  
    151.     printf("Please input ping IP:");  
    152.     scanf("%s",cPing);  
    153.       
    154.     if(ping(cPing,10000))  
    155.     {  
    156.         printf("Ping succeed! ");  
    157.     }  
    158.     else  
    159.     {  
    160.         printf("Ping wrong! ");  
    161.     }  
    162.       
    163. }  

    测试结果:
    root@an-virtual-machine:~/wyz/test# ./testping
    Please input ping IP:192.168.1.155
    Nowip:192.168.1.155 Fromip:192.168.1.133
    Nowip is not same to Fromip,so ping wrong!
    Ping wrong!
    root@an-virtual-machine:~/wyz/test# ./testping
    Please input ping IP:192.168.1.188
    ip:192.168.1.188
    ,icmp->icmp_type:0
    ,icmp->icmp_id:27865
    Ping succeed!
  • 相关阅读:
    Visual studio之C# 调用系统软键盘(外部"osk.exe")
    Visual studio之C# 重新定义Messbox的显示窗口位置
    Visual studio之C#的一些常见问题
    C8051F340之USB简介
    CentOS 安装 Sun JDK
    配置Tomcat以指定的身份(非root)运行
    CentOS6 root 用户 vi/vim 无法开启高亮
    删除 Mac OS X 中“打开方式”里重复或无用的程序列表
    快速建立Linux c/c++编译环境
    Ubuntu 安装 Sun JDK
  • 原文地址:https://www.cnblogs.com/daochong/p/6873589.html
Copyright © 2011-2022 走看看