c++builder ping_C++实现ping功能转
今天接到需求要实现ping的功能,然后网上查了一些资料,对网络编程的一些函数熟悉了一下,虽然还有一些细节不清楚,但是慢慢积累。
要实现这样的功能:
基础知识
ping的过程是向目的IP发送一个type=8的ICMP响应请求报文,目标主机收到这个报文之后,会向源IP(发送方,我)回复一个type=0的ICMP响应应答报文。
那上面的字节、往访时间、TTL之类的信息又是从哪来的呢?这取决于IP和ICMP的头部。
IP头部:
头部内容有点多,我们关心的只有以下几个:
IHL:首部长度。因为IP的头部不是定长的,所以需要这个信息进行IP包的解析,从而找到Data字段的起始点。
另外注意这个IHL是以4个字节为单位的,所以首部实际长度是IHL*4字节。
Time to Live:生存时间,这个就是TTL了。
Data:这部分是IP包的数据,也就是ICMP的报文内容。
ICMP响应请求/应答报文头部:
Type:类型,type=8表示响应请求报文,type=0表示响应应答报文。
Code:代码,与type组合,表示具体的信息,参考这里。
Checksum:检验和,这个是整个ICMP报文的检验和,包括Type、Code、...、Data。
Identifier:标识符,这个一般填入本进程的标识符。
Sequence Number:序号
Data:数据部分
上面是标准的ICMP报文,一般而言,统计ping的往返时间的做法是,在ICMP报文的Data区域写入4个字节的时间戳。
在收到应答报文时,取出这个时间戳与当前的时间对比即可。
Ping程序实现步骤
创建类型为SOCK_RAW的一个套接字,同时设定协议IPPROTO_ICMP。
创建并初始化ICMP头。
调用sendto或WSASendto,将ICMP请求发给远程主机。
调用recvfrom或WSARecvfrom,以接收任何ICMP响应。
ping.h
#pragma once
//在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h//要么将#include 放在#include前面或者直接去掉#include
#include
#pragma comment(lib, "WS2_32") //链接到WS2_32.lib
#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0
structIPHeader
{
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地址
};structICMPHeader
{
BYTE m_byType;//类型
BYTE m_byCode; //代码
USHORT m_usChecksum; //检验和
USHORT m_usID; //标识符
USHORT m_usSeq; //序号
ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)
};structPingReply
{
USHORT m_usSeq;
DWORD m_dwRoundTripTime;
DWORD m_dwBytes;
DWORD m_dwTTL;
};classCPing
{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, intnSize);
ULONG GetTickCountCalibrate();private:
SOCKET m_sockRaw;
WSAEVENT m_event;
USHORT m_usCurrentProcID;char *m_szICMPData;
BOOL m_bIsInitSucc;private:staticUSHORT s_usPacketSeq;
};
ping.cpp
#include "ping.h"#includeUSHORT 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)
{returnPingCore(dwDestIP, pPingReply, dwTimeout);
}
BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL !=szDestIP)
{returnPingCore(inet_addr(szDestIP), pPingReply, dwTimeout);
}returnFALSE;
}
BOOL CPing::PingCore(DWORD dwDestIP, PingReply*pPingReply, DWORD dwTimeout)
{//判断初始化是否成功
if (!m_bIsInitSucc)
{returnFALSE;
}//配置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)
{returnFALSE;
}//判断是否需要接收相应报文
if (pPingReply ==NULL)
{returnTRUE;
}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;returnTRUE;
}
}
}
}//超时
if (GetTickCountCalibrate() - ulSendTimestamp >=dwTimeout)
{returnFALSE;
}
}
}
USHORT CPing::CalCheckSum(USHORT*pBuffer, intnSize)
{
unsignedlong 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);
}
main.cpp
#include #include#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;
}
结果:
附录:如何计算检验和
ICMP中检验和的计算算法为:
1、将检验和字段置为0
2、把需校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和
3、把得到的结果存入检验和字段中
所谓二进制反码求和,就是:
1、将源数据转成反码
2、0+0=0 0+1=1 1+1=0进1
3、若最高位相加后产生进位,则最后得到的结果要加1
在实际实现的过程中,比较常见的代码写法是:
1、将检验和字段置为0
2、把需校验的数据看成以16位为单位的数字组成,依次进行求和,并存到32位的整型中
3、把求和结果中的高16位(进位)加到低16位上,如果还有进位,重复第3步[实际上,这一步最多会执行2次]
4、将这个32位的整型按位取反,并强制转换为16位整型(截断)后返回
其中也遇到了很多问题,头文件包含,WSAStartup函数初始化失败。。。
主要参考:
原贴地址:https://www.cnblogs.com/ranjiewen/p/5704627.html
c++builder ping_C++实现ping功能转相关推荐
- windows开启ping功能
方法/步骤 1.直接定位到控制面板中,选择[windows防火墙] 2.在windows防火墙页面左侧的列表中找到[高级设置]菜单 3.进入后在左侧找到"入站规则"点击后在中间部分 ...
- Linux系统实现ICMP ping功能,并计算时延
<Linux实现ICMP PING代码> <C语言实现ICMP协议,并进行PING测试> <本文源代码下载> 目录 源代码 icmpping.c icmpping. ...
- java ping 实现的_java实现ping功能
一.纯Java实现ICMP的ping命令 import java.io.*; import java.net.*; import java.nio.channels.*; import java.ut ...
- FPGA纯verilog实现UDP通信,三速网自协商仲裁,动态ARP和Ping功能,提供工程源码和技术支持
目录 1.前言 2.我这里已有的UDP方案 3.UDP详细设计方案 MAC层发送 MAC发送模式 ARP发送 IP层发送 IP发送模式 UDP发送 MAC层接收 ARP接收 IP层接收 UDP接收 S ...
- C++ 实现 ping 功能 域名(URL)解析实际 IP地址
1.简述 一般情况下,我们想知道在当前电脑设备环境下,某一个网址能不能访问,最简单的方法是win + R 键 ,输入cmd,召唤cmd命令行程序,然后直接用ping命令 + 网址 来看返回的结果,那么 ...
- Shell脚本实现 ping功能
Shell 脚本实现 ip ping 功能 具体代码: #!/bin/bash # ========================================================= ...
- 方法:如何解决用MFC实现的ping功能中把目标主机不可到达的当成ping通的问题...
http://www.cnblogs.com/xuesongshu/ 网上查到的资料能实现ping功能,但是都有一个问题,它只检测是否存在错误,而不检测ICMP数据包是哪个机器回复的,这样造成一种错误 ...
- 方法:如何解决用MFC实现的ping功能中把目标主机不可到达的当成ping通的问题
转载请注明来源: http://www.cnblogs.com/xuesongshu/ 网上查到的资料能实现ping功能,但是都有一个问题,它只检测是否存在错误,而不检测ICMP数据包是哪个机器回复的 ...
- C语言实现ping功能(查看设备联网状态)
本文ping功能是利用gethostbyname()函数来实现的. gethostbyname()函数说明--用域名或主机名获取IP地址 包含头文件 #include <netdb.h> ...
最新文章
- 史上自定义 JavaScript 函数Top 10
- 盘点几个值得你借鉴的Java学习方法
- jQuery的Accordion插件
- MATLAB无穷大上的反常积分
- PyQt5 笔记4 -- 多线程
- gtw-050090|执行拦截器时发生异常_执行流程 | 你真的了解Spring AOP的执行顺序吗?...
- poj3713 Transferring Sylla 枚举+tarjan判割点
- MySQL保存或更新 saveOrUpdate
- Golang——Tag、自定义类型、未命名类型
- 货币系统(信息学奥数一本通-T12973)
- 例子---PHP与Form表单前导篇
- Maven3路程(六)用Maven创建Spring3 MVC项目
- BayaiM__MySQL错误对照表
- php 修改html编码,PHP将HTML Charset编码弄乱了
- jQuery load和unload函数使用
- Mongodb数据库安装
- 2019液晶电视机质量排名前十名
- 2019中兴笔试选择题
- 守望先锋外挂(OWG)透视原理分析
- Jsckson 实现 java 对象与 JSONObject 和 JSONArray 互转
热门文章
- 蓝桥2017真题剪邮票
- python zip dict_关于python:zip(list)和zip(* list)之间的区别
- 的ppt_PPT丨清新淡雅年终总结PPT模板
- 档案盒正面标签制作_如何制作差异化的短视频内容?
- json取数据怎么取_灯罩怎么取下来 各种吸顶灯的灯罩拆卸方法
- 【编辑器】VSCode+TEXLIVE环境配置,以SJTUthesis为测试用例
- 【Python3】【爬虫】meizituSpider
- NYOJ266 - 字符串逆序输出
- xhtml标签和html标签,XHTML常用标签
- 第六章节 三层架构(一. 三层架构的概述)