如何ping网络C++编写

网络看到别人写的,然后学习了一下,可以用vs2015测试通过
在这里插入图片描述

(ping.h)
#pragma once

//在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h
//要么将#include <winsock2.h>放在#include<windows.h>前面或者直接去掉#include<windows.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 cppPing
{
public:
	cppPing();
	~cppPing();
	BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 1000);
	BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 1000);
	int domain2p(char* hostname, char*IP);
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;//初始化标志

	addrinfo hints, *res;
	in_addr addr;
	int err;
private:
	static USHORT s_usPacketSeq;
};


#include "stdafx.h"
#include "cppPing.h"
#include <ws2tcpip.h>
#include <iostream>

#pragma warning(disable:4996);
USHORT cppPing::s_usPacketSeq = 0;
//构造函数
cppPing::cppPing() :m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{
	WSADATA WSAData;
	int Ret = -1;
	//带有WSA前缀的都是Winsock2版本的API函数
	Ret=WSAStartup(MAKEWORD(2, 0), &WSAData);
	if(Ret!=0)
	{
		/*如果初始化不成功则报错,不能用GetLastError(),因为WinSocket没有加载*/
		printf("WSAStartup() failed with err: %d\n", Ret);
		return;
	}
	else
	{
		printf("WSAStartup() success!\n");
	}

	//创建一个初始状态为失信的匿名的需要手动重置的事件对象
	//如果函数成功,则返回值即是事件对象的句柄。
	//如果函数失败,返回WSA_INVALID_EVENT。应用程序可通过调用WSAGetLastError()函数获取进一步的错误信息。
	m_event = WSACreateEvent();
	//是获取当前进程一个唯一的标识符。
	m_usCurrentProcID = (USHORT)GetCurrentProcessId();
	//创建套接字
	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;//初始化成功
		//为发送ICMP报文分配空间
		m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));
		//判断
		if (m_szICMPData == NULL)
		{
			m_bIsInitSucc = FALSE;
		}
	}
}

cppPing::~cppPing()
{
	WSACloseEvent(m_event);
	closesocket(m_sockRaw);
	WSACleanup();

	if (NULL != m_szICMPData)
	{
		free(m_szICMPData);
		m_szICMPData = NULL;
	}
}

BOOL cppPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
	return PingCore(dwDestIP, pPingReply, dwTimeout);
}

BOOL cppPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
	if (NULL != szDestIP)
	{
		//inet_addr将点分十进制转换为网络端数据
		return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);
	}
	return FALSE;
}



int cppPing::domain2p(char* hostname, char*IP)
{
	memset(&hints, 0, sizeof(addrinfo));
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = AF_INET;

	if ((err = getaddrinfo(hostname, NULL, &hints, /*_out*/&res)) != 0)
	{
		printf("error %d : %s\n", err, gai_strerror(err));
		return 1;
	}
	addr.s_addr = ((sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;
	inet_ntop(AF_INET, (void *)&addr, IP, 16);
	printf("ip addresss: %s\n", IP);
	//free res
	freeaddrinfo(res);
	return 0;
}

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

	//配置SOCKET
	sockaddr_in sockaddrDest;
	//IPV4
	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();
	//ULONG ulSendTimestamp = GetTickCount();//测试
	/*std::cout << "first;  "
		<< ulSendTimestamp << std::endl;*/


	//序号
	USHORT usSeq = ++s_usPacketSeq;
	//清空结构体
	memset(m_szICMPData, 0, nICMPDataSize);
	//强制转换结构体为ICMP报头
	ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;
	//Type:类型,type=8表示响应请求报文,type=0表示响应应答报文。
	pICMPHeader->m_byType = ECHO_REQUEST;
	//Code:代码,与type组合,表示具体的信息
	pICMPHeader->m_byCode = 0;
	//Identifier:标识符,这个一般填入本进程的标识符
	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)
		DWORD Ret;
		Ret = WSAWaitForMultipleEvents(1, &m_event, FALSE, 1000, FALSE);
		if (Ret == WSA_WAIT_FAILED)
		{
			printf("WSAWaitForMultipleEvents failed\n");
			return FALSE;
		}
		else if (Ret == WSA_WAIT_TIMEOUT)
		{
			printf("WSAWaitForMultipleEvents timeout\n");
			return FALSE;
		}
		else
		{
			WSANETWORKEVENTS netEvent;
			WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);
			//判断事件
			if (netEvent.lNetworkEvents & FD_READ)
			{
				//获取时间
				//Sleep(1000);//测试
				ULONG nRecvTimestamp = GetTickCountCalibrate();
				
				int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);
				if (nPacketSize != SOCKET_ERROR)
				{
					//剥除IP报头
					IPHeader *pIPHeader = (IPHeader*)recvbuf;
					//头部长度IHL:首部长度。因为IP的头部不是定长的,所以需要这个信息进行IP包的解析,从而找到Data字段的起始点。
					//另外注意这个IHL是以4个字节为单位的,所以首部实际长度是IHL * 4字节。
					//取出第四位IHL,这是IP报头长度
					USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);
					//locate ICMP head location
					ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);
					//判断是否为当前进程发送的ICMP
					if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文
						&& pICMPHeader->m_byType == ECHO_REPLY //是ICMP响应报文
						&& pICMPHeader->m_usSeq == usSeq //是本次请求报文的响应报文
						)
					{
						//填充reply结构体,获取想要的信息
						//序号
						pPingReply->m_usSeq = usSeq;
						//
						pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;
					/*	std::cout << "second;  "
							<< pICMPHeader->m_ulTimeStamp << std::endl;*/
						pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);
						pPingReply->m_dwTTL = pIPHeader->m_byTTL;
						return TRUE;
					}
				}
			}
		}
		//超时
		if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout)
		{
			return FALSE;
		}
	}
}

