C++实现ping功能

基础知识

ping的过程是向目的IP发送一个type=8的ICMP响应请求报文,目标主机收到这个报文之后,会向源IP(发送方,我)回复一个type=0的ICMP响应应答报文。

那上面的字节、往访时间、TTL之类的信息又是从哪来的呢?这取决于IP和ICMP的头部。


#pragma once

//在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h
//要么将#include winsock2.h放在#includewindows.h前面或者直接去掉#includewindows.h

#include winsock2.h
#pragma comment(lib, WS2_32) 链接到WS2_32.lib

#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0

struct IPHeader
{
BYTE m_byVerHLen; 4位版本+4位首部长度
BYTE m_byTOS; 服务类型
USHORT m_usTotalLen; 总长度
USHORT m_usID; 标识
USHORT m_usFlagFragOffset; 3位标志+13位片偏移
BYTE m_byTTL; TTL
BYTE 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;
DWORD m_dwRoundTripTime;
DWORD m_dwBytes;
DWORD m_dwTTL;
};

class CPing
{
public
CPing();
~CPing();
BOOL Ping(DWORD dwDestIP, PingReply pPingReply = NULL, DWORD dwTimeout = 2000);
BOOL Ping(char szDestIP, PingReply pPingReply = NULL, DWORD dwTimeout = 2000);
private
BOOL PingCore(DWORD dwDestIP, PingReply pPingReply, DWORD dwTimeout);
USHORT CalCheckSum(USHORT pBuffer, int nSize);
ULONG GetTickCountCalibrate();
private
SOCKET m_sockRaw;
WSAEVENT m_event;
USHORT m_usCurrentProcID;
char m_szICMPData;
BOOL m_bIsInitSucc;
private
static USHORT s_usPacketSeq;
};



#include "ping.h"
#include <iostream>
USHORT CPing::s_usPacketSeq = 0;

CPing::CPing() :m_szICMPData(NULL),m_bIsInitSucc(FALSE)
{
WSADATA WSAData;
//WSAStartup(MAKEWORD(2, 2), &WSAData);
if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
{
/*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/
printf("WSAStartup() failed: %d\n", GetLastError());
return;
}
m_event = WSACreateEvent();
m_usCurrentProcID = (USHORT)GetCurrentProcessId();
//setsockopt(m_sockRaw);
/*if ((m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0)) != SOCKET_ERROR)
{
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;
}
}*/
m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
if (m_sockRaw == INVALID_SOCKET)
{
std::cerr << "WSASocket() failed:" << WSAGetLastError ()<< std::endl; //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。
}
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;
}
}

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;
}

BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
//判断初始化是否成功
if (!m_bIsInitSucc)
{
return FALSE;
}

//配置SOCKET
sockaddr_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);
}



#include <winsock2.h>
#include <stdio.h>
#include "ping.h"

int main(void)
{
CPing objPing;

char *szDestIP = "127.0.0.1";
PingReply reply;

printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE);
while (TRUE)
{
objPing.Ping(szDestIP, &reply);
printf("Reply from %s: bytes=%d time=%ldms TTL=%ld\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);
Sleep(500);
}

return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值