您的位置:首页 > 其它

使用 ICMP 和 RAW Sockets实现 ping 类

2017-12-15 20:47 495 查看
PS: Qt环境,实验可行。


来源:http://blog.csdn.net/frankiewang008/article/details/38010695

[cpp] view
plaincopy

//  

// Ping.h  

//  

  

#pragma pack(push)  

#pragma pack(1)  

  

#include <stdio.h>  

#include <stdlib.h>  

#include <stdarg.h>  

#include <winsock.h>  

  

class CPing  

{  

  #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;  

  

  // ICMP Echo Request  

  #define REQ_DATASIZE 32       // Echo Request Data size  

  typedef struct tagECHOREQUEST  

  {  

    ICMPHDR icmpHdr;  

    DWORD   dwTime;  

    char    cData[REQ_DATASIZE];  

  }ECHOREQUEST, *PECHOREQUEST;  

  

  // ICMP Echo Reply  

  typedef struct tagECHOREPLY  

  {  

    IPHDR   ipHdr;  

    ECHOREQUEST echoRequest;  

    char    cFiller[256];  

  }ECHOREPLY, *PECHOREPLY;  

  

private:  

  bool bInitWinSockOK;  

  u_short in_cksum(u_short *addr, int len);  

  int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr);  

  DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL);  

  int WaitForEchoReply(SOCKET s);  

  void Report(LPCTSTR format, ...);  

  

public:  

  CPing();  

  virtual ~CPing();  

  

  void ping(LPCSTR pstrHost);  

  

};  

  

#pragma pack(pop)  

 

 

[cpp] view
plaincopy

//  

// PING.CPP -- Ping program using ICMP and RAW Sockets  

//  

  

#include "ping.h"  

#pragma comment(lib, "ws2_32.lib")  

  

CPing::CPing()  

{  

  bInitWinSockOK = false;  

  

  // Init WinSock  

  WSADATA wsaData;  

  WORD wVersionRequested = MAKEWORD(1,1);  

  if ( WSAStartup(wVersionRequested, &wsaData) )  

  {  

    Report("/nError initializing WinSock/n");  

  }    

  else if (wsaData.wVersion != wVersionRequested)// Check version  

  {  

    Report("/nWinSock version not supported/n");  

  }  

  else  

  {  

    bInitWinSockOK = true;  

  }  

}  

  

CPing::~CPing()  

{  

  if(bInitWinSockOK)  

    WSACleanup();// Free WinSock  

}  

  

// ping()  

// Calls SendEchoRequest() and  

// RecvEchoReply() and retport results  

void CPing::ping(LPCSTR pstrHost)  

{  

  SOCKET      rawSocket;  

  LPHOSTENT lpHost;  

  struct    sockaddr_in saDest;  

  struct    sockaddr_in saSrc;  

  DWORD   dwTimeSent;  

  DWORD   dwElapsed;  

  u_char    cTTL;  

  int       nLoop;  

  int       nRet;  

  

  if(!bInitWinSockOK)  

  {  

    Report("/nWinSock must be initializing/n");  

    return;  

  }  

  

  // Create a Raw socket  

  rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  

  if (rawSocket == SOCKET_ERROR)   

  {  

    Report("socket() error: %d/n", WSAGetLastError());  

    return;  

  }  

  

  // Lookup host  

  lpHost = gethostbyname(pstrHost);  

  if (lpHost == NULL)  

  {  

    Report("/nHost not found: %s/n", 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  

  Report("/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)  

    {  

      Report("select() error: %d/n", WSAGetLastError());  

      break;  

    }  

    if (!nRet)  

    {  

      Report("/nTimeOut");  

      break;  

    }  

  

    // Receive reply  

    dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);  

  

    // Calculate elapsed time  

    dwElapsed = GetTickCount() - dwTimeSent;  

    Report("/nReply from: %s: bytes=%d time=%ldms TTL=%d",   

      inet_ntoa(saSrc.sin_addr),   

      REQ_DATASIZE,  

      dwElapsed,  

      cTTL);  

  }  

  Report("/n");  

  nRet = closesocket(rawSocket);  

  if (nRet == SOCKET_ERROR)  

    Report("closesocket() error: %d/n", WSAGetLastError());  

}  

  

void CPing::Report(LPCSTR format, ...)  

{  

  char _Buff[8192];  

  memset(_Buff, 0, sizeof(_Buff));  

  

    va_list arg;    

    va_start(arg, format);  

    int charSize = _vsnprintf(_Buff, sizeof(_Buff), format, arg);  

    va_end(arg);  

    

  OutputDebugStringA(_Buff);  

}  

  

//  

//  Checksum routine for Internet Protocol family headers (C Version)  

u_short CPing::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);  

}  

  

// SendEchoRequest()  

// Fill in echo request header  

// and send to destination  

int CPing::SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)   

{  

  static ECHOREQUEST echoReq;  

  static u_short nId = 1;  

  static u_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] = ' '+nRet;  

  

  // 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)   

    Report("sendto() error: %d/n", WSAGetLastError());  

  return (nRet);  

}  

  

// RecvEchoReply()  

// Receive incoming data  

// and parse out fields  

DWORD CPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)   

{  

  ECHOREPLY echoReply;  

  int nRet;  

  int nAddrLen = sizeof(struct sockaddr_in);  

  

  // Receive the echo reply   

  nRet = recvfrom(s,                    // socket  

              (LPSTR)&echoReply,    // buffer  

              sizeof(ECHOREPLY),    // size of buffer  

              0,                    // flags  

              (LPSOCKADDR)lpsaFrom, // From address  

              &nAddrLen);           // pointer to address len  

  

  // Check return value  

  if (nRet == SOCKET_ERROR)   

    Report("recvfrom() error: %d/n", WSAGetLastError());  

  

  // return time sent and IP TTL  

  *pTTL = echoReply.ipHdr.TTL;  

  return(echoReply.echoRequest.dwTime);           

}  

  

// WaitForEchoReply()  

// Use select() to determine when  

// data is waiting to be read  

int CPing::WaitForEchoReply(SOCKET s)  

{  

  struct timeval Timeout;  

  fd_set readfds;  

  

  readfds.fd_count = 1;  

  readfds.fd_array[0] = s;  

  Timeout.tv_sec = 5;  

  Timeout.tv_usec = 0;  

  

  return(select(1, &readfds, NULL, NULL, &Timeout));  

}  

 

[cpp] view
plaincopy

//test code  

  CPing ping;  

  ping.ping( "www.sina.com.cn" );  

  

//output  

Pinging www.sina.com.cn [61.172.201.194] with 32 bytes of data:  

  

Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249  

Reply from: 61.172.201.194: bytes=32 time=16ms TTL=249  

Reply from: 61.172.201.194: bytes=32 time=0ms TTL=249  

Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249  

使用 ICMP 和 RAW Sockets实现 ping 类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: