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 }