- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章C++代码实现网络Ping功能由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
ping 127.0.0.1: 这个Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机.
localhost是个操作系统的网络保留名,是127.0.0.1的别名.
ping www.baidu.com――对这个域名执行Ping命令,你的计算机必须先将域名转换成IP地址,通常是通过DNS服务器.
。
// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。////程序应用: ping命令是向目的主机发送ICMP报文,检验本地主机和远程的目的主机是否连接#include <winsock2.h>#include <stdio.h>#include "ping.h"int main(void){ CPing objPing; //CPing类与对象 char *szDestIP = "127.0.0.1"; //字符IP地址 //127.0.0.1 这个Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机。localhost是个操作系统的网络保留名,是127.0.0.1的别名。//ping www.baidu.com――对这个域名执行Ping命令,你的计算机必须先将域名转换成IP地址,通常是通过DNS服务器。 PingReply reply; //PingReply类与对象 printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE); //ping 远端IP地址,32字节的数据 while (TRUE) { objPing.Ping(szDestIP, &reply);//远端IP地址不为空(NULL),就返回true表示需要响应报文。远端IP空时不需要响应报文false。 printf("Reply from %s: bytes=%d time=%ldms TTL=%ld\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL); //字节数,时间,TTL生存时间 Sleep(500); } return 0;}
。
IHL:首部长度。因为IP的头部不是定长的,所以需要这个信息进行IP包的解析,从而找到Data字段的起始点。 。
另外注意这个IHL是以4个字节为单位的,所以首部实际长度是IHL*4字节.
Time to Live:生存时间,这个就是TTL了.
Data:这部分是IP包的数据,也就是ICMP的报文内容.
//1.IP头结构体:20字节struct IPHeader{ BYTE m_byVerHLen; //4位版本Version+4位首部长度IHL 1B BYTE m_byTOS; //服务类型 1B=16b USHORT m_usTotalLen; //总长度 2B=16b USHORT m_usID; //标识 2B=16b USHORT m_usFlagFragOffset; //3位标志+13位片偏移=16位 2B=16b BYTE m_byTTL; //TTL 生存时间 1B=8b BYTE m_byProtocol; //协议 1B=8b 为1时表示是ICMP报文 USHORT m_usHChecksum; //首部检验和 2B=16b ULONG m_ulSrcIP; //源IP地址 4B=32b ULONG m_ulDestIP; //目的IP地址 4B=32b};
类型Type、代码Code、校验和、标识符、序列号、ICMP数据 。
//ICMP报文由首部8B和数据段组成。//首部为定长的8个字节,前4个字节是通用部分(类型1B/代码1B/校验和2B),后4个字节随报文类型的不同有所差异。//2.ICMP头结构体 (标准ICMP头为8字节)struct ICMPHeader{ BYTE m_byType; //类型 1B type=8表示响应请求报文,type=0表示响应应答报文。 BYTE m_byCode; //代码 1B 与type组合,表示具体的信息 USHORT m_usChecksum; //检验和 2B 整个ICMP报文的检验和,包括Type、Code、...、Data。 USHORT m_usID; //标识符 2B=16bits 用于标识本进程 USHORT m_usSeq; //序列号 2B=16bits 用于判断回显应答数据报。 ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)4B //统计ping的往返时间的做法是,在ICMP报文的Data区域写入4个字节的时间戳。在收到应答报文时,取出这个时间戳与当前的时间对比即可。};
//3.ICMP回答报文结构体struct PingReply{ USHORT m_usSeq; //ICMP包的序列号 2B DWORD m_dwRoundTripTime;//时间差 4B (word是2字节) DWORD m_dwBytes; //数据所占字节数 4B DWORD m_dwTTL; //TTL生存时间 4B};
//类class CPing{//公共变量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, int nSize);//计算检验和 ULONG GetTickCountCalibrate(); //计算毫秒级别的时间差private: SOCKET m_sockRaw; //需要监听的socket WSAEVENT m_event; //网络事件对象 USHORT m_usCurrentProcID; //当前进程发出的报文 char *m_szICMPData; //ICMP(Internet Control Message Protocol,网际控制报文协议) BOOL m_bIsInitSucc; //初始化成功private: static USHORT s_usPacketSeq; //序列号++(16位=2字节)};
。
#include "ping.h"#include <iostream>USHORT 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) { printf("WSAStartup() failed: %d\n", GetLastError()); /*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/ return; } m_event = WSACreateEvent(); //创建一个网络事件对象(HANDLE m_event)。 //返回一个手工重置的事件对象句柄 (HANDLE hEventObject) m_usCurrentProcID = (USHORT)GetCurrentProcessId(); //当前进程ID /*ICMP必须使用原始套接字进行设计,要手动设置IP的头部和ICMP的头部并行校验*/ m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0); //创建一个监听的socket (SOCKET m_sockRaw) //当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。 if (m_sockRaw == INVALID_SOCKET) //无效套接字 { std::cerr << "WSASocket() failed:" << WSAGetLastError() << std::endl; //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。 } else //是ICMP报文,令初始化成功,为ICMP数据分配内存 { WSAEventSelect(m_sockRaw, m_event, FD_READ); //调用WSAEventSelect将监听的socket(m_sockRaw)与该事件(m_event)进行关联。WSAEventSelect(套接字,网络事件对象,需要关注的事件) m_bIsInitSucc = TRUE; m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader)); //为ICMPData分配内存 //ping命令的工作原理是:向网络上的另一个主机系统发送ICMP报文,如果指定系统得到了报文,它将把报文一模一样地传回给发送者 if (m_szICMPData == NULL) { m_bIsInitSucc = FALSE; } }}CPing::~CPing(){ WSACleanup(); if (NULL != m_szICMPData) { free(m_szICMPData); m_szICMPData = NULL; }}
BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout){ if (NULL != szDestIP) //远端IP非空 { return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout); //项目 -> 属性 -> C/C++ > SDL检查:否。――修改VS配置,告诉它我就要用旧函数。inet_pton() or InetPton() } return FALSE; //远端IP为空,false}
1.//判断初始化是否成功 。
2.//配置套接字SOCKET 。
3.//构建ICMP包 。
4.//填补ICMP首部 。
5.//发送ICMP请求报文(ping请求) 。
6.//判断是否需要接收响应报文 。
7.//等待网络事件接收响应报文 。
BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout){ //判断初始化是否成功 if (!m_bIsInitSucc) { return FALSE; //初始化没成功 } //配置套接字SOCKET sockaddr_in sockaddrDest; //sockaddr_in是internet环境下的套接字地址。定义在ws2def.h中的结构体 sockaddrDest.sin_family = AF_INET; //地址族(Address Family):网络类型 sockaddrDest.sin_addr.s_addr = dwDestIP; //32位IP地址(4字节) int nSockaddrDestSize = sizeof(sockaddrDest);//大小 //构建ICMP包 int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader); //ICMP包长度:32+ICMP头 ULONG ulSendTimestamp = GetTickCountCalibrate(); //发送时间戳(毫秒级) USHORT usSeq = ++s_usPacketSeq; //2字节,++0 memset(m_szICMPData, 0, nICMPDataSize); //数据,长度。memset()初始化内存: //memset(* Dst, int Val, size_t Size):将指针变量Dst所指向的前Size字节的内存单元用一个“整数”Val替换。 //填补ICMP首部 ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData; pICMPHeader->m_byType = ECHO_REQUEST; //类型。Type:8,Code:0:表示回显请求报文(ping请求)。 Type:0,Code:0:表示回显回答报文(ping应答) pICMPHeader->m_byCode = 0; //代码 pICMPHeader->m_usID = m_usCurrentProcID;//标识符 pICMPHeader->m_usSeq = usSeq; //本报的序列号 pICMPHeader->m_ulTimeStamp = ulSendTimestamp;//发送时间戳(非标准ICMP头部) pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);//计算检验和 //TCP/IP协议栈使用的校验算法:对16位的数据进行累加计算,并返回计算结果 //―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― //发送ICMP请求报文(ping请求) 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) //等待事件(m_event)。 //WSAWaitForMultipleEvents(事件对象数组里边的个数为1,事件对象数组,等待类型为FALSE表示事件数组里至少有一个信号就返回,等待的超时时间为100,当系统的执行队列有I/O例程要执行时不返回) { WSANETWORKEVENTS netEvent; WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent); //m_event = WSACreateEvent(); //事件发生时,调用WSAEnumNetworkEvents,检测指定的socket上的网络事件,并将关联信息保存在netEvent中。 //WSAEnumNetworkEvents(SOCKET s,WSAEVENT hEventObject,LPWSANETWORKEVENTS lpNetworkEvents) //当调用WSAEnumNetworkEvents函数成功后,它会将我们指定的socket和事件对象所关联的网络事件的信息保存到LPWSANETWORKEVENTS这个结构体里去,根据这个结构体我们就可以判断是否是我们所关注的网络事件已经发生了。 //如果是FD_READ,读的网络事件发生了,那就调用recv函数进行操作。 //若是FD_CLOSE,关闭的网络事件发生了,就调用closesocket将socket关掉,在数组里将其置零等操作。 if (netEvent.lNetworkEvents & FD_READ) //有网络事件,且可读,那就recv收数啊 { ULONG nRecvTimestamp = GetTickCountCalibrate(); //计算开始发送的时间 int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize); //从远端IP处收数。 if (nPacketSize != SOCKET_ERROR) { //ICMP协议是IP层的一个协议,但是由于差错报告在发送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以ICMP报文需通过IP协议来发送。 //ICMP数据报的数据发送前需要两级封装:首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报。 //拆解封装:IP数据报去掉IP报头,剩下ICMP报文,再去掉ICMP报头,就是ICMP数据报了。 IPHeader *pIPHeader = (IPHeader*)recvbuf; USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4); //(4位版本+4位首部长度)&0xf,然后乘4 //IP头部20字节 ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen); if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文 && pICMPHeader->m_byType == ECHO_REPLY //是响应报文类型(Type=0) && pICMPHeader->m_usSeq == usSeq //是本次请求报文的响应报文 ) { pPingReply->m_usSeq = usSeq; //ICMP包的序列号 pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp; //当应答返回时,用当前时间减去存放在ICMP报文中的时间值,即是往返时间。 pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);//数据大小:整包数据-IP头-ICMP头 pPingReply->m_dwTTL = pIPHeader->m_byTTL; //TTL生存时间 return TRUE; } } } } //超时 if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout)//时间戳(非标准ICMP头部) { return FALSE; } }}
WSA连网事件流程图 。
//计算检验和 // TCP/IP协议栈使用的校验算法是比较经典的,对16位的数据进行累加计算,并返回计算结果USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize) //CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);{ unsigned long ulCheckSum = 0; //4B=32b //(1)将检验和字段置为0 while (nSize > 1) { ulCheckSum += *pBuffer++; //数值相加 //(2)把需校验的数据看成以16位为单位的数字组成,依次进行求和,并存到32位的整型中 nSize -= sizeof(USHORT); //长度相减 } if (nSize) { ulCheckSum += *(UCHAR*)pBuffer; } ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff); //高位相加 //(3)把求和结果中的高16位(进位)加到低16位上,如果还有进位,重复 ulCheckSum += (ulCheckSum >> 16); //将溢出位加入 return (USHORT)(~ulCheckSum); //返回值:取反 //(4)将这个32位的整型按位取反,并强制转换为16位整型(截断)后返回}/*附录:如何计算检验和ICMP中检验和的计算算法为:1、将检验和字段置为02、把需校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和3、把得到的结果存入检验和字段中所谓二进制反码求和,就是:1、将源数据转成反码2、0 + 0 = 0 0 + 1 = 1 1 + 1 = 0进13、若最高位相加后产生进位,则最后得到的结果要加1在实际实现的过程中,比较常见的代码写法是:1、将检验和字段置为02、把需校验的数据看成以16位为单位的数字组成,依次进行求和,并存到32位的整型中3、把求和结果中的高16位(进位)加到低16位上,如果还有进位,重复第3步[实际上,这一步最多会执行2次]4、将这个32位的整型按位取反,并强制转换为16位整型(截断)后返回*/
//计算毫秒级别的时间差。返回值是unsigned long级别的ULONG CPing::GetTickCountCalibrate(){ static ULONG s_ulFirstCallTick = 0; static LONGLONG s_ullFirstCallTickMS = 0; SYSTEMTIME systemtime; //系统时间SYSTEMTIME与tm类似,不过多了一项wMilliseconds。 FILETIME filetime; //文件时间FILETIME与time_t类似,是64位整型,不过FILETIME是以100纳秒(ns)为单位。 GetLocalTime(&systemtime); //GetLocalTime获得当前的本地时间,GetSystemTime函数获得当前的UTC时间,两个时间存在着时差。 SystemTimeToFileTime(&systemtime, &filetime);//UTC的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(); //返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数。用GetTickCount()计算毫秒级的时间差是不靠谱的! } if (s_ullFirstCallTickMS == 0) { s_ullFirstCallTickMS = llCurrentTimeMS; } return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);// 当前时间ms - 第一次回应时间ms}
到此这篇关于C++代码实现网络Ping功能的文章就介绍到这了,更多相关C++网络Ping内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.csdn.net/luyibing2017/article/details/119597300 。
最后此篇关于C++代码实现网络Ping功能的文章就讲到这里了,如果你想了解更多关于C++代码实现网络Ping功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我尝试理解[c代码 -> 汇编]代码 void node::Check( data & _data1, vector& _data2) { -> push ebp -> mov ebp,esp ->
我需要在当前表单(代码)的上下文中运行文本文件中的代码。其中一项要求是让代码创建新控件并将其添加到当前窗体。 例如,在Form1.cs中: using System.Windows.Forms; ..
我有此 C++ 代码并将其转换为 C# (.net Framework 4) 代码。有没有人给我一些关于 malloc、free 和 sprintf 方法的提示? int monate = ee; d
我的网络服务器代码有问题 #include #include #include #include #include #include #include int
给定以下 html 代码,将列表中的第三个元素(即“美丽”一词)以斜体显示的 CSS 代码是什么?当然,我可以给这个元素一个 id 或一个 class,但 html 代码必须保持不变。谢谢
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我试图制作一个宏来避免重复代码和注释。 我试过这个: #define GrowOnPage(any Page, any Component) Component.Width := Page.Surfa
我正在尝试将我的旧 C++ 代码“翻译”成头条新闻所暗示的 C# 代码。问题是我是 C# 中的新手,并不是所有的东西都像 C++ 中那样。在 C++ 中这些解决方案运行良好,但在 C# 中只是不能。我
在 Windows 10 上工作,R 语言的格式化程序似乎没有在 Visual Studio Code 中完成它的工作。我试过R support for Visual Studio Code和 R-T
我正在处理一些报告(计数),我必须获取不同参数的计数。非常简单但乏味。 一个参数的示例查询: qCountsEmployee = ( "select count(*) from %s wher
最近几天我尝试从 d00m 调试网络错误。我开始用尽想法/线索,我希望其他 SO 用户拥有可能有用的宝贵经验。我希望能够提供所有相关信息,但我个人无法控制服务器环境。 整个事情始于用户注意到我们应用程
我有一个 app.js 文件,其中包含如下 dojo amd 模式代码: require(["dojo/dom", ..], function(dom){ dom.byId('someId').i
我对“-gencode”语句中的“code=sm_X”选项有点困惑。 一个例子:NVCC 编译器选项有什么作用 -gencode arch=compute_13,code=sm_13 嵌入库中? 只有
我为我的表格使用 X-editable 框架。 但是我有一些问题。 $(document).ready(function() { $('.access').editable({
我一直在通过本教程学习 flask/python http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-wo
我想将 Vim 和 EMACS 用于 CNC、G 代码和 M 代码。 Vim 或 EMACS 是否有任何语法或模式来处理这种类型的代码? 最佳答案 一些快速搜索使我找到了 this vim 和 thi
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve this
这个问题在这里已经有了答案: Enabling markdown highlighting in Vim (5 个回答) 6年前关闭。 当我在 Vim 中编辑包含 Markdown 代码的 READM
我正在 Swift3 iOS 中开发视频应用程序。基本上我必须将视频 Assets 和音频与淡入淡出效果合并为一个并将其保存到 iPhone 画廊。为此,我使用以下方法: private func d
pipeline { agent any stages { stage('Build') { steps { e
我是一名优秀的程序员,十分优秀!