USHORT cppPing::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 cppPing::GetTickCountCalibrate()
{
	//GetTickCount()这个函数精度非常差,误差在100ms左右
	//静态,值初始化一次
	static ULONG s_ulFirstCallTick = 0;
	static LONGLONG s_ullFirstCallTickMS = 0;
	//系统事件
	SYSTEMTIME systemtime;
	//文件时间
	FILETIME filetime;
	/*
	typedef struct _FILETIME {
	DWORD dwLowDateTime;
	DWORD dwHighDateTime;
	} FILETIME, *PFILETIME;
	typedef struct _SYSTEMTIME {
	WORD wYear;
	WORD wMonth;
	WORD wDayOfWeek;
	WORD wDay;
	WORD wHour;
	WORD wMinute;
	WORD wSecond;
	WORD wMilliseconds;
	} SYSTEMTIME, *PSYSTEMTIME;
	*/

	//获取本地时间,也就是系统设置的当前时间
	//GetSystemTime:所返回的是UTC.格林威治时间
	GetLocalTime(&systemtime);
	//A file time is a 64-bit value that represents the number of 100-nanosecond 
	//intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC)
	SystemTimeToFileTime(&systemtime, &filetime);
	//大结构体数据
	LARGE_INTEGER liCurrentTime;
	liCurrentTime.HighPart = filetime.dwHighDateTime;
	liCurrentTime.LowPart = filetime.dwLowDateTime;
	//合并大结构体数据,合并后为ms
	LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;

	if (s_ulFirstCallTick == 0)
	{
		//它返回从操作系统启动到当前所经过的毫秒数,这个是静态局部变量
		s_ulFirstCallTick = GetTickCount();
	}
	if (s_ullFirstCallTickMS == 0)
	{
		//静态局部变量,第一次将sendICMP报文时间赋值

		s_ullFirstCallTickMS = llCurrentTimeMS;
	}
	//第一次开机启动到第一次获取时间+(第n次-第1次间隔时间)
	return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);//ms
}

// myPing.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#include "cppPing.h"
#include <ws2tcpip.h>
#include <vector>
#include <iostream>
#include<algorithm>
#include <numeric>



int main(void)
{
	cppPing objPing;
	char IP[16];
	bool pingOK;
	int send_Cnt = 0;
	int failed_Cnt = 0;
	std::vector<int>  delayTm;
	int delayMaxTm = -1;
	int delayMinTm = -1;
	double delayAverTm = -1;

	//char *szDestIP = "192.168.16.8";
	char* hostname = "www.baidu.com";
	objPing.domain2p(hostname, IP);

	PingReply reply;

	//printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE);
	char sIP[17] = { 0 };
	memcpy(sIP, IP, 16);
	printf("Pinging %s with %d bytes of data:\n", sIP, DEF_PACKET_SIZE);
	do
	{
		pingOK = objPing.Ping(IP, &reply);
		if (pingOK)
		{
			printf("Reply from %s: bytes=%d time=%ldms\t TTL=%ld\n", sIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);
			delayTm.push_back((int)reply.m_dwRoundTripTime);

		}
		else
		{
			printf("ping timeout...\n");
			delayTm.push_back(0);
			++failed_Cnt;
		}
		++send_Cnt;
		Sleep(50);
	} while (send_Cnt < 5);

	delayMaxTm = *(max_element(delayTm.begin(), delayTm.end()));
	delayMinTm = *(min_element(delayTm.begin(), delayTm.end()));
	delayAverTm = accumulate(delayTm.begin(), delayTm.end(), 0.0) / delayTm.size();

	printf("%s统计信息:\n", sIP);
	printf("数据包  已发送:%d,  已接收: %d\n", send_Cnt, send_Cnt - failed_Cnt);
	printf("delayMaxTm =%d ms\n", delayMaxTm);
	printf("delayMinTm = %d ms\n", delayMinTm);
	printf("delayAverTm = %.2f ms\n", delayAverTm);

	if (failed_Cnt >= 0 && failed_Cnt < 5)
	{
		if (delayAverTm > 0 && delayAverTm < 20)//
		{
			printf("信号较强\n");
			system("pause");
			return 555;
		}
		else if (delayAverTm >= 20 && delayAverTm < 35)//
		{
			printf("信号强\n");
			system("pause");
			return 444;
		}
		else if (delayAverTm >= 35 && delayAverTm < 50)//
		{
			printf("信号一般\n");
			system("pause");
			return 333;
		}
		else if (delayAverTm >= 50 && delayAverTm < 75)//
		{
			printf("信号弱\n");
			system("pause");
			return 222;
		}
		else
		{
			printf("信号微弱\n");
			system("pause");
			return 111;
		}
	}
	else
	{
		printf("无信号\n");
		return 000;
	}

	
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值