ping程序的实现

Posted 紫色年华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ping程序的实现相关的知识,希望对你有一定的参考价值。

构造icmp包,发送给自己在同一网段的主机,使用select函数,非阻塞方式接收回包。还包括反码算术求和求首部校验和的函数。

 

转载请注明出处。

 

可能的情况

1、在线

  目的主机直接回复icmp包。

2、终点不可达(发送不到目的主机)

  接收到路由器或本机的icmp的终点不可达回包。

3、接受不到回包(能发送到目的主机)

  能发送到目的主机,但是被目的主机的防火墙拦截了,不做回复,所以收不到回包。

 

代码绝大部分是老师上课的内容,自己整理了一遍,加上了一些注释:

  1 // SetAvailableIP.cpp : 定义控制台应用程序的入口点。
  2 //
  3 
  4 #pragma pack(4)
  5 /*
  6 *编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。
  7 *n字节对齐就是说变量存放的起始地址的偏移量有两种情况,
  8 *第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,
  9 *第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
 10 *结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,
 11 *那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
 12 */
 13 
 14 
 15 #define WIN32_LEAN_AND_MEAN
 16 #include "stdafx.h"
 17 #include <stdlib.h>
 18 #include <WINSOCK2.H>//与#include <winsock2.h>相同
 19 #pragma comment(lib,"ws2_32.lib")
 20 
 21 
 22 #define ICMP_ECHO 8
 23 #define ICMP_ECHOREPLY 0
 24 
 25 #define ICMP_MIN 8//// minimum 8 byte icmp packet (just header)
 26 #define uchar unsigned char
 27 #define uint unsigned int
 28 
 29 
 30 /* ip header*/
 31 typedef struct iphdr{
 32     uchar h_len:4;//位域,占四个二进制位 length of header
 33     uchar version:4;//Version of ip   因为位序是小端所以颠倒位置
 34     uchar tos;        //type of service
 35     uchar total_len;//total length of packet
 36     uchar ident;    //unique idetifier  超出mtu,需要分片时的组号标识
 37     uchar frag_and_flags;//标志3位   片偏移
 38     uchar ttl;            //time to live
 39     uchar proto;        //protocol (TCP\UDP etc)
 40     uchar checksum;        //IP checksum
 41     uint sourceIP;        
 42     uint destIP;
 43 }IpHeader;
 44 
 45 
 46 // ICMP header
 47 //
 48 typedef struct _ihdr {
 49   BYTE i_type;
 50   BYTE i_code; /* type sub code */
 51   USHORT i_cksum;
 52   USHORT i_id;
 53   USHORT i_seq;
 54   /* This is not the std header, but we reserve space for time */
 55   ULONG timestamp;
 56 }IcmpHeader;
 57 
 58 
 59 #define STATUS_FAILED 0xFFFF
 60 #define DEF_PACKET_SIZE 32
 61 #define MAX_PACKET 1024
 62 
 63 #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
 64 #define xfree(p)   HeapFree (GetProcessHeap(),0,(p))
 65 
 66 void fill_icmp_data(char *, int);
 67 USHORT checksum(USHORT *, int);
 68 void decode_resp(char *,int ,struct sockaddr_in *);
 69 bool ping(IN_ADDR ip);
 70 
 71 void Usage(char *progname){
 72   
 73   fprintf(stderr,"Usage:\n");
 74   fprintf(stderr,"%s <host> [data_size]\n",progname);
 75   fprintf(stderr,"datasize can be up to 1Kb\n");
 76   ExitProcess(STATUS_FAILED);
 77 }
 78 
 79 
 80 
 81 int _tmain(int argc, _TCHAR* argv[])
 82 {
 83     IN_ADDR ipStart,ipEnd,ip,netMask;
 84     unsigned int iStart,iEnd,i;
 85 
 86 
 87     WSADATA wsaData;
 88     WSAStartup(MAKEWORD(2,2),&wsaData);//当一个应用程序调用WSAStartup函数时,
 89     //操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。
 90     //以后应用程序就可以调用所请求的Socket库中的其它Socket函数了
 91 
 92 
 93     ip.s_addr = inet_addr("192.168.26.155");//点分十进制转换成网络字节顺序
 94     netMask.s_addr = inet_addr("255.255.255.128");
 95     ipStart.s_addr = ip.s_addr & netMask.s_addr;//两个unlong,ip与掩码与 求出ip最小值
 96     ipEnd.s_addr = (ipStart.s_addr & netMask.s_addr) | ~netMask.s_addr;
 97 
 98     iStart = ntohl(ipStart.s_addr);//网络自己顺序转换成主机字节顺序
 99     iEnd = ntohl(ipEnd.s_addr);
100     
101     for(i = iStart+2;i<iEnd;i++)//iStart不可用,iStart+1一般做默认网关地址,iStart+2开始
102     {
103         ip.s_addr = htonl(i);
104         printf("%s\n",inet_ntoa(ip));//in_addr(点分十进制)转换成字符串
105         ping(ip);
106     }
107     WSACleanup();
108     return 0;
109 }
110 
111 
112 bool ping(IN_ADDR ip)//ping程序
113 {
114     SOCKET sockRaw;
115     struct sockaddr_in dest,from;
116     int bread,datasize;
117     int fromlen = sizeof(from);
118     int timeout = 1000;
119     char *dest_ip;
120     char *icmp_data;
121     char *recvbuf;
122     unsigned int addr = 0;
123     USHORT seq_no = 0;
124 
125     struct timeval tv;
126     tv.tv_sec =  3;
127     tv.tv_usec = 0;
128 
129     
130     sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,0);
131 
132     if(sockRaw == INVALID_SOCKET)
133     {
134         fprintf(stderr,"WSASocket() failed:%d\n",WSAGetLastError());
135         ExitProcess(STATUS_FAILED);
136     }
137 
138     bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));//设置1秒超时时间
139 
140     if(bread == SOCKET_ERROR) {
141           fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
142         ExitProcess(STATUS_FAILED);
143     }
144 
145     timeout = 1000;
146     bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,
147                       sizeof(timeout));
148     if(bread == SOCKET_ERROR) {
149           fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
150         ExitProcess(STATUS_FAILED);
151     }
152 
153     memset(&dest,0,sizeof(dest));//初始化
154 
155     dest.sin_addr = ip;
156     dest.sin_family = AF_INET;
157     dest_ip = inet_ntoa(dest.sin_addr);
158 
159     datasize = DEF_PACKET_SIZE;
160     datasize += sizeof(IcmpHeader);
161 
162     icmp_data = (char*)xmalloc(MAX_PACKET);
163     recvbuf = (char*)xmalloc(MAX_PACKET);
164 
165 
166     if (!icmp_data) {
167         fprintf(stderr,"HeapAlloc failed %d\n",GetLastError());
168         ExitProcess(STATUS_FAILED);
169     }
170 
171 
172     memset(icmp_data,0,MAX_PACKET);
173     fill_icmp_data(icmp_data,datasize);
174 
175 
176     //while(1) {
177         int bwrote;
178 
179         ((IcmpHeader*)icmp_data)->i_cksum = 0;
180         ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
181 
182         ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
183         ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, 
184             datasize);
185 
186         bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,
187             sizeof(dest));
188 
189         if (bwrote == SOCKET_ERROR){
190             if (WSAGetLastError() == WSAETIMEDOUT) {
191                 printf("timed out\n");
192                 //continue;
193             }
194             fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());
195             ExitProcess(STATUS_FAILED);
196         }
197         if (bwrote < datasize ) {
198             fprintf(stdout,"Wrote %d bytes\n",bwrote);
199         }
200 
201 
202 
203         fd_set socketset;
204         memset(&socketset, 0, sizeof(socketset));
205         socketset.fd_count = 1;
206         socketset.fd_array[0] = sockRaw;
207         bread = select(0, &socketset, NULL, NULL, &tv);//非阻塞方式接收数据,接收不到显示超时继续ping后面的IP
208         if (bread == 0){
209             //if (WSAGetLastError() == WSAETIMEDOUT) {
210             printf("recv timed out\n");
211             //continue;
212             //}
213             //fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());
214             //ExitProcess(STATUS_FAILED);
215         }
216         else{
217             bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,
218                 &fromlen);
219             decode_resp(recvbuf,bread,&from);
220         }
221         //Sleep(1000);
222 
223     //}
224 
225     xfree(icmp_data);
226     xfree(recvbuf);
227 
228 }
229 
230 
231 USHORT checksum(USHORT *buffer, int size) {//校验和
232 
233   unsigned long cksum=0;
234 
235   while(size >1) {
236     cksum+=*buffer++;
237     size -=sizeof(USHORT);
238   }
239   
240   if(size ) {
241     cksum += *(UCHAR*)buffer;
242   }
243 
244   cksum = (cksum >> 16) + (cksum & 0xffff);//反码算术运算求和  
245   cksum += (cksum >>16);//最多进一位,右移加上进位,不可能再次产生进位  比如99+99 =198  98+1=99 不会继续产生进位  
246   return (USHORT)(~cksum);
247 }
248 
249 /* 
250     Helper function to fill in various stuff in our ICMP request.
251 */
252 void fill_icmp_data(char * icmp_data, int datasize){
253 
254   IcmpHeader *icmp_hdr;
255   char *datapart;
256 
257   icmp_hdr = (IcmpHeader*)icmp_data;
258 
259   icmp_hdr->i_type = ICMP_ECHO;
260   icmp_hdr->i_code = 0;
261   icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
262   icmp_hdr->i_cksum = 0;
263   icmp_hdr->i_seq = 0;
264   
265   datapart = icmp_data + sizeof(IcmpHeader);//截取数据部分
266   //
267   // Place some junk in the buffer.
268   //
269   memset(datapart,E, datasize - sizeof(IcmpHeader));//填充数据
270 }
271 
272 /* 
273     The response is an IP packet. We must decode the IP header to locate 
274     the ICMP data 
275 */
276 void decode_resp(char *buf, int bytes,struct sockaddr_in *from) {
277 
278     IpHeader *iphdr;
279     IcmpHeader *icmphdr;
280     unsigned short iphdrlen;
281 
282     iphdr = (IpHeader *)buf;
283 
284     iphdrlen = iphdr->h_len * 4; // number of 32-bit words *4 = bytes
285 
286     if (bytes  < iphdrlen + ICMP_MIN) {
287         printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
288     }
289 
290     icmphdr = (IcmpHeader*)(buf + iphdrlen);
291 
292     if (icmphdr->i_type != ICMP_ECHOREPLY) {
293         fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type);
294         return;
295     }
296     if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {
297         fprintf(stderr,"someone else‘s packet!\n");
298         return ;
299     }
300     printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
301     printf(" icmp_seq = %d. ",icmphdr->i_seq);
302     printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);//GetTickCount()操作系统从启动经过的毫秒数
303     printf("\n");
304         
305 }

 

以上是关于ping程序的实现的主要内容,如果未能解决你的问题,请参考以下文章

python学习:通过python实现快速ping

ASP.net MVC 代码片段问题中的 Jqgrid 实现

linux c程序中获取shell脚本输出的实现方法

4-1 For 实现批量主机ping探测

ping程序的实现

微信小程序代码片段