转载自b站up主 某科学的_半个人(已经过同意)
源文件
#include "he.h"
#include<iostream>
#include<windows.h>
using namespace std;
char IP[30];
const char* BToC(bool b)
{
if (b)
{
return "true ";
}
else
{
return "false ";
}
}
int main()
{
//输入
cout << "IP:";
cin >> IP;
int counts = 5, i = 0; // 发送次数
while (i < counts)
{
PingAPI PA;
PingReply* RP = new PingReply();
cout << BToC(PA.Ping(IP, RP));
cout << "Reply from " << IP << ": bytes=" << RP->m_dwBytes << " time=" << RP->m_dwRoundTripTime << "ms TTL=" << RP->m_dwTTL << "\n";
Sleep(1000);
i++;
}
}
头文件
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<WinSock2.h>
#pragma comment(lib, "WS2_32") //连接到WS2_32.lib
#include<iostream>
#define DEF_PACKET_SIZE 32 //定义了 ICMP 数据包的大小
#define ECHO_REQUEST 8 //echo 请求的类型代码
#define ECHO_REPLY 0 //echo 响应的类型代码
struct IPHeader
{
BYTE m_byVerHLen; //4位版本+4位首部长度
BYTE m_byTOS; //服务类型
USHORT m_usTotalLen;//总长度
USHORT m_usID; //标识
USHORT m_usFlagFragOffset;//3位标志+3位片位移
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; //序号 主要用于区分连续 ping 的时候发出的多个数据包
ULONG m_ulTimeStamp; //时间戳
};
struct PingReply
{
USHORT m_usSeq;
DWORD m_dwRoundTripTime = 0;
DWORD m_dwBytes = 0;
DWORD m_dwTTL = 0;
};
class PingAPI
{
public:
PingAPI()
{
WSADATA WSAData; //用于存储 Winsock 库的信息
if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0) //调用初始化 Winsock 库
{
printf("WSAStartup() failed: %d\n", GetLastError());
return;
}
m_event = WSACreateEvent(); // 创建一个事件对象,用于后续的事件驱动网络操作
m_usCurrentProcID = (USHORT)GetCurrentProcessId(); //获取当前进程的 ID
m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
/*创建一个原始套接字,用于发送和接收 ICMP 数据包。AF_INET 指定了地址族(IPv4),
SOCK_RAW 指定了套接字类型(原始套接字),IPPROTO_ICMP 指定了协议类型(ICMP)*/
if (m_sockRaw == INVALID_SOCKET)
{
std::cerr << "Socket creation failed: " << GetLastError() << std::endl;
}
else
{
WSAEventSelect(m_sockRaw, m_event, FD_READ);
m_bIsInitSucc = TRUE;
m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));
//分配内存用于构建 ICMP 数据包。DEF_PACKET_SIZE 是数据部分的大小,sizeof(ICMPHeader) 是 ICMP 头部的大小。
if (m_szICMPData == NULL)
{
m_bIsInitSucc = FALSE;
}
}
}
~PingAPI()
{
WSACleanup();
if (NULL != m_szICMPData)
{
free(m_szICMPData);
m_szICMPData = NULL;
}
}
BOOL Ping(DWORD dwDestIP, PingReply* pPingReply = NULL, DWORD dwTimeout = 2000)
{
return PingCore(dwDestIP, pPingReply, dwTimeout);
}
BOOL Ping(const char* szDestIP, PingReply* pPingReply = NULL, DWORD dwTimeout = 2000)
{
if (NULL != szDestIP)
{
return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);
}
return FALSE;
}
private:
BOOL PingCore(DWORD dwDestIP, PingReply* pPingReply, DWORD dwTimeout)
{ //判断初始化是否成功
if (!m_bIsInitSucc)
{
return FALSE;
}
//配置SOCKET
sockaddr_in sockaddrDest; //sockaddr_in 结构体变量,用于存储目标地址信息
sockaddrDest.sin_family = AF_INET; //设置地址族为 IPv4
sockaddrDest.sin_addr.s_addr = dwDestIP; //设置目标 IP 地址
int nSockaddrDestSize = sizeof(sockaddrDest); //获取目标地址结构体的大小,以便在 sendto 函数中使用
//构建ICMP包
int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader); //ICMP 数据包的总大小
ULONG ulSendTimestamp = GetTickCountCalibrate(); //获取当前时间戳,用于测量往返时间
USHORT usSeq = ++s_usPacketSeq; //递增序列号,用于标识此次 ping 请求
memset(m_szICMPData, 0, nICMPDataSize); //将分配的 ICMP 数据包缓冲区清零
ICMPHeader* pICMPHeader = (ICMPHeader*)m_szICMPData; //将缓冲区的开始处视为 ICMP 头部结构体的指针
pICMPHeader->m_byType = ECHO_REQUEST; //设置 ICMP 类型为 echo 请求
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)
{
/*m_sockRaw:这是一个原始套接字(raw socket),用于发送 ICMP 请求。
m_szICMPData:这是一个缓冲区,包含了要发送的 ICMP 数据包。
nICMPDataSize:这是 ICMP 数据包的大小。
0:这是发送操作的标志,通常用于指定发送操作的特定行为。在这里,0 表示没有特定的标志。
(struct sockaddr*)&sockaddrDest:这是指向目的地地址的指针,它是一个 sockaddr_in 结构体,包含了目标 IP 地址和端口号(在这个例子中,端口号通常设置为 0,因为 ICMP 不使用端口号)。
nSockaddrDestSize:这是目的地地址结构体的大小。*/
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);
//从套接字接收数据到 recvbuf 缓冲区。256 是缓冲区大小,这里设置为 256 字节
if (nPacketSize != SOCKET_ERROR)
{
IPHeader* pIPHeader = (IPHeader*)recvbuf;
USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4); //计算 IP 头部的长度。
ICMPHeader* pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen); //定位 ICMP 头部在数据包中的位置。
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);//数据长度=总长度-IP头部长度-ICMP头部长度
pPingReply->m_dwTTL = pIPHeader->m_byTTL;
return TRUE;
}
}
}
}
if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout) //超时
{
return FALSE;
}
}
}
ULONG 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);
}
USHORT 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);
}
private:
SOCKET m_sockRaw;
WSAEVENT m_event;
USHORT m_usCurrentProcID;
char* m_szICMPData;
BOOL m_bIsInitSucc;
private:
USHORT s_usPacketSeq = 0;
};
运行结果