| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
wdey
10年前发布

用C++自己实现Ping程序

//  // Ping.h  //      #pragma pack(1)      #define ICMP_ECHOREPLY  0  #define ICMP_ECHOREQ    8      // IP Header -- RFC 791  typedef struct tagIPHDR  {      u_char  VIHL;           // Version and IHL      u_char  TOS;            // Type Of Service      short   TotLen;         // Total Length      short   ID;             // Identification      short   FlagOff;        // Flags and Fragment Offset      u_char  TTL;            // Time To Live      u_char  Protocol;       // Protocol      u_short Checksum;       // Checksum      struct  in_addr iaSrc;  // Internet Address - Source      struct  in_addr iaDst;  // Internet Address - Destination  }IPHDR, *PIPHDR;          // ICMP Header - RFC 792  typedef struct tagICMPHDR  {      u_char  Type;           // Type      u_char  Code;           // Code      u_short Checksum;       // Checksum      u_short ID;             // Identification      u_short Seq;            // Sequence      //char  Data;           // Data  }ICMPHDR, *PICMPHDR;          #define REQ_DATASIZE 36          #define REQ_datasize2 30         #define Renum   66  // ICMP Echo Request  typedef struct tagECHOREQUEST  {      ICMPHDR icmpHdr;      DWORD   dwTime;      char    cData[REQ_DATASIZE];  }ECHOREQUEST, *PECHOREQUEST;  typedef struct tempECHREQUEST{      ICMPHDR icmpHdr;      DWORD   dwTime;      char    cData[Renum];  }tempECHREQUEST,*PtempECHOREQUEST;  typedef struct tempECHREQUEST2{      ICMPHDR icmpHdr;      DWORD   dwTime;      char    cData[Renum+8];  }tempecho,*Ptempecho;  // ICMP Echo Reply  typedef struct tagECHOREPLY  {      IPHDR   ipHdr;      ECHOREQUEST echoRequest;      char    cFiller[256];  }ECHOREPLY, *PECHOREPLY;  //IP echo request  typedef struct tagIPEchorequest{      IPHDR ipHeader;      ECHOREQUEST icmp_data;  }IPECHOREQUET,*PIPECHOREQUEST;  typedef struct tagIPEchoTwo{      IPHDR ipHeader;      char cData[REQ_datasize2];  }IPEchoTwo,*PIPEchoTwo;  typedef struct tagIPechoTwo2{      IPHDR ipHeader;      char cData[REQ_datasize2+8];  }IPechotwo2,*PIPechotwo2;  //ip echo reply  typedef struct tagIPREPLY{      IPHDR       ipHdr;      ECHOREQUEST echoRequest;      char        cFiller[256];  }IPECHOREPLY,*PIPECHOREPLY;  #pragma pack()                          // PING.C -- Ping program using ICMP and RAW Sockets  //  #include <stdio.h>  #include <stdlib.h>  #include <winsock2.h>  #include "ws2tcpip.h"  #include <iostream>      #pragma  comment(lib,"WS2_32.lib")  #include "ping.h"      // Internal Functions  void Ping(LPCSTR pstrHost);  void ReportError(LPCSTR pstrFrom);  int  WaitForEchoReply(SOCKET s);  u_short in_cksum(u_short *addr, int len);      // ICMP Echo Request/Reply functions  int     SendEchoRequest(SOCKET, LPSOCKADDR_IN);  DWORD   RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);      //IP internal function  void sendIP(LPCSTR pstrHost);          int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr);  int SendIPEchoRequest(SOCKET, LPSOCKADDR_IN);      static short nId = 1;  static short nSeq = 1;  static short ip_id = rand()%256;  static char DataToSend[Renum];  static short flag1 = 0;             //标记,0标识不重复,1标识有重复  static short flag2 = 0;             //标记,0标识顺序,1标识逆序  char sourseIP[16];  // main()  void main(int argc, char **argv)  {      WSADATA wsaData;      WORD wVersionRequested = MAKEWORD(2,1);      int nRet;          // Check arguments      if (argc != 2)      {          fprintf(stderr,"nUsage: ping hostnamen");          return;      }          // Init WinSock如果成功,则返回0      nRet = WSAStartup(wVersionRequested, &wsaData);      if (nRet)      {          fprintf(stderr,"nError initializing WinSockn");          return;      }          // Check version      if (wsaData.wVersion != wVersionRequested)      {          fprintf(stderr,"nWinSock version not supportedn");          return;      }          printf("        n输入目的主机IP地址:n");      gets(argv[1]);      sendIP(argv[1]);              system("pause");      // Free WinSock      WSACleanup();  }      // Ping()  // Calls SendEchoRequest() and  // RecvEchoReply() and prints results  void Ping(LPCSTR pstrHost)  {      SOCKET    rawSocket;            //原始套接字      LPHOSTENT lpHost;               //保存目的主机信息      struct    sockaddr_in saDest;   //目的主机      struct    sockaddr_in saSrc;    //源主机      DWORD     dwTimeSent;           //发送时间      DWORD     dwElapsed;            //时间间隔      u_char    cTTL;                 //ttl      int       nLoop;                //发送循环的ping的次数      int       nRet;          // Create a Raw socket      rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);      if (rawSocket == SOCKET_ERROR)      {          ReportError("socket()");          return;      }              // Lookup host      lpHost = gethostbyname(pstrHost);            if (lpHost == NULL)      {          fprintf(stderr,"nHost not found: %sn", pstrHost);          return;      }              // Setup destination socket address      saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));      saDest.sin_family = AF_INET;      saDest.sin_port = 0;          // Tell the user what we're doing      printf("nPinging %s [%s] with %d bytes of data:n",                  pstrHost,                  inet_ntoa(saDest.sin_addr),                  REQ_DATASIZE);          // Ping multiple times      for (nLoop = 0; nLoop < 4; nLoop++)      {          // Send ICMP echo request          SendEchoRequest(rawSocket, &saDest);              // Use select() to wait for data to be received          nRet = WaitForEchoReply(rawSocket);          if (nRet == SOCKET_ERROR)          {              ReportError("select()");              break;          }          if (!nRet)          {              printf("nTimeOut");              break;          }              // Receive reply          dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);              // Calculate elapsed time          dwElapsed = GetTickCount() - dwTimeSent;          printf("nReply from: %s: bytes=%d time=%ldms TTL=%d",                 inet_ntoa(saSrc.sin_addr),                 REQ_DATASIZE,                 dwElapsed,                 cTTL);      }      printf("n");      nRet = closesocket(rawSocket);      if (nRet == SOCKET_ERROR)          ReportError("closesocket()");  }  /*  使用原始套接字编程,实现分片IP数据包的构造,能够用两个IP分片  构成ICMP  ECHO请求,并接收另一方协议栈返回的ICMP应答。  */  void sendIP(LPCSTR pstrHost){          SOCKET    rawSocket;            //原始套接字      LPHOSTENT lpHost;               //保存目的主机信息      struct    sockaddr_in saDest;   //目的主机      struct    sockaddr_in saSrc;    //源主机      DWORD     dwTimeSent;           //发送时间      DWORD     dwElapsed;            //时间间隔      u_char    cTTL;                 //ttl      //int       nLoop;              //发送循环的ping的次数,这里是需要分片的片数目      int       nRet;          // Create a Raw socket创建IP数据包的套接字      rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP);      if (rawSocket == SOCKET_ERROR)      {          ReportError("socket()");          return;      }      //设置套接字选项      BOOL blflag = true;      int set_return = setsockopt(rawSocket,          IPPROTO_IP,          IP_HDRINCL,          (char*)&blflag,          sizeof(blflag));      if (set_return)      {          printf("设置选项失败!%d",set_return);          return;      }      if (set_return == SOCKET_ERROR)      {          printf("设置选项错误!");          return;      }          // Lookup host      lpHost = gethostbyname(pstrHost);            if (lpHost == NULL)      {          fprintf(stderr,"nHost not found: %sn", pstrHost);          return;      }      printf("n输入本机IP地址:n");      gets(sourseIP);      // Setup destination socket address      saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));      saDest.sin_family = AF_INET;      saDest.sin_port = 0;          // Tell the user what we're doing      printf("n发送数据包(2个分片第一片)到 %s --[%s] 数据大小是%d :n",                          pstrHost,                          inet_ntoa(saDest.sin_addr),                          REQ_DATASIZE);      // Ping multiple times      printf("n分片发送,不重叠输入0,重叠输入1:n");      std::cin>>flag1;      printf("n分片发送,两片顺序发送输入0,逆序发送输入1:n");      std::cin>>flag2;              if (flag2==0)      {          SendIPEchoRequest(rawSocket,&saDest);          SendIPEchoRequestFragmentTwo(rawSocket,&saDest);      }      if (flag2==1)      {          SendIPEchoRequestFragmentTwo(rawSocket,&saDest);          SendIPEchoRequest(rawSocket,&saDest);      }      // Use select() to wait for data to be received      nRet = WaitForEchoReply(rawSocket);      if (nRet == SOCKET_ERROR)          ReportError("select()");              if (!nRet)          printf("nTimeOut");      // Receive reply//////////////////////////////////////////////      dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);      printf("n检查收到的时间:%dn",dwTimeSent);      // Calculate elapsed time              dwElapsed = GetTickCount() - dwTimeSent;      printf("n当前的时间 :%dn",GetTickCount());      printf("nReply from: %s: bytes=%d time=%ldms TTL=%d",      inet_ntoa(saSrc.sin_addr),              REQ_DATASIZE,              dwElapsed,              cTTL);              printf("n");      nRet = closesocket(rawSocket);      if (nRet == SOCKET_ERROR)          ReportError("closesocket()");  }      // SendEchoRequest()  // Fill in echo request header  // and send to destination  int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)  {      static ECHOREQUEST echoReq;                  static short nId = 1;      static short nSeq = 1;      int nRet;          // Fill in echo request      echoReq.icmpHdr.Type        = ICMP_ECHOREQ;      echoReq.icmpHdr.Code        = 0;      echoReq.icmpHdr.Checksum    = 0;      echoReq.icmpHdr.ID          = nId++;      echoReq.icmpHdr.Seq         = nSeq++;          // Fill in some data to send      for (nRet = 0; nRet < REQ_DATASIZE; nRet++)          echoReq.cData[nRet] = 'a'+nRet;          //  printf("nn%dnn",sizeof(echoReq));      // Save tick count when sent      echoReq.dwTime              = GetTickCount();          // Put data in packet and compute checksum      echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));          // Send the echo request                                       nRet = sendto(s,                        /* socket */          (LPSTR)&echoReq,            /* buffer */          sizeof(ECHOREQUEST),          0,                          /* flags */          (LPSOCKADDR)lpstToAddr, /* destination */          sizeof(SOCKADDR_IN));   /* address length */          if (nRet == SOCKET_ERROR)          ReportError("sendto()");      return (nRet);  }      /*发送第一片数据*/  int SendIPEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr){          static IPECHOREQUET ipechoReq;              //第一片的IP头部处理过程      ipechoReq.ipHeader.VIHL = 0x45;                                 //版本是4,首部长度是5      ipechoReq.ipHeader.TOS = 0x0;                                   //由标准组织分配      ipechoReq.ipHeader.ID = ip_id;                                  //ip的标识(一唯),这里一定要注意,ip标识不变,同一个数据包的两片      ipechoReq.ipHeader.TTL = 64;                                    //网络中的最大寿命              ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP);          //源主机地址      ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr;             //      ipechoReq.ipHeader.Protocol = IPPROTO_ICMP;                     //协议的类型包含ICMP      ipechoReq.ipHeader.FlagOff = htons(0x2000);                     //第一片标志位是001000000000000      ipechoReq.ipHeader.TotLen = sizeof(IPECHOREQUET);               //总产度设置成64,数据部分应该是52              ipechoReq.ipHeader.Checksum = 0;                                //          //IP包头校验和计算      ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR));          // Fill in echo request      ipechoReq.icmp_data.icmpHdr.Type        = ICMP_ECHOREQ;         //请求数据包类型      ipechoReq.icmp_data.icmpHdr.Code        = 0;                    //      ipechoReq.icmp_data.icmpHdr.Checksum    = 0;                    //检验和字段      ipechoReq.icmp_data.icmpHdr.ID          = nId++;                //成对出现的      ipechoReq.icmp_data.icmpHdr.Seq         = nSeq++;               //成对出现          // Save tick count when sent      ipechoReq.icmp_data.dwTime              = GetTickCount();          printf("n发送的时间:%dn",ipechoReq.icmp_data.dwTime);      memset(DataToSend,0,Renum);      //ICMP头部填充////////////////////////////////////////////////////////////////////////////      int nRet;      for (nRet = 0;nRet < REQ_DATASIZE;nRet++)          DataToSend[nRet] = 'a';      for(nRet = REQ_DATASIZE;nRet<Renum;nRet++)          DataToSend[nRet] = 'b';          //第一片需要发送的数据      for (nRet = 0;nRet < REQ_DATASIZE;nRet++)          ipechoReq.icmp_data.cData[nRet] = DataToSend[nRet];              //计算校验和的部分构成      if (flag1 == 0)      {          tempECHREQUEST tempecho;          memset(tempecho.cData,0,Renum);          tempecho.icmpHdr = ipechoReq.icmp_data.icmpHdr;          tempecho.dwTime = ipechoReq.icmp_data.dwTime;          memcpy(tempecho.cData,DataToSend,Renum);              // Put data in packet and compute checksum          ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&tempecho,              sizeof(tempECHREQUEST));      }      if (flag1==1)      {          tempecho    temp;          memset(temp.cData,0,Renum+8);          temp.dwTime = ipechoReq.icmp_data.dwTime;          temp.icmpHdr = ipechoReq.icmp_data.icmpHdr;          memcpy(temp.cData,DataToSend,REQ_DATASIZE);                 //第一片复制          memcpy(temp.cData+REQ_DATASIZE,DataToSend+REQ_DATASIZE-8,REQ_datasize2);          ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&temp,                                                          sizeof(tempecho));      }      //发送echo 请求      nRet = sendto(s,                          (LPSTR)&ipechoReq,                          sizeof(IPECHOREQUET),                          0,                          (LPSOCKADDR)lpstToAddr,                          sizeof(SOCKADDR_IN));      if (nRet == SOCKET_ERROR)          ReportError("sendto()");      return nRet;  }  /*  发送第二片数据  */  int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr){      IPEchoTwo ipechoReq;      //第二片的IP头部处理过程          ipechoReq.ipHeader.VIHL = 0x45;                                 //版本是4,首部长度是5      ipechoReq.ipHeader.TOS = 0x0;                                   //由标准组织分配      ipechoReq.ipHeader.ID = ip_id;                                  //ip的标识(一唯)      ipechoReq.ipHeader.TTL = 64;                                    //网络中的最大寿命      ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP);  //源主机地址      ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr;             //      ipechoReq.ipHeader.Protocol = IPPROTO_ICMP;                     //协议的类型包含ICMP      //////////////////////////////////////////////////////////////////////////////////////      if (flag1 == 0)          ipechoReq.ipHeader.FlagOff = htons(0x6);                        //第二片标志位是      if (flag1 == 1)          ipechoReq.ipHeader.FlagOff = htons(0x5);                        //第二片标志位是      ipechoReq.ipHeader.TotLen = sizeof(IPEchoTwo);                  //总产度设置成64,数据部分应该是44          ipechoReq.ipHeader.Checksum = 0;                                //          //IP包头校验和计算      ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR));          //ICMP头部填充      int nRet;              //填充其他的数据部分/////////////////////////////////////////////////////////////////////////////      if (flag1 == 0){          memcpy(ipechoReq.cData,DataToSend+REQ_DATASIZE,REQ_datasize2);          nRet = sendto(s,                          (LPSTR)&ipechoReq,                          sizeof(IPEchoTwo),                          0,                          (LPSOCKADDR)lpstToAddr,                          sizeof(SOCKADDR_IN));      }      if (flag1 == 1){          IPechotwo2 ipecho;          ipecho.ipHeader = ipechoReq.ipHeader;          memset(ipecho.cData,0,REQ_datasize2+8);          memcpy(ipecho.cData,DataToSend+REQ_DATASIZE-8,REQ_datasize2+8);          nRet = sendto(s,                          (LPSTR)&ipechoReq,                          sizeof(IPechotwo2),                          0,                          (LPSOCKADDR)lpstToAddr,                          sizeof(SOCKADDR_IN));          }      //发送echo 请求              if (nRet == SOCKET_ERROR)          ReportError("sendto()");      return nRet;  }  // RecvEchoReply()  // Receive incoming data  // and parse out fields  DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)  {      IPECHOREPLY     ipreply;      int nRet;      int nAddrLen = sizeof(struct sockaddr_in);          // Receive the echo reply        nRet = recvfrom(s,                          // socket                      (LPSTR)&ipreply,            // buffer                      sizeof(IPECHOREPLY),        // size of buffer                      0,                          // flags                      (LPSOCKADDR)lpsaFrom,       // From address                      &nAddrLen);                 // pointer to address len          // Check return value      if (nRet == SOCKET_ERROR)          ReportError("recvfrom()");          // return time sent and IP TTL      *pTTL = ipreply.ipHdr.TTL;      return(ipreply.echoRequest.dwTime);          }      // What happened?  void ReportError(LPCSTR pWhere)  {      fprintf(stderr,"n%s error: %dn",          WSAGetLastError());  }      // WaitForEchoReply()  // Use select() to determine when  // data is waiting to be read  int WaitForEchoReply(SOCKET s)  {      struct timeval Timeout;      fd_set readfds;      /*fd_set结构体的定义      typedef struct fd_set{          u_int   fd_count;               //集合中的socket数量          SOCKET  fd_array[FD_SETSIZE];   //集合中包含的Socket数组      }fd_set;      */      readfds.fd_count = 1;      readfds.fd_array[0] = s;      Timeout.tv_sec = 5;      Timeout.tv_usec = 0;          return(select(1, &readfds, NULL, NULL, &Timeout));  }          //  // Mike Muuss' in_cksum() function  // and his comments from the original  // ping program  //  // * Author -  // *    Mike Muuss  // *    U. S. Army Ballistic Research Laboratory  // *    December, 1983      /*   *          I N _ C K S U M   *   * Checksum routine for Internet Protocol family headers (C Version)   *   */  u_short in_cksum(u_short *addr, int len)  {      register int nleft = len;      register u_short *w = addr;      register u_short answer;      register int sum = 0;          /*       *  Our algorithm is simple, using a 32 bit accumulator (sum),       *  we add sequential 16 bit words to it, and at the end, fold       *  back all the carry bits from the top 16 bits into the lower       *  16 bits.       */      while( nleft > 1 )  {          sum += *w++;          nleft -= 2;      }          /* mop up an odd byte, if necessary */      if( nleft == 1 ) {          u_short u = 0;              *(u_char *)(&u) = *(u_char *)w ;          sum += u;      }          /*       * add back carry outs from top 16 bits to low 16 bits       */      sum = (sum >> 16) + (sum & 0xffff);   /* add hi 16 to low 16 */      sum += (sum >> 16);           /* add carry */      answer = ~sum;              /* truncate to 16 bits */      return (answer);  }