#include "Ping.h"
#include <assert.h>
using namespace netcomm;
CPing::CPing():
m_nSendCount(10)
{
ZeroMemory(m_szIcmpPacket, ICMP_PACKET_SIZE);
m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
assert(m_socket != INVALID_SOCKET);
SetRecvTimeout(50);
}
CPing::~CPing()
{
closesocket(m_socket);
}
DWORD g_dwSendTime, g_dwRecvTime;
/*****************************************************************************************
设置发送ICMP包的数量, 默认为4
*****************************************************************************************/
inline void CPing::SetSendCount(UINT nSendCount)
{
if (0 == nSendCount)
{
m_nSendCount = 4;
}
else
{
m_nSendCount = nSendCount;
}
}
/*****************************************************************************************
设置接收超时
*****************************************************************************************/
void CPing::SetRecvTimeout(int nTimeout)
{
setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeout, sizeof(nTimeout));
}
/*****************************************************************************************
解析目的地址
入口
strDest : 目的地址
出口
若解析成功,返回对应的addrinfo指针; 否则返回NULL
*****************************************************************************************/
addrinfo* CPing::ResolveAddress(const string& strDest)
{
struct addrinfo hints, *res = NULL;
int rc;
const char* pnodename = strDest.c_str();
memset(&hints, 0, sizeof(hints));
hints.ai_flags = ( pnodename ? 0 : AI_PASSIVE);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
rc = getaddrinfo(pnodename, NULL, &hints, &res);
if (rc != 0)
{
printf("Invalid address %s, getaddrinfo failed: %d\n", pnodename, rc);
return NULL;
}
return res;
}
/*****************************************************************************************
初始化ICMP报头
*****************************************************************************************/
void CPing::InitIcmpPacket()
{
ZeroMemory(m_szIcmpPacket, ICMP_PACKET_SIZE);
ICMP_HEADER* pIcmpHeader = (PICMP_HEADER)m_szIcmpPacket;
pIcmpHeader->icmp_type = ICMPV4_ECHO_REQUEST_TYPE; // request an ICMP echo
pIcmpHeader->icmp_code = ICMPV4_ECHO_REQUEST_CODE;
pIcmpHeader->icmp_id = (unsigned short)GetCurrentProcessId();
pIcmpHeader->icmp_timestamp = 0;
pIcmpHeader->icmp_checksum = 0;
pIcmpHeader->icmp_sequence = 0;
}
/*****************************************************************************************
设置ICMP报头
*****************************************************************************************/
void CPing::SetIcmpPacket(unsigned short uSequence /* = 0 */)
{
ICMP_HEADER* pIcmpHeader = (PICMP_HEADER)m_szIcmpPacket;
pIcmpHeader->icmp_timestamp= GetTickCount();
pIcmpHeader->icmp_sequence = uSequence;
pIcmpHeader->icmp_checksum = 0; //非常重要
pIcmpHeader->icmp_checksum = checksum((unsigned short*)m_szIcmpPacket, ICMP_PACKET_SIZE);
}
/*****************************************************************************************
解析收到的响应包
入口
pData : 收到的ICMP数据包
nDataLen: 数据包长度
出口
若是正确的ICMP响应包,返回true; 否则返回false
******************************************************************************************/
bool CPing::ParseIcmpPacket(const char* pData, int nDataLen)
{
assert(pData);
if (nDataLen < IP_HEADER_SIZE + ICMP_HEADER_SIZE)
{
return false;
}
IP_HEADER* pIpHeader = (PIP_HEADER)pData;
ICMP_HEADER* pIcmpHeader = (PICMP_HEADER)(pData + IP_HEADER_SIZE);
if (pIcmpHeader->icmp_type != ICMPV4_ECHO_REPLY_TYPE)
{
printf("icmp_type error! \n");
return false;
}
if (pIcmpHeader->icmp_code != ICMPV4_ECHO_REPLY_CODE)
{
printf("icmp_code error! \n");
return false;
}
if (pIcmpHeader->icmp_id != GetCurrentProcessId())
{
printf("icmp_id error! \n");
return false;
}
in_addr addr;
memcpy(&addr, &pIpHeader->ipSource, sizeof(addr));
DWORD dwTime = g_dwRecvTime - pIcmpHeader->icmp_timestamp;
if (dwTime <=0 )
{
printf("Reply from %s: bytes = %d, time < 1ms, TTL = %d \n", inet_ntoa(addr), nDataLen, pIpHeader->ipTTL);
}
else
{
printf("Reply from %s: bytes = %d, time = %dms, TTL = %d \n", inet_ntoa(addr), nDataLen, dwTime, pIpHeader->ipTTL);
}
return true;
}
/***********************************************************************************************
发送ICMP包
***********************************************************************************************/
inline int CPing::SendIcmpPacket(const addrinfo* pDestAddr)
{
assert(pDestAddr);
return sendto(m_socket, m_szIcmpPacket, ICMP_PACKET_SIZE, 0, pDestAddr->ai_addr, pDestAddr->ai_addrlen);
}
/***********************************************************************************************
接收ICMP包
***********************************************************************************************/
inline int CPing::RecvIcmpPacket(char* pBuf, int len, sockaddr_in* pSrcAddr)
{
assert(pBuf && pSrcAddr);
int addr_len = sizeof(*pSrcAddr);
return recvfrom(m_socket, pBuf, len, 0, (sockaddr*)pSrcAddr, &addr_len);
}
/**********************************************************************************************
测试网络是否连通
入口
strDest: 目的地址,域名或点分制IP
出口
若连通则返回true, 否则返回false
**********************************************************************************************/
bool CPing::Ping(const string &strDest)
{
InitIcmpPacket();
addrinfo* dest_addr = ResolveAddress(strDest);
if (NULL == dest_addr)
{
return false;
}
char szBuf[0xFFFF] = { 0 };
sockaddr_in src_addr;
int addr_len = sizeof(src_addr);
int nRecv = 0, nRet;
for (int n = 0; n < m_nSendCount; ++n)
{
SetIcmpPacket(n);
nRet = SendIcmpPacket(dest_addr);
if (SOCKET_ERROR == nRet)
{
printf("sendto failed with error = %d \n", WSAGetLastError());
return false;
}
g_dwSendTime = GetTickCount();
nRet = RecvIcmpPacket(szBuf, 64, &src_addr);
g_dwRecvTime = GetTickCount();
if (SOCKET_ERROR == nRet)
{
if(WSAGetLastError() == WSAETIMEDOUT)
{
printf("Timed out\n");
continue;
}
}
if (ParseIcmpPacket(szBuf, nRet))
{
++nRecv;
}
Sleep(1000);
}
printf("Send = %d, Receive = %d, Lost = %d \n", m_nSendCount, nRecv, m_nSendCount - nRecv);
freeaddrinfo(dest_addr);
//如果丢包,则网络有问题, 认为不通
if (m_nSendCount != nRecv)
{
return false;
}
return true;
}