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 }
时间: 2024-10-12 09:48:26

ping程序的实现的相关文章

计算机网络(5)-----ICMP协议和PING程序

控制报文协议(Internet Control Message Protocol) 定义 它是TCP/IP协议族的一个子协议,用于在IP主机.路由器之间传递控制消息.控制消息是指网络通不通.主机是否可达.路由是否可用等网络本身的消息. 简介 ICMP报文就像是IP报文的小弟,总顶着IP报文的名头出来混.因为ICMP报文是在IP报文内部的,如图: IP协议并不是一个可靠的协议,它不保证数据被送达,那么,自然的,保证数据送达的工作应该由其他的模块来完成.其中一个重要的模块就是ICMP(网络控制报文)

Ping程序

一.概述 Ping程序是对两个TCP/IP系统连通性进行测试的基本工具.该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答. 二.格式 大多数TCP/IP实现都在内核中直接支持Ping服务器--这种服务器不是一个用户进程. ? 在Unix中,把ICMP的标识符字段设置为发送进程的ID号,这样子即使在同一台主机上同时运行了多个Ping程序实例,也能正确识别出返回的信息.

《网络编程》原始套接字 ---ping程序实现

概述 基于字节流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)不可以访问传输层协议,只是对应用层的报文进行操作,传输层的数据报格式都是由系统提供的协议栈实现,用户只需要填充相应的应用层报文,由系统完成底层报文首部的填充并发送.原始套接字(SOCK_RAW)可以访问位于基层的传输层协议,原始套接字没有端口号. 原始套接字(SOCK_RAW)是一种不同于 SOCK_STREAM.SOCK_DGRAM 的套接字,它实现于系统核心.原始套接字使进程可以读与写 ICMP.IGMP

Ping程序与Traceroute程序的原理(五)

(参考文献)TCP/IP详解,卷1:协议 "ping"这个名字来源于声呐定位操作.目的是为了测试另外一台主机是否可以到达.该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答. 一般来说,如果不能ping到某台主机那么就不能Telnet或者FTP到那台主机.反过来,如果不能Telnet到某台主机,那么通常可以用ping程序来确定问题出在哪里.Ping程序还能测出到这台主机的往返时间,以表明该主机离我们有"多远". Ping程序 我们称发送回显请求的p

ping程序剖析

   在剖析ping之前我们先补充一点知识...     (1)结构体addinfo      头文件:#include<netdb.h>     struct addrinfo     {            int  ai_flags;            int  ai_family;      //AF_INET,AF_INET6,UNIX etc            int  ai_socktype;   //STREAW,DATAGRAM,RAW            int

Linux下实现ping程序

今天参照大神的代码实现了一个ping程序. 总体是先发送一个ping请求,然后循环四次监听返回. send_ping函數 將icmp_hdr(icmp消息的header)的指針指向一片內存空間,然後定義各個屬性.通過memcpy函數將要發送的數據複製到data屬性中. 再通過sendto函數將icmp數據包發送到指定地址 sendto函數 #include <sys/types.h> #include <sys/socket.h> int sendto(int socketfd,

003.同时Ping多个IP(select实现IO复用,信号计时),ping程序升级版

写这个的目的主要是为了以后的方便: 1.信号计时函数的使用 2.ip头的构建和icmp头的构建 3.selec函数t的用法 代码实现: /src/ping.h 1 /* 2 * ping.h 3 * 4 * Created on: 2015年11月6日 5 * Author: root 6 */ 7 8 #ifndef PING_H_ 9 #define PING_H_ 10 11 #endif /* PING_H_ */ 12 13 #include <sys/types.h> 14 #in

TCP协议学习记录 (三) Ping程序 RR选项 记录路由hop

一开始想直接在上个程序改,自己构造IP包头,但后来发现不行,微软不让干了,所有后来选用libcap库来收发包 代码写的很乱.. 1 #pragma pack(4) 2 3 #define ECHO_REQUEST 8 4 #define DATASIZE 65500 5 #define PACKETSIZE 65535 6 #define IPCOUNT 9 7 #define MACSIZE 6 8 #define OPTION_RR 7 9 10 struct ethhdr 11 { 12

Ping程序实现

/************************************************************************* > File Name: Ping.c > Author: ICKelin > Mail: [email protected] > Created Time: 2014年12月17日 星期三 04时53分50秒 **************************************************************