目的
使用C++实现ping功能,用于在进行TCP/IP通讯前测试是否能与目标主机通讯。
原理
ping 命令是基于ICMP 协议来工作的,「ICMP」全称为Internet 控制报文协议( Internet Control Message Protocol)。基于ICMP协议向目标IP发送ICMP响应请求报文,目标主机收到这个报文之后,会向源IP回复一个ICMP响应应答报文。
C++实现
构建ICMPPing类实现上述功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #ifndef ICMPPING_H #define ICMPPING_H
#include <Winsock2.h> #include <iphlpapi.h>
#pragma comment(lib,"Iphlpapi.lib") #pragma comment(lib,"Ws2_32.lib")
class ICMPPing { private: typedef HANDLE(WINAPI* ICMPCREATEFILE)(VOID); typedef BOOL(WINAPI* ICMPCLOSEHANDLE)(HANDLE); typedef DWORD(WINAPI* ICMPSENDECHO)(HANDLE, DWORD, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); private: ICMPPing(); public: ~ICMPPing(); static ICMPPing* GetInstance(); BOOL Init(); BOOL Ping(const char* host); private: static ICMPPing* icmp_ping_; ICMPCREATEFILE pIcmpCreateFile_; ICMPCLOSEHANDLE pIcmpCloseHandle_; ICMPSENDECHO pIcmpSendEcho_; };
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include "icmp_ping.h"
ICMPPing* ICMPPing::icmp_ping_ = NULL;
ICMPPing::ICMPPing() : pIcmpCreateFile_(NULL), pIcmpCloseHandle_(NULL), pIcmpSendEcho_(NULL) { }
ICMPPing::~ICMPPing() { delete icmp_ping_; }
ICMPPing* ICMPPing::GetInstance() { if (icmp_ping_ == NULL) icmp_ping_ = new ICMPPing;
return icmp_ping_; }
BOOL ICMPPing::Init() { HINSTANCE hIcmp = LoadLibrary("ICMP.DLL"); if (hIcmp == NULL) { return false; } pIcmpCreateFile_ = (ICMPCREATEFILE)GetProcAddress(hIcmp, "IcmpCreateFile"); pIcmpCloseHandle_ = (ICMPCLOSEHANDLE)GetProcAddress(hIcmp, "IcmpCloseHandle"); pIcmpSendEcho_ = (ICMPSENDECHO)GetProcAddress(hIcmp, "IcmpSendEcho"); if ((pIcmpCreateFile_ == NULL) || (pIcmpCloseHandle_ == NULL) || (pIcmpSendEcho_ == NULL)) return false; return true; }
BOOL ICMPPing::Ping(const char* host) { DWORD timeOut = 1000; ULONG hAddr = inet_addr(host); if (hAddr == INADDR_NONE) { hostent* hp = gethostbyname(host); if (hp) memcpy(&hAddr, hp->h_addr_list, hp->h_length); else { return false; } } HANDLE hIp = pIcmpCreateFile_(); IP_OPTION_INFORMATION ipoi; memset(&ipoi, 0, sizeof(IP_OPTION_INFORMATION)); ipoi.Ttl = 128;
unsigned char pSend[36]; memset(pSend, 'E', 32);
int repSize = sizeof(ICMP_ECHO_REPLY) + 32; unsigned char pReply[100]; ICMP_ECHO_REPLY* pEchoReply = (ICMP_ECHO_REPLY*)pReply;
DWORD nPackets = pIcmpSendEcho_(hIp, hAddr, pSend, 32, &ipoi, pReply, repSize, timeOut);
if (pEchoReply->Status != 0) { pIcmpCloseHandle_(hIp); return false; }
pIcmpCloseHandle_(hIp); return true; }
|
该类使用单例模式,在主程序中使用时,通过调用GetInstance()
函数返回类指针对象;之后通过Init()
函数进行初始化,并判断返回值是否为false;最后调用Ping()
函数验证是否能和目标主机进行通讯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "icmp_ping.h" #include <iostream>
int main() { ICMPPing* icmp_ping = ICMPPing::GetInstance();
if (!icmp_ping->Init()) std::cout << "init error!" << std::endl;
if (icmp_ping->Ping("192.168.0.12")){ std::cout << "ping success!" << std::endl; } else{ std::cout << "ping fail!" << std::endl; }
return 0; }
|
注意
若该类同时与其他包含有<Windows.h>的文件放在同一项目下编译时,会报重定义的错误,这是因为<Windows.h>中包含了<Winsock.h>头文件,而本类包含了<Winsock2.h>文件,因此会有重定义。解决方案:
在项目属性 ——> C/C++ ———> 预处理器 -------> 预处理器定义
增加如下定义
WIN32_LEAN_AND_MEAN
致谢
代码参考自http://www.cnblogs.com/guoyz1314/p/3527340.html