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

测试结果:

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!

Linux用ICMP协议实现简单Ping网络监测功能的更多相关文章

  1. ICMP 协议仿真及ping命令用途

    1.实验目的 加深对 IPv4 协议首部各定义域的理解,掌握路由表的结构和基本配置命令,熟悉 ICMP 的调试操作. 2.实验原理 IPv4 协议定义,网络层协议的相关 RFC 定义和描述. 3.实验 ...

  2. 002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)

    一.大致流程: 将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去.目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt. 二.数据结构: ICM ...

  3. ICMP协议、DNS、ARP协议、ping、DHCP协议

    1.ICMP协议 1)ICMP协议,即:网络控制消息协议(Internet Control Message Protocol) 2)ICMP是网络层协议,因为ICMP报文是装在IP数据报中,作为它的数 ...

  4. 三、ARP协议和ICMP协议

    一.ARP协议 网络设备有数据要发送到另一台网络设备时,必须要知道对方的网络层地址(IP).IP地址由网络层来提供,但是仅有IP地址是不够的,IP数据报文必须封装成帧才能通过数据链路进行发送.数据帧必 ...

  5. (转)协议森林06 瑞士军刀 (ICMP协议)

    协议森林06 瑞士军刀 (ICMP协议) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 到现在为止,我们讲解了网络层中最重要的I ...

  6. UNIX网络编程——利用ARP和ICMP协议解释ping命令

    一.MTU 以太网和IEEE 802.3对数据帧的长度都有限制,其最大值分别是1500和1492字节,将这个限制称作最大传输单元(MTU,Maximum Transmission Unit)      ...

  7. 【网络协议】ICMP协议、Ping、Traceroute

        ICMP协议 ICMP常常被觉得是IP层的一个组成部分,它是网络层的一个协议.它传递差错报文以及其它须要注意的信息.ICMP报文通常被IP层或更高层(TCP.UDP等)使用,它是在IP数据报内 ...

  8. PING的原理以及ICMP协议

    主要内容: 1.ping的原理以及工作过程 2.ICMP协议 3.ICMP的应用:ping,traceroute 1.ping的原理以及工作过程  ping的原理  ping 程序是用来探测主机到主机 ...

  9. 4.ICMP协议,ping和Traceroute

    1.IMCP协议介绍 前面讲到了,IP协议并不是一个可靠的协议,它不保证数据被送达,那么,自然的,保证数据送达的工作应该由其他的模块来完成.其中一个重要的模块就是ICMP(网络控制报文)协议. 当传送 ...

随机推荐

  1. pymysql操作数据库、索引、慢日志管理

    目录 pymysql操作数据库 简单操作 sql的注入问题 sql注入问题解决办法 sql注入问题模板总结 利用pymysql操作数据库 (增删改),conn.commit() 索引 1.为何要有索引 ...

  2. C# 跨窗体事件

    跨窗体事件:例如从一个窗体改变另一个窗体button的颜色,首先需要将需要改变button的属性改为public using System; using System.Drawing; using S ...

  3. JS 全局作用域和局部作用域

    一.作用域 1.什么是作用域(Scope) 通常来说,一段程序代码中所用到的名字不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域. JS作用域:就是代码名字(变量)作用的范围 ...

  4. vs 2019 调试web项目 浏览器

  5. UOJ #460. 新年的拯救计划 神仙题+构造

    对于这个神仙题,我还能说什么~ 第一个答案=$n/2$ 还是比较好猜的. 对于构造这个树,大概就是先从 $1$ 号节点向 $n/2$ 距离以内都连一条边,再在第 $n/2$ 个节点进行这个操作,然后从 ...

  6. 【PKUSC2019】线弦图【计数】【树形DP】【分治FFT】

    Description 定义线图为把无向图的边变成点,新图中点与点之间右边当且仅当它们对应的边在原图中有公共点,这样得到的图. 定义弦图为不存在一个长度大于3的纯环,纯环的定义是在环上任取两个不相邻的 ...

  7. PyCharm中Qt Designer+PyUIC配置

    本文环境配置: 系统=>windows10:64位 语言=>Python:3.7.1 第三方库PyQT5:5.11.3pyqt5-tools:5.11.3.1.4 工具=>PyCha ...

  8. ...扩展运算符+rest参数+call/apply/bind

    之前在set,map里面有提过扩展运算符的概念,但是今天偶然遇到一个问题,类似于扩展运算符的经典用法,突然发现对其了解不是很深,所以再来整理一下扩展运算符的相关知识. 重点:扩展运算符内部调用的是数据 ...

  9. grep家族、sed、awk

    一.grep家族 grep是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. 在上一次的博客中已经使用过egrep通过正则表达式来匹配正确IP地址,这里的用法就不多说了. f ...

  10. JavaScript 普通声明式函数

    1.为什么需要函数 实现代码的复用.存在函数提升,且会在变量提升的上面; 2.函数的创建 js中函数语法: function 函数名(形参){ //函数体 } 调用时:函数名(形参) 注: (1) 形 ...