网络看到别人写的,然后学习了一下,可以用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;
}
}