微网站的建设模板有哪些内容,国内做优秀的农业信息网站,淮安网站建设案例,文艺主题wordpressPing 使用 Internet 控制消息协议#xff08;ICMP#xff09;来测试主机之间的连接。当用户发送一个 ping 请求时#xff0c;则对应的发送一个 ICMP Echo 请求消息到目标主机#xff0c;并等待目标主机回复一个 ICMP Echo 回应消息。如果目标主机接收到请求并且网络连接正常…Ping 使用 Internet 控制消息协议ICMP来测试主机之间的连接。当用户发送一个 ping 请求时则对应的发送一个 ICMP Echo 请求消息到目标主机并等待目标主机回复一个 ICMP Echo 回应消息。如果目标主机接收到请求并且网络连接正常则会返回一个回应消息表示主机之间的网络连接是正常的。如果目标主机没有收到请求消息或网络连接不正常则不会有回应消息返回。
编译报错问题解决
在Windows环境下编程不可避免的会用到windows.h和winsock.h头文件在默认情况下windows.h头文件会包含winsock.h此时当尝试包含winsock.h时就会出现头文件定义冲突的情况。解决这个冲突的方式有两种第一种在头部定义#define WIN32_LEAN_AND_MEAN来主动去除winsock.h头文件包含。第二种是将#include winsock2.h头文件放在#includewindows.h之前。两种方式均可这些方法在进行Windows套接字编程时非常重要可以防止头文件冲突确保编译顺利进行。
Ping头文件
如下头文件代码定义了几个结构体用于表示IP协议头、ICMP协议头和Ping的回复信息。这些结构体主要用于网络编程中解析和构建网络数据包。
#pragma once
#include winsock2.h
#pragma comment(lib, WS2_32)#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0struct IPHeader
{BYTE m_byVerHLen; // 4位版本4位首部长度BYTE m_byTOS; // 服务类型USHORT m_usTotalLen; // 总长度USHORT m_usID; // 标识USHORT m_usFlagFragOffset; // 3位标志13位片偏移BYTE m_byTTL; // TTLBYTE m_byProtocol; // 协议USHORT m_usHChecksum; // 首部检验和ULONG m_ulSrcIP; // 源IP地址ULONG m_ulDestIP; // 目的IP地址
};struct ICMPHeader
{BYTE m_byType; // 类型BYTE m_byCode; // 代码USHORT m_usChecksum; // 检验和 USHORT m_usID; // 标识符USHORT m_usSeq; // 序号ULONG m_ulTimeStamp; // 时间戳非标准ICMP头部
};struct PingReply
{USHORT m_usSeq; // 来源IPDWORD m_dwRoundTripTime; // 时间戳DWORD m_dwBytes; // 返回长度DWORD m_dwTTL; // TTL值
};class CPing
{
public:CPing(); // 构造函数~CPing(); // 析构函数// 执行 Ping 操作的方法传入目标 IP 地址或域名、PingReply 结构体和超时时间BOOL Ping(DWORD dwDestIP, PingReply *pPingReply NULL, DWORD dwTimeout 2000);BOOL Ping(char *szDestIP, PingReply *pPingReply NULL, DWORD dwTimeout 2000);private:// Ping 核心方法传入目标 IP 地址、PingReply 结构体和超时时间BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);// 计算检验和的方法传入缓冲区和大小USHORT CalCheckSum(USHORT *pBuffer, int nSize);// 获取时钟计时器的校准值ULONG GetTickCountCalibrate();private:SOCKET m_sockRaw; // 原始套接字WSAEVENT m_event; // WSA 事件USHORT m_usCurrentProcID; // 当前进程 IDchar *m_szICMPData; // ICMP 数据BOOL m_bIsInitSucc; // 初始化是否成功private:static USHORT s_usPacketSeq; // 静态变量用于记录 ICMP 包的序列号
};下面是对每个结构体成员的简要说明
IPHeader 结构体 m_byVerHLen: 4位版本号 4位首部长度。m_byTOS: 服务类型。m_usTotalLen: 总长度。m_usID: 标识。m_usFlagFragOffset: 3位标志 13位片偏移。m_byTTL: 生存时间。m_byProtocol: 协议类型。m_usHChecksum: 首部检验和。m_ulSrcIP: 源IP地址。m_ulDestIP: 目的IP地址。 ICMPHeader 结构体 m_byType: ICMP类型。m_byCode: ICMP代码。m_usChecksum: 检验和。m_usID: 标识符。m_usSeq: 序号。m_ulTimeStamp: 时间戳非标准ICMP头部。 PingReply 结构体 m_usSeq: 序列号。m_dwRoundTripTime: 往返时间。m_dwBytes: 返回长度。m_dwTTL: TTL值。
这些结构体主要用于在网络编程中处理与IP、ICMP和Ping相关的数据包。在实际应用中可以使用这些结构体来解析接收到的网络数据包或者构建要发送的数据包。
类成员说明
m_sockRaw: 用于发送原始套接字的成员变量。m_event: WSA 事件。m_usCurrentProcID: 当前进程 ID。m_szICMPData: ICMP 数据。m_bIsInitSucc: 初始化是否成功的标志。s_usPacketSeq: 静态变量用于记录 ICMP 包的序列号。
类方法说明
Ping: 执行 Ping 操作的方法可以传入目标 IP 地址或域名、PingReply 结构体和超时时间。PingCore: Ping 核心方法用于发送 ICMP 数据包计算往返时间等。CalCheckSum: 计算检验和的方法。GetTickCountCalibrate: 获取时钟计时器的校准值。
MyPing实现
1. CPing 构造函数和析构函数
CPing::CPing() : m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{// ...省略其他初始化代码m_szICMPData (char*)malloc(DEF_PACKET_SIZE sizeof(ICMPHeader));if (m_szICMPData NULL){m_bIsInitSucc FALSE;}
}CPing::~CPing()
{WSACleanup();if (NULL ! m_szICMPData){free(m_szICMPData);m_szICMPData NULL;}
}构造函数中首先进行 Winsock 初始化创建原始套接字并分配内存用于存储 ICMP 数据。如果分配内存失败则初始化标志 m_bIsInitSucc 置为 FALSE。析构函数负责清理 Winsock 资源和释放内存。
2. PingCore 函数
BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{// ...省略其他代码if (!m_bIsInitSucc){return FALSE;}// ...省略其他代码if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)sockaddrDest, nSockaddrDestSize) SOCKET_ERROR){return FALSE;}// ...省略其他代码char recvbuf[256] { \0 };while (TRUE){// ...省略其他代码if (WSAWaitForMultipleEvents(1, m_event, FALSE, 100, FALSE) ! WSA_WAIT_TIMEOUT){WSANETWORKEVENTS netEvent;WSAEnumNetworkEvents(m_sockRaw, m_event, netEvent);if (netEvent.lNetworkEvents FD_READ){// ...省略其他代码if (nPacketSize ! SOCKET_ERROR){IPHeader *pIPHeader (IPHeader*)recvbuf;USHORT usIPHeaderLen (USHORT)((pIPHeader-m_byVerHLen 0x0f) * 4);ICMPHeader *pICMPHeader (ICMPHeader*)(recvbuf usIPHeaderLen);if (pICMPHeader-m_usID m_usCurrentProcID pICMPHeader-m_byType ECHO_REPLY pICMPHeader-m_usSeq usSeq){// ...省略其他代码return TRUE;}}}}// ...省略其他代码if (GetTickCountCalibrate() - ulSendTimestamp dwTimeout){return FALSE;}}
}PingCore 函数是 Ping 工具的核心部分负责构建 ICMP 报文、发送报文、接收响应报文并进行超时处理。通过循环等待接收事件实时检测是否有 ICMP 响应报文到达。在接收到响应后判断响应是否符合预期条件如果符合则填充 pPingReply 结构体并返回 TRUE。
3. CalCheckSum 函数
USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
{unsigned long ulCheckSum 0;while (nSize 1){ulCheckSum *pBuffer;nSize - sizeof(USHORT);}if (nSize){ulCheckSum *(UCHAR*)pBuffer;}ulCheckSum (ulCheckSum 16) (ulCheckSum 0xffff);ulCheckSum (ulCheckSum 16);return (USHORT)(~ulCheckSum);
}CalCheckSum 函数用于计算 ICMP 报文的校验和。校验和的计算采用了累加和的方法最后对累加和进行溢出处理。计算完成后返回取反后的校验和。
4. GetTickCountCalibrate 函数
ULONG CPing::GetTickCountCalibrate()
{// ...省略其他代码return s_ulFirstCallTick (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}GetTickCountCalibrate 函数用于获取经过调校的系统时间。通过计算系统时间相对于 Ping 工具启动时的时间差实现对系统时间的校准。这样做是为了处理系统时间溢出的情况。
5. Ping 函数
BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{return PingCore(dwDestIP, pPingReply, dwTimeout);
}BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL ! szDestIP){return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);}return FALSE;
}Ping 函数是对 PingCore 函数的封装根据目标 IP 地址调用 PingCore 进行 Ping
最后的MyPing.cpp完整实现如下所示
#include MyPing.hUSHORT CPing::s_usPacketSeq 0;// 构造函数
CPing::CPing() :m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{WSADATA WSAData;if (WSAStartup(MAKEWORD(1, 1), WSAData) ! 0){// 如果初始化不成功则返回return;}m_event WSACreateEvent();m_usCurrentProcID (USHORT)GetCurrentProcessId();m_sockRaw WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);if (m_sockRaw INVALID_SOCKET){// 10013 以一种访问权限不允许的方式做了一个访问套接字的尝试return;}else{WSAEventSelect(m_sockRaw, m_event, FD_READ);m_bIsInitSucc TRUE;m_szICMPData (char*)malloc(DEF_PACKET_SIZE sizeof(ICMPHeader));if (m_szICMPData NULL){m_bIsInitSucc FALSE;}}
}// 析构函数
CPing::~CPing()
{WSACleanup();if (NULL ! m_szICMPData){free(m_szICMPData);m_szICMPData NULL;}
}// Ping 方法传入目标 IP 地址或域名、PingReply 结构体和超时时间
BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{return PingCore(dwDestIP, pPingReply, dwTimeout);
}// Ping 方法传入目标 IP 地址或域名、PingReply 结构体和超时时间
BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL ! szDestIP){return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);}return FALSE;
}// Ping 核心方法传入目标 IP 地址、PingReply 结构体和超时时间
BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{// 判断初始化是否成功if (!m_bIsInitSucc){return FALSE;}// 配置 SOCKETsockaddr_in sockaddrDest;sockaddrDest.sin_family AF_INET;sockaddrDest.sin_addr.s_addr dwDestIP;int nSockaddrDestSize sizeof(sockaddrDest);// 构建 ICMP 包int nICMPDataSize DEF_PACKET_SIZE sizeof(ICMPHeader);ULONG ulSendTimestamp GetTickCountCalibrate();USHORT usSeq s_usPacketSeq;memset(m_szICMPData, 0, nICMPDataSize);ICMPHeader *pICMPHeader (ICMPHeader*)m_szICMPData;pICMPHeader-m_byType ECHO_REQUEST;pICMPHeader-m_byCode 0;pICMPHeader-m_usID m_usCurrentProcID;pICMPHeader-m_usSeq usSeq;pICMPHeader-m_ulTimeStamp ulSendTimestamp;pICMPHeader-m_usChecksum CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);// 发送 ICMP 报文if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)sockaddrDest, nSockaddrDestSize) SOCKET_ERROR){return FALSE;}// 判断是否需要接收相应报文if (pPingReply NULL){return TRUE;}char recvbuf[256] { \0 };while (TRUE){// 接收响应报文if (WSAWaitForMultipleEvents(1, m_event, FALSE, 100, FALSE) ! WSA_WAIT_TIMEOUT){WSANETWORKEVENTS netEvent;WSAEnumNetworkEvents(m_sockRaw, m_event, netEvent);if (netEvent.lNetworkEvents FD_READ){ULONG nRecvTimestamp GetTickCountCalibrate();int nPacketSize recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)sockaddrDest, nSockaddrDestSize);if (nPacketSize ! SOCKET_ERROR){IPHeader *pIPHeader (IPHeader*)recvbuf;USHORT usIPHeaderLen (USHORT)((pIPHeader-m_byVerHLen 0x0f) * 4);ICMPHeader *pICMPHeader (ICMPHeader*)(recvbuf usIPHeaderLen);if (pICMPHeader-m_usID m_usCurrentProcID // 是当前进程发出的报文 pICMPHeader-m_byType ECHO_REPLY // 是 ICMP 响应报文 pICMPHeader-m_usSeq usSeq // 是本次请求报文的响应报文){pPingReply-m_usSeq usSeq;pPingReply-m_dwRoundTripTime nRecvTimestamp - pICMPHeader-m_ulTimeStamp;pPingReply-m_dwBytes nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);pPingReply-m_dwTTL pIPHeader-m_byTTL;return TRUE;}}}}// 超时if (GetTickCountCalibrate() - ulSendTimestamp dwTimeout){return FALSE;}}
}// 计算检验和的方法
USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
{unsigned long ulCheckSum 0;while (nSize 1){ulCheckSum *pBuffer;nSize - sizeof(USHORT);}if (nSize){ulCheckSum *(UCHAR*)pBuffer;}ulCheckSum (ulCheckSum 16) (ulCheckSum 0xffff);ulCheckSum (ulCheckSum 16);return (USHORT)(~ulCheckSum);
}// 获取时钟计时器的校准值
ULONG CPing::GetTickCountCalibrate()
{static ULONG s_ulFirstCallTick 0;static LONGLONG s_ullFirstCallTickMS 0;SYSTEMTIME systemtime;FILETIME filetime;GetLocalTime(systemtime);SystemTimeToFileTime(systemtime, filetime);LARGE_INTEGER liCurrentTime;liCurrentTime.HighPart filetime.dwHighDateTime;liCurrentTime.LowPart filetime.dwLowDateTime;LONGLONG llCurrentTimeMS liCurrentTime.QuadPart / 10000;if (s_ulFirstCallTick 0){s_ulFirstCallTick GetTickCount();}if (s_ullFirstCallTickMS 0){s_ullFirstCallTickMS llCurrentTimeMS;}return s_ulFirstCallTick (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}如何使用
在主程序中直接引入头文件MyPing.h并在main()函数中直接调用CPing类即可实现探测主机是否存活。
探测主机是否存活
#include MyPing.h
#include iostream// 探测主机是否存活
bool TestPing(char *szIP)
{CPing objPing;PingReply reply;objPing.Ping(szIP, reply);if (reply.m_dwTTL 10 reply.m_dwTTL 255){return true;}return false;
}int main(int argc, char *argv[])
{bool is_open TestPing(202.89.233.100);std::cout 本机是否存活: is_open std::endl;system(pause);return 0;
}运行效果如下所示 模拟系统Ping测试
#include MyPing.h
#include iostream// 模拟系统Ping测试
void SystemPing(char *szIP, int szCount)
{CPing objPing;PingReply reply;for (int x 0; x szCount; x){objPing.Ping(szIP, reply);std::cout 探测主机: szIP 默认字节: DEF_PACKET_SIZE 发送长度: reply.m_dwBytes 时间: reply.m_dwRoundTripTime TTL: reply.m_dwTTL std::endl;Sleep(1000);}
}int main(int argc, char *argv[])
{SystemPing(202.89.233.100, 5);system(pause);return 0;
}运行效果如下所示 参考资料
代码的实现来源于博客园Snser博主此处仅用于功能收录以便于后期在项目中应用。