实现Ping

代码如下:
1.main函数

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

int main(void)
{
	CPing objPing;
	char DestIP[] = "172.22.56.231";
	PingReply reply;

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

	return 0;
}

2.ping.h头文件

#pragma once
#include<WinSock2.h>
#pragma comment(lib,"WS2_32")

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

struct IPHeader
{
	BYTE ip_VerAndLen; //版本长度和首部长度
	BYTE ip_ServiceType;   //服务类型
	USHORT ip_TotalLen; //总长度
	USHORT ip_ID;//标识
	USHORT ip_MarkAndSliceOffset;//标志和片偏移
	BYTE ip_TTL; //生存时间
	BYTE ip_Protocol;//协议
	USHORT ip_HeaderCheckSum;//首部检验和
	ULONG ip_SrcIP; //源IP
	LONG ip_DestIP;  //目的IP
};

struct ICMPHeader
{
	BYTE icmp_Type;  //类型(取0或者8)
	BYTE icmp_Code;  //代码
	USHORT icmp_CheckSum;//校验和
	USHORT icmp_ID; //标识符
	USHORT icmp_Seq; //序号
	ULONG icmp_TimeStamp;  //时间戳
};
//回应结构体
struct PingReply
{
	USHORT re_Seq;
	DWORD re_RoundTripTime;
	DWORD re_Bytes;
	DWORD re_TTL;
};

class CPing
{
public :
	CPing();
	~CPing();
	BOOL Ping(DWORD DestIP, PingReply* pPingReply = NULL, DWORD timeOut = 2000);
	BOOL Ping(char* DestIP, PingReply* pPingReply = NULL, DWORD timeOut = 2000);

private :
	BOOL PingCore(DWORD DestIP, PingReply* pPingReply, DWORD timeOut);
	USHORT CalCheckSum(USHORT* pBuffer, int nSize);
	ULONG GetTickCountCalibrate();
private :
	SOCKET my_socketRaw;
	WSAEVENT my_event;
	USHORT my_CurrentProcID;
	char* my_ICMPData;
	BOOL my_InitSucc;
private :
	static USHORT my_PacketSeq;
};

3.ping.cpp文件

#pragma warning (disable:4996)
#include"ping.h"
#include<iostream>

USHORT CPing::my_PacketSeq = 0;

CPing::CPing() :my_ICMPData(NULL), my_InitSucc(FALSE)
{
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
	{
		//初始化不成功就会报错
		printf("WSAStartup() failed: %d\n", GetLastError());
		return;
	}
	my_event = WSACreateEvent();
	my_CurrentProcID = (USHORT)GetCurrentProcessId();

	my_socketRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
	if (my_socketRaw == INVALID_SOCKET)
	{
		std::cerr << " WSASOcket() failed " << WSAGetLastError() << std::endl;
	}
	else
	{
		WSAEventSelect(my_socketRaw, my_event, FD_READ);
		my_InitSucc = TRUE;
		my_ICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));

		if (my_ICMPData == NULL)
		{
			my_InitSucc = FALSE;
		}

	}
}

CPing::~CPing()
{
	WSACleanup();
	if (NULL != my_ICMPData)
	{
		free(my_ICMPData);
		my_ICMPData = NULL;
	}
}

BOOL CPing::Ping(DWORD DestIP, PingReply* pPingReply, DWORD timeOut)
{
	return PingCore(DestIP, pPingReply, timeOut);
}


BOOL CPing::Ping(char* DestIP, PingReply* pPingReply, DWORD timeOut)
{
	if (NULL != DestIP)
	{
		return PingCore(inet_addr(DestIP), pPingReply, timeOut);
	}
	return FALSE;
}

BOOL CPing::PingCore(DWORD DestIP, PingReply* pPingReply, DWORD timeOut)
{
	if (!my_InitSucc)
	{
		return FALSE;
	}
	//配置SOCKET
	sockaddr_in sockaddrDest;
	sockaddrDest.sin_family = AF_INET;
	sockaddrDest.sin_addr.s_addr = DestIP;
	int nSockaddrDestSize = sizeof(sockaddrDest);

	//构建ICMP包
	int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);
	ULONG usSendTimestamp = GetTickCountCalibrate();
	USHORT usSeq = ++my_PacketSeq;
	memset(my_ICMPData, 0, nICMPDataSize);
	ICMPHeader* pICMPHeader = (ICMPHeader*)my_ICMPData;
	pICMPHeader->icmp_Type = ECHO_REQUEST;
	pICMPHeader->icmp_Code = 0;
	pICMPHeader->icmp_ID = my_CurrentProcID;
	pICMPHeader->icmp_Seq = usSeq;
	pICMPHeader->icmp_TimeStamp = usSendTimestamp;
	pICMPHeader->icmp_CheckSum = CalCheckSum((USHORT*)my_ICMPData, nICMPDataSize);


	//发送ICMP报文
	if (sendto(my_socketRaw, my_ICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR)
	{
		return FALSE;
	}

	//判断是否需要接收相应报文
	if (pPingReply == NULL)
	{
		return TRUE;
	}

	char recvbuf[256] = { "\0" };
	while (TRUE)
	{
		//接收响应报文
		if (WSAWaitForMultipleEvents(1, &my_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT)
		{
			WSANETWORKEVENTS netEvent;
			WSAEnumNetworkEvents(my_socketRaw, my_event, &netEvent);

			if (netEvent.lNetworkEvents & FD_READ)
			{
				ULONG nRecvTimestamp = GetTickCountCalibrate();
				int nPacketSize = recvfrom(my_socketRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);
			
				if (nPacketSize != SOCKET_ERROR)
				{
					IPHeader* pIPHeader = (IPHeader*)recvbuf;
					USHORT usIPHeaderLen = (USHORT)((pIPHeader->ip_VerAndLen & 0x0f) * 4);
					ICMPHeader* pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);
				
					if (pICMPHeader->icmp_ID == my_CurrentProcID
						&& pICMPHeader->icmp_Type == ECHO_REPLY
						&& pICMPHeader->icmp_Seq == usSeq
						)
					{
						pPingReply->re_Seq = usSeq;
						pPingReply->re_RoundTripTime = nRecvTimestamp - pICMPHeader->icmp_TimeStamp;
						pPingReply->re_Bytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);
						pPingReply->re_TTL = pIPHeader->ip_TTL;
						return TRUE;
					}
				}
			}
		}
		if (GetTickCountCalibrate() - usSendTimestamp >= timeOut)
		{
			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 ULONG 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);
}

参考博客:https://www.cnblogs.com/ranjiewen/p/5704627.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值