- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在构建一个 RCP 应用程序,其中每个季度都会更新功能/插件。因此,如果用户选择自动更新功能/插件,则会下载更新插件的新 jar,但旧插件仍在使用我不再使用的磁盘空间。 我厌倦了删除包含旧 jar
我如何从外部 Controller 功能中调用 Controller 内部的功能,例如电话间隙回调功能 这是 Controller 外部定义的功能 function onDeviceReady()
如果某个功能(例如 MediaSource)可用,我如何使用 Google Dart 检查。 new MediaSource() 抛出一个错误。如何以编程方式检查此类或功能是否存在?有任何想法吗?是否
我正在尝试运行 Azure Orchestrations,突然我开始从 statusQueryGetUri 收到错误: 协调器函数“UploadDocumentOrchestrator”失败:函数“U
我见过 iPhone 上的应用程序,如果在 3.0 上运行,将使用 3.0 功能/API,例如应用内电子邮件编辑器,如果在 2.x 上运行,则不使用这些功能,并退出应用程序以启动邮件相反。 这是怎么做
这是 DB 规范化理论中的一个概念: Third normal form is violated when a non-key field is a fact about another non-ke
如果我定义 #if SOMETHING #endif 而且我还没有在任何地方定义 SOMETHING。 #if 中的代码会编译吗? 最佳答案 当#if的参数表达式中使用的名称未定义为宏时(在所有其他宏
我刚刚澄清了 A* 路径查找应该如何在两条路径具有相等值的 [情况] 下运行,无论是在计算期间还是在结束时,如果有两条相等的短路径。 例如,我在我的起始节点,我可以扩展到两个可能的节点,但它们都具有相
Java有没有类似下面的东西 宏 一种遍历所有私有(private)字段的方法 类似于 smalltalk symbols 的东西——即用于快速比较静态字符串的东西? 请注意,我正在尝试为 black
这个程序应该将华氏度转换为摄氏度: #include int main() { float fahrenheit, celsius; int max, min, step;
当打开PC缓存功能后, 软件将采用先进先出的原则排队对示波器采集的每一帧数据, 进行帧缓存。 当发现屏幕中有感兴趣的波形掠过时, 鼠标点击软件的(暂停)按钮, 可以选择回看某一帧的波形
我有一个特殊的(虚拟)函数,我想在沙盒环境中使用它: disable.system.call eval(parse(text = 'model.frame("1 ~ 1")'), envir = e
使用新的 Service 实现,我是否必须为我的所有服务提供一个 Options 方法? 使用我的所有服务当前使用的旧 ServiceBase 方法,OPTIONS 返回 OK,但没有 Access-
我正在阅读 Fogus 的关于 Clojure 的喜悦的书,在并行编程章节中,我看到了一个函数定义,它肯定想说明一些重要的事情,但我不知道是什么。此外,我看不到这个函数有什么用 - 当我执行时,它什么
我有大量的 C 代码,大部分代码被注释掉和/或 #if 0。当我使用 % 键匹配 if-else 的左括号和右括号时,它也匹配注释掉的代码。 有没有办法或vim插件在匹配括号时不考虑注释掉或#if 0
我有这个功能: map(map(fn x =>[x])) [[],[1],[2,3,4]]; 产生: val it = [[],[[1]],[[2],[3],[4]]] 我不明白这个功能是如何工作的。
我使用 Visual Studio 代码创建了一个函数应用程序,然后发布了它。功能应用程序运行良好。我现在在功能门户中使用代码部署功能(KUDU)并跳过构建。下面是日志 9:55:46 AM
我有一个数据框df: userID Score Task_Alpha Task_Beta Task_Charlie Task_Delta 3108 -8.00 Easy Easy
我真的无法解决这个问题: 我有一个返回数据框的函数。但是,数据框仅打印在我的控制台中,尽管我希望将其存储在工作空间中。我怎样才能做到这一点? 样本数据: n <- 32640 t <- seq(3*p
有没有办法找出所有可能的激活器命令行选项? activator -help仅提供最低限度的可用选项/功能列表,但所有好的东西都隐藏起来,即使在 typesafe 网站在线文档中也不可用。 到目前为止,
我是一名优秀的程序员,十分优秀!