//ping.h #ifndef _CPING_H_ #define _CPING_H_ #include <Winsock2.h> #include <Windows.h> #pragma pack(1) #define ICMP_ECHOREPLY 0 #define ICMP_ECHOREQ 8 #define REQ_DATASIZE 32 // Echo 請求數據的大小 class CPing { public: //ping host, nRetries-ping次數 bool Ping(LPCSTR pstrHost, UINT nRetries = 4); void Result(int* nElapseTime, float* fMissPack = NULL, u_char* cTTL = NULL); //void Result(CPing::REQ_RESULT& result); private: int WaitForEchoReply(SOCKET s); //ICMP回應的請求和回答函數 int SendEchoRequest(SOCKET, LPSOCKADDR_IN); DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *); u_short in_cksum(u_short *addr, int len); private: struct REQ_RESULT { int nElapseTime; //請求響應時間。 u_char cTTL; //請求TTL(生存時間) float fMissPack; //丟包率 }; REQ_RESULT m_Result; }; // 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 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; #pragma pack() #endif
//ping.cpp #include "ping.h" #pragma comment(lib, "ws2_32.lib") bool CPing::Ping(LPCSTR pstrHost, UINT nRetries) { //創建一個Raw套節字 SOCKET rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (rawSocket == INVALID_SOCKET) { int err = WSAGetLastError(); return false; } int nNetTimeout = 1000;//1秒 //發送時限 setsockopt(rawSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int)); //接收時限 setsockopt(rawSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int)); //獲得主機信息 LPHOSTENT lpHost = gethostbyname(pstrHost); if (lpHost == NULL) { return false; } //構造目標套節字地址信息 struct sockaddr_in saDest; struct sockaddr_in saSrc; saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr)); saDest.sin_family = AF_INET; saDest.sin_port = 3077;//0; DWORD dwTimeSent; u_char cTTL; int nRet; int nRecvNum = 0; int nTotalTime = 0; //多次ping for (UINT nLoop = 0; nLoop < nRetries; ++nLoop) { //發送ICMP回應請求 if ((nRet = SendEchoRequest(rawSocket, &saDest)) < 0) { break; } if ((nRet = WaitForEchoReply(rawSocket)) == SOCKET_ERROR) { break; } if (nRet) { //獲得回應 if ( (dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL)) < 0) { nRet = dwTimeSent; break; } //計算時間 nTotalTime += GetTickCount() - dwTimeSent; //Sleep(1000); ++nRecvNum; } } closesocket(rawSocket); if (nRecvNum > 0 && nRet >= 0) { m_Result.nElapseTime = nTotalTime/nRetries; m_Result.cTTL = cTTL; m_Result.fMissPack = (float)(nRetries - nRecvNum)/nRetries; return true; } return false; } //發送ICMPECHO數據包請求 int CPing::SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr) { static ECHOREQUEST echoReq; static int nId = 1; static int nSeq = 1; int nRet; //構造回應請求 echoReq.icmpHdr.Type = ICMP_ECHOREQ; echoReq.icmpHdr.Code = 0; echoReq.icmpHdr.Checksum = 0; echoReq.icmpHdr.ID = nId++; echoReq.icmpHdr.Seq = nSeq++; for (nRet = 0; nRet < REQ_DATASIZE; nRet++) echoReq.cData[nRet] = ' '+nRet; //保存發送時間 echoReq.dwTime = GetTickCount(); echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST)); //發送請求 nRet = sendto(s, (LPSTR)&echoReq, sizeof(ECHOREQUEST), 0, (LPSOCKADDR)lpstToAddr, sizeof(SOCKADDR_IN)); //檢查返回值 if (nRet == SOCKET_ERROR) { } return (nRet); } //接收ICMPECHO數據包回應 DWORD CPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL) { ECHOREPLY echoReply; int nRet; int nAddrLen = sizeof(struct sockaddr_in); //接收請求回應 nRet = recvfrom(s, (LPSTR)&echoReply, sizeof(ECHOREPLY), 0, (LPSOCKADDR)lpsaFrom, &nAddrLen); //檢查返回值 if (nRet == SOCKET_ERROR) { return nRet; } //返回發送的時間 *pTTL = echoReply.ipHdr.TTL; return(echoReply.echoRequest.dwTime); } //等待回應 int CPing::WaitForEchoReply(SOCKET s) { struct timeval Timeout; fd_set readfds; readfds.fd_count = 1; readfds.fd_array[0] = s; Timeout.tv_sec = 1; Timeout.tv_usec = 0; return(select(1, &readfds, NULL, NULL, &Timeout)); } //轉換地址 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; while( nleft > 1 ) { sum += *w++; nleft -= 2; } if( nleft == 1 ) { u_short u = 0; *(u_char *)(&u) = *(u_char *)w ; sum += u; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } void CPing::Result(int* nElapseTime, float* fMissPack, u_char* cTTL) { if (nElapseTime) { *nElapseTime = m_Result.nElapseTime; } if (fMissPack) { *fMissPack = m_Result.fMissPack; } if (cTTL) { *cTTL = m_Result.cTTL; } }
//main.cpp #include <stdlib.h> #include <iostream> #include <string> #include "ping.h" int main(int argn, char *argv[]) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); CPing ping; bool bResult = false; if (argn == 2) { bResult = ping.Ping(argv[1]); } else { bResult = ping.Ping("www.baidu.com"); } std::cout << "result : " << bResult << std::endl; if (bResult) { int nTime; u_char nTTL; float fMiss; ping.Result(&nTime, &fMiss, &nTTL); std::cout << "time : " << nTime << " TTL : " << (int)nTTL << " miss : " << fMiss*100 << "% " << std::endl; } return 0; }