编写自己的一个ping程序,可以说是许多人迈出网络编程的第一步吧!!这个ping程序的源代码经过我的修改和调试,基本上可以取代windows中自带的ping程序. 各个模块后都有我的详细注释和修改日志,希望能够对大家的学习
1 /* 本程序的主要源代码来自MSDN网站, 笔者只是做了一些改进和注释! 另外需要注意的是在Build之前,必须加入ws2_32.lib库文件,
否则会提示"error LNK2001:"的错误!*/
2
3 /******************************************************************************\
4 | Version 1.1 修改记录: |
5 | <1> 解决了socket阻塞的问题,从而能够正确地处理超时的请求! |
6 |------------------------------------------------------------------------------|
7 | Version 1.2 修改记录: |
8 | <1> 增加了由用户控制发送ICMP包的数目的功能(即命令的第二个参数). |
9 | <2> 增加了对ping结果的统计功能. |
10 \******************************************************************************/
11
12 #pragma pack(4)
13
14 #include <WINSOCK2.H>
15 #include <STDIO.H>
16 #include <STDLIB.H>
17
18 #define ICMP_ECHO 8
19 #define ICMP_ECHOREPLY 0
20
21 #define ICMP_MIN 8 // minimum 8 byte icmp packet (just header)
22
23 /* The IP header */
24 typedef struct iphdr {
25 unsigned int h_len:4; // length of the header
26 unsigned int version:4; // Version of IP
27 unsigned char tos; // Type of service
28 unsigned short total_len; // total length of the packet
29 unsigned short ident; // unique identifier
30 unsigned short frag_and_flags; // flags
31 unsigned char ttl;
32 unsigned char proto; // protocol (TCP, UDP etc)
33 unsigned short checksum; // IP checksum
34
35 unsigned int sourceIP;
36 unsigned int destIP;
37
38 }IpHeader;
39
40 //
41 // ICMP header
42 //
43 typedef struct icmphdr {
44 BYTE i_type;
45 BYTE i_code; /* type sub code */
46 USHORT i_cksum;
47 USHORT i_id;
48 USHORT i_seq;
49 /* This is not the std header, but we reserve space for time */
50 ULONG timestamp;
51 }IcmpHeader;
52
53 #define STATUS_FAILED 0xFFFF
54 #define DEF_PACKET_SIZE 32
55 #define DEF_PACKET_NUMBER 4 /* 发送数据报的个数 */
56 #define MAX_PACKET 1024
57
58 #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
59 #define xfree(p) HeapFree (GetProcessHeap(),0,(p))
60
61 void fill_icmp_data(char *, int);
62 USHORT checksum(USHORT *, int);
63 int decode_resp(char *,int ,struct sockaddr_in *);
64
65 void Usage(char *progname){
66
67 fprintf(stderr,"Usage:\n");
68 fprintf(stderr,"%s [number of packets] [data_size]\n",progname);
69 fprintf(stderr,"datasize can be up to 1Kb\n");
70 ExitProcess(STATUS_FAILED);
71
72 }
73 int main(int argc, char **argv){
74
75 WSADATA wsaData;
76 SOCKET sockRaw;
77 struct sockaddr_in dest,from;
78 struct hostent * hp;
79 int bread,datasize,times;
80 int fromlen = sizeof(from);
81 int timeout = 1000;
82 int statistic = 0; /* 用于统计结果 */
83 char *dest_ip;
84 char *icmp_data;
85 char *recvbuf;
86 unsigned int addr=0;
87 USHORT seq_no = 0;
88
89 if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0){
90 fprintf(stderr,"WSAStartup failed: %d\n",GetLastError());
91 ExitProcess(STATUS_FAILED);
92 }
93
94 if (argc <2 ) {
95 Usage(argv[0]);
96 }
97 sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL, 0,WSA_FLAG_OVERLAPPED);
98
99 //
100 //注:为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO),
101 // 必须将标志位设为WSA_FLAG_OVERLAPPED !
102 //
103
104 if (sockRaw == INVALID_SOCKET) {
105 fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
106 ExitProcess(STATUS_FAILED);
107 }
108 bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,
109 sizeof(timeout));
110 if(bread == SOCKET_ERROR) {
111 fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
112 ExitProcess(STATUS_FAILED);
113 }
114 timeout = 1000;
115 bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,
116 sizeof(timeout));
117 if(bread == SOCKET_ERROR) {
118 fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
119 ExitProcess(STATUS_FAILED);
120 }
121 memset(&dest,0,sizeof(dest));
122
123 hp = gethostbyname(argv[1]);
124
125 if (!hp){
126 addr = inet_addr(argv[1]);
127 }
128 if ((!hp) && (addr == INADDR_NONE) ) {
129 fprintf(stderr,"Unable to resolve %s\n",argv[1]);
130 ExitProcess(STATUS_FAILED);
131 }
132
133 if (hp != NULL)
134 memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
135 else
136 dest.sin_addr.s_addr = addr;
137
138 if (hp)
139 dest.sin_family = hp->h_addrtype;
140 else
141 dest.sin_family = AF_INET;
142
143 dest_ip = inet_ntoa(dest.sin_addr);
144
145 //
146 // atoi函数原型是: int atoi( const char *string );
147 // The return value is 0 if the input cannot be converted to an integer !
148 //
149 if(argc>2)
150 {
151 times=atoi(argv[2]);
152 if(times == 0)
153 times=DEF_PACKET_NUMBER;
154 }
155 else
156 times=DEF_PACKET_NUMBER;
157
158 if (argc >3)
159 {
160 datasize = atoi(argv[3]);
161 if (datasize == 0)
162 datasize = DEF_PACKET_SIZE;
163 if (datasize >1024) /* 用户给出的数据包大小太大 */
164 {
165 fprintf(stderr,"WARNING : data_size is too large !\n");
166 datasize = DEF_PACKET_SIZE;
167 }
168 }
169 else
170 datasize = DEF_PACKET_SIZE;
171
172 datasize += sizeof(IcmpHeader);
173
174 icmp_data = (char*)xmalloc(MAX_PACKET);
175 recvbuf = (char*)xmalloc(MAX_PACKET);
176
177 if (!icmp_data) {
178 fprintf(stderr,"HeapAlloc failed %d\n",GetLastError());
179 ExitProcess(STATUS_FAILED);
180 }
181
182
183 memset(icmp_data,0,MAX_PACKET);
184 fill_icmp_data(icmp_data,datasize);
185
186 //
187 //显示提示信息
188 //
189 fprintf(stdout,"\nPinging %s .\n\n",dest_ip);
190
191
192 for(int i=0;i<times;++i){
193 int bwrote;
194
195 ((IcmpHeader*)icmp_data)->i_cksum = 0;
196 ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
197
198 ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
199 ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,datasize);
200
201 bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
202 if (bwrote == SOCKET_ERROR){
203 if (WSAGetLastError() == WSAETIMEDOUT) {
204 printf("Request timed out.\n");
205 continue;
206 }
207 fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());
208 ExitProcess(STATUS_FAILED);
209 }
210 if (bwrote < datasize ) {
211 fprintf(stdout,"Wrote %d bytes\n",bwrote);
212 }
213 bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);
214 if (bread == SOCKET_ERROR){
215 if (WSAGetLastError() == WSAETIMEDOUT) {
216 printf("Request timed out.\n");
217 continue;
218 }
219 fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());
220 ExitProcess(STATUS_FAILED);
221 }
222 if(!decode_resp(recvbuf,bread,&from))
223 statistic++; /* 成功接收的数目++ */
224 Sleep(1000);
225
226 }
227
228 /*
229 Display the statistic result
230 */
231 fprintf(stdout,"\nPing statistics for %s \n",dest_ip);
232 fprintf(stdout," Packets: Sent = %d,Received = %d, Lost = %d (%2.0f%% loss)\n",times,
233 statistic,(times-statistic),(float)(times-statistic)/times*100);
234
235
236 WSACleanup();
237 return 0;
238
239 }
240 /*
241 The response is an IP packet. We must decode the IP header to locate
242 the ICMP data
243 */
244 int decode_resp(char *buf, int bytes,struct sockaddr_in *from) {
245
246 IpHeader *iphdr;
247 IcmpHeader *icmphdr;
248 unsigned short iphdrlen;
249
250 iphdr = (IpHeader *)buf;
251
252 iphdrlen = (iphdr->h_len) * 4 ; // number of 32-bit words *4 = bytes
253
254 if (bytes < iphdrlen + ICMP_MIN) {
255 printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
256 }
257
258 icmphdr = (IcmpHeader*)(buf + iphdrlen);
259
260 if (icmphdr->i_type != ICMP_ECHOREPLY) {
261 fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type);
262 return 1;
263 }
264 if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {
265 fprintf(stderr,"someone else's packet!\n");
266 return 1;
267 }
268 printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
269 printf(" icmp_seq = %d. ",icmphdr->i_seq);
270 printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);
271 printf("\n");
272 return 0;
273
274 }
275
276
277 USHORT checksum(USHORT *buffer, int size) {
278
279 unsigned long cksum=0;
280
281 while(size >1) {
282 cksum+=*buffer++;
283 size -=sizeof(USHORT);
284 }
285
286 if(size) {
287 cksum += *(UCHAR*)buffer;
288 }
289
290 cksum = (cksum >> 16) + (cksum & 0xffff);
291 cksum += (cksum >>16);
292 return (USHORT)(~cksum);
293 }
294 /*
295 Helper function to fill in various stuff in our ICMP request.
296 */
297 void fill_icmp_data(char * icmp_data, int datasize){
298
299 IcmpHeader *icmp_hdr;
300 char *datapart;
301
302 icmp_hdr = (IcmpHeader*)icmp_data;
303
304 icmp_hdr->i_type = ICMP_ECHO;
305 icmp_hdr->i_code = 0;
306 icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
307 icmp_hdr->i_cksum = 0;
308 icmp_hdr->i_seq = 0;
309
310 datapart = icmp_data + sizeof(IcmpHeader);
311 //
312 // Place some junk in the buffer.
313 //
314 memset(datapart,'E', datasize - sizeof(IcmpHeader));
315
316 }
317
318 /******************* 附: ping命令执行时显示的画面 **************\
319 * C:\Documents and Settings\houzhijiang>ping 236.56.54.12 *
320 * *
321 * Pinging 236.56.54.12 with 32 bytes of data: *
322 * *
323 * Request timed out. *
324 * Request timed out. *
325 * Request timed out. *
326 * Request timed out. *
327 * *
328 * Ping statistics for 236.56.54.12: *
329 * Packets: Sent = 4, Received = 0, Lost = 4 (100% loss), *
330 * *
331 \***************************************************************/
332
333 /***************************************************************\
334 * C:\Documents and Settings\houzhijiang>ping 127.0.0.1 *
335 * *
336 * Pinging 127.0.0.1 with 32 bytes of data: *
337 * *
338 * Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 *
339 * Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 *
340 * Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 *
341 * Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 *
342 * *
343 * Ping statistics for 127.0.0.1: *
344 * Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), *
345 * Approximate round trip times in milli-seconds: *
346 * Minimum = 0ms, Maximum = 0ms, Average = 0ms *
347 * *
348 \***************************************************************/
349
posted on 2009-07-23 16:01
老马驿站 阅读(5710)
评论(1) 编辑 收藏 引用 所属分类:
windows 、
protocol