自己动手写一个简单的ping命令(windows下)

时间:2024-03-05 07:34:39

计算机网络的一个作业:实现ping+ip 不要求其它参数

用到的知识:ip报头结构,icmp报头结构,获取主机ip方法,winsock的简单操作

实现的结果:一般的ip能够ping通,如www.baidu.com, www.sina.com.cn

存在的问题:本机(127.0.0.1)ping不通  

  1 /*
  2 *filename:ping.c
  3 *author:ChrisZZ
  4 */
  5 #pragma comment(lib, "ws2_32.lib")
  6 #include <stdio.h>
  7 #include <winsock2.h>
  8 #include <ws2tcpip.h>
  9 #include <process.h>
 10 
 11 
 12 struct IPHeader{//IPv4头部结构
 13     unsigned char ver_hlen;//头部长度,用4位表示,range[5,15]
 14     //  unsigned int ver:4;//version
 15     unsigned char tos;//type of service
 16     unsigned short total_len;
 17     unsigned short id;
 18     unsigned short frags_and_flags;
 19     unsigned char ttl;//time to live
 20     unsigned char proto;//protocal
 21     unsigned short chksum;//checksum
 22     unsigned int srcIp;//source IP
 23     unsigned int dstIp;//destination IP
 24 };
 25 
 26 struct ICMPHeader{//ICMP头部结构
 27     unsigned char type;
 28     unsigned char code;
 29     unsigned short chksum;
 30     unsigned short id;
 31     unsigned short seq;//sequence
 32     unsigned int timestamp;
 33 };
 34 
 35 #define ICMP_ECHO       8
 36 #define ICMP_ECHO_REPLY 0
 37 #define  PACKAGE_SIZE         sizeof(IPHeader)+sizeof(ICMPHeader)
 38 void usage();
 39 unsigned short CheckSum(unsigned short*, int);
 40 void HandleError(char *);
 41 
 42 //用法说明函数
 43 void usage(){
 44     printf("用法:ping 目标主机\n\n");
 45     system("pause");
 46 }
 47 
 48 //错误处理函数
 49 void HandleError(char *str){
 50     int errCode=WSAGetLastError();
 51 
 52     char info[65]={0};
 53     _snprintf(info, 64, "%s:  %d\n", str, errCode);
 54     printf(info);
 55 }
 56 
 57 unsigned short checksum(unsigned short* buff, int size){
 58     unsigned int chksum=0;
 59     while(size>1){
 60         chksum+=*buff++;
 61         size-=sizeof(unsigned short);
 62     }
 63     if(size){
 64         chksum+=*(unsigned char*)(buff);
 65     }
 66     chksum=(chksum>>16)+(chksum &0xffff);
 67     chksum+=(chksum>>16);
 68     return (unsigned short)(~chksum);
 69 }
 70 
 71 void FillPackage(char *pData, unsigned int dstIP, unsigned short id){
 72     memset(pData, 0, PACKAGE_SIZE);
 73 
 74     IPHeader *pIPHeader=(IPHeader*)pData;
 75 
 76     int nVersion=4;
 77     int nHeadSize=sizeof(IPHeader)/4;
 78 
 79     //获得本机IP
 80     struct sockaddr_in SrcAddr;
 81     struct hostent * hpsrc;
 82     char localhost[32];
 83     gethostname(localhost, sizeof(localhost));
 84     hpsrc=gethostbyname(localhost);
 85     memset(&SrcAddr, 0, sizeof(SrcAddr));
 86     memcpy(&(SrcAddr.sin_addr), hpsrc->h_addr, hpsrc->h_length);
 87     SrcAddr.sin_port=htons(0);
 88     SrcAddr.sin_family=AF_INET;
 89 
 90 
 91     unsigned int srcIP=SrcAddr.sin_addr.s_addr;//本机IP
 92     unsigned int destIp=dstIP;
 93     pIPHeader->ver_hlen=(nVersion<<4)|nHeadSize;
 94     pIPHeader->tos=0;
 95     pIPHeader->total_len=htons(PACKAGE_SIZE);
 96     pIPHeader->id=htons(1234);//????
 97     pIPHeader->frags_and_flags=0;
 98     pIPHeader->ttl=255;//??
 99     pIPHeader->proto=IPPROTO_ICMP;
100     pIPHeader->chksum=0;
101     pIPHeader->srcIp=srcIP;
102     pIPHeader->dstIp=dstIP;
103 
104     pIPHeader->chksum=checksum((unsigned short*)pData, sizeof(IPHeader));
105     ICMPHeader* pICMPHeader=(ICMPHeader*)(pData+sizeof(IPHeader));
106     pICMPHeader->type=ICMP_ECHO;
107     pICMPHeader->code=0;
108     pICMPHeader->chksum=0;
109     pICMPHeader->id=htons(id);
110     pICMPHeader->seq=htons(id);
111     pICMPHeader->chksum=checksum((unsigned short*)((char*)pData+sizeof(IPHeader)), sizeof(ICMPHeader));
112 
113 }
114 
115 int main(int argc, char* argv[]){
116 
117     //初始化
118     WSADATA wsaData;
119     struct sockaddr_in DestAddr,from;
120     IPHeader *ip;
121     ICMPHeader *icmp;
122     ICMPHeader *SendIcmp;
123     int Timeout=1000;
124     char IcmpBuffer[PACKAGE_SIZE]="";
125     SOCKET IcmpSocket;
126     char RecvBuffer[1024];
127     sockaddr_in addr;
128     int Len = sizeof(addr);
129     int Result;
130     struct hostent * hpdst;
131     int pid=_getpid();
132     struct timeval tv;
133     fd_set readSet;
134     BOOL bBroadcast=false;
135 
136     //确定参数个数
137     if(argc!=2){
138         printf("参数不正确\n");
139         usage();
140         return -1;
141     }
142 
143 
144     if ((Result = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0){
145         printf("WSAStartup failed with error %d\n", Result);
146         return 0;
147     }
148 
149 
150     //获取目的主机IP
151     hpdst=gethostbyname(argv[1]);
152     if(hpdst==NULL){
153         HandleError("gethostbyname");
154         WSACleanup();
155         return -1;
156     }
157 
158     //创建ICMP封包并发送
159     //char buff[sizeof(ICMP_HDR)+32];
160     IcmpSocket=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
161     if(IcmpSocket==INVALID_SOCKET){
162         HandleError("socket");
163         WSACleanup();
164         return -1;
165     }
166     BOOL on=TRUE;
167     Result=setsockopt(IcmpSocket,IPPROTO_IP,IP_HDRINCL, (char*)&on, sizeof(on));
168     if(Result==SOCKET_ERROR){
169         HandleError("socketopt");
170         WSACleanup();
171         return -1;  
172     }   
173     memset(&DestAddr, 0, sizeof(DestAddr));
174     memcpy(&(DestAddr.sin_addr), hpdst->h_addr, hpdst->h_length);
175     DestAddr.sin_port=htons(0);
176     /*reference:http://www.cnblogs.com/CBDoctor/archive/2012/10/28/2743109.html*/
177     DestAddr.sin_family=AF_INET;
178 
179 
180 
181     int i;
182     for(i=0; i<3; i++){
183        if(i==0) printf("正在Ping %s 具有 %d 字节的数据:\n", inet_ntoa(DestAddr.sin_addr), 32);
184 
185         FillPackage(IcmpBuffer, DestAddr.sin_addr.s_addr/*dstIp*/, (unsigned short)pid);
186 
187         Result=sendto(IcmpSocket, IcmpBuffer, PACKAGE_SIZE, 0, (struct sockaddr*)&DestAddr, sizeof(DestAddr));
188         if(Result==SOCKET_ERROR){
189             HandleError("sendto");
190             break;
191         }
192 
193         while (1){
194             tv.tv_sec=3;
195             tv.tv_usec=0;
196             FD_ZERO(&readSet);
197             FD_SET(IcmpSocket,&readSet);
198 
199             int res=select(IcmpSocket+1,&readSet,NULL,NULL,&tv);
200             if(res==SOCKET_ERROR){
201                 HandleError("select");
202                 break;
203             }
204             if(res==0){
205                 if(!bBroadcast)
206                     printf("请求超时。\n");
207                 break;
208             }
209             if(FD_ISSET(IcmpSocket, &readSet)){
210                 memset(IcmpBuffer,0, PACKAGE_SIZE);
211                 memset(&from,0,sizeof(from));
212                 int len=sizeof(from);
213                 if(recvfrom(IcmpSocket, IcmpBuffer, PACKAGE_SIZE,0,(struct sockaddr*) &from, &len)==SOCKET_ERROR)
214                 {
215                     HandleError("recvfrom");
216                     break;
217                 }
218 
219                 IPHeader *pIPHdr=(IPHeader *) IcmpBuffer;
220                 ICMPHeader *pICMPHdr=(ICMPHeader*)(IcmpBuffer+sizeof(IPHeader));
221                 int nTime = GetTickCount() - pICMPHdr->timestamp;  
222                 if(pICMPHdr->id==htons((u_short) pid)&&pICMPHdr->seq==htons((u_short) pid)
223                     &&pICMPHdr->type==ICMP_ECHO_REPLY){
224                         printf("来自 %s 的回复: 字节=%d time=%dms\n",inet_ntoa(from.sin_addr), Result, pIPHdr->ttl);
225                         if (!bBroadcast) break;
226                 } 
227             }
228         }//end of while
229         Sleep(1000);
230     }
231 
232     //关闭套接字
233     closesocket(IcmpSocket);
234     WSACleanup();
235     system("pause");
236     return 0;
237 }