【C++】C++ 实现ping功能

目的

使用C++实现ping功能,用于在进行TCP/IP通讯前测试是否能与目标主机通讯。

原理

ping 命令是基于ICMP 协议来工作的,「ICMP」全称为Internet 控制报文协议( Internet Control Message Protocol)。基于ICMP协议向目标IP发送ICMP响应请求报文,目标主机收到这个报文之后,会向源IP回复一个ICMP响应应答报文。

C++实现

构建ICMPPing类实现上述功能。

  • icmp_ping.h:
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();
// init ICMP function, return true for success, or for error
BOOL Init();
// ping host, return true for connect success, or for timeout
BOOL Ping(const char* host);
private:
static ICMPPing* icmp_ping_;
// pointer of function
ICMPCREATEFILE pIcmpCreateFile_;
ICMPCLOSEHANDLE pIcmpCloseHandle_;
ICMPSENDECHO pIcmpSendEcho_;
};

#endif
  • icmp_ping.cpp:
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