Tracert 与 Ping 程序设计与实现(2024)

2024-01-07 09:44

本文主要是介绍Tracert 与 Ping 程序设计与实现(2024),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.题目描述

了解 Tracert 程序的实现原理,并调试通过。然后参考 Tracert 程序和计算机网络教材 4.4.2 节, 计算机网络 课程设计指导书 2 编写一个 Ping 程序,并能测试本局域网的所有机器是否在线,运行界面如下图所示的 QuickPing 程序。


2.程序Demo

aaf753f838634837989a1fc8fbb5e2bf.png


3.参考代码

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string>
#include "sstream"using namespace std;
#pragma comment(lib, "Ws2_32.lib")
//IP 报头
typedef struct {unsigned char hdr_len: 4;       //4 位头部长度unsigned char version: 4;       //4 位版本号unsigned char tos;              //8 位服务类型unsigned short total_len;       //16位总长度unsigned short identifier;      //16位标识符unsigned short frag_and_flags;  //3 位标志加 13 位片偏移unsigned char ttl;              //8 位生存时间unsigned char protocol;         //8 位上层协议号unsigned short checksum;        //16位校验和unsigned long sourceIP;         //32位源 IP 地址unsigned long destIP;           //32位目的 IP 地址
} IP_HEADER;
//ICMP 报头
typedef struct {BYTE type; //8 位类型字段BYTE code; //8 位代码字段USHORT cksum; //16 位校验和USHORT id; //16 位标识符USHORT seq; //16 位序列号
} ICMP_HEADER;
//报文解码结构
typedef struct {USHORT usSeqNo; //序列号DWORD dwRoundTripTime; //往返时间in_addr dwIPaddr; //返回报文的 IP 地址
} DECODE_RESULT;//计算网际校验和函数
USHORT checksum(USHORT *pBuf, int iSize) {unsigned long cksum = 0;while (iSize > 1) {cksum += *pBuf++;iSize -= sizeof(USHORT);}if (iSize) {cksum += *(UCHAR *) pBuf;}cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >> 16);return (USHORT) (~cksum);
}//对数据包进行解码
BOOL DecodeIcmpResponse(char *pBuf, int iPacketSize, DECODE_RESULT &DecodeResult, BYTE ICMP_ECHO_REPLY, BYTE ICMP_TIMEOUT) {//检查数据报大小的合法性IP_HEADER *pIpHdr = (IP_HEADER *) pBuf;int iIpHdrLen = pIpHdr->hdr_len * 4;if (iPacketSize < (int) (iIpHdrLen + sizeof(ICMP_HEADER))) return FALSE;//根据 ICMP 报文类型提取 ID 字段和序列号字段ICMP_HEADER *pIcmpHdr = (ICMP_HEADER *) (pBuf + iIpHdrLen);USHORT usID, usSquNo;if (pIcmpHdr->type == ICMP_ECHO_REPLY) //ICMP 回显应答报文{usID = pIcmpHdr->id; //报文 IDusSquNo = pIcmpHdr->seq; //报文序列号} else if (pIcmpHdr->type == ICMP_TIMEOUT)//ICMP 超时差错报文{char *pInnerIpHdr = pBuf + iIpHdrLen + sizeof(ICMP_HEADER); //载荷中的 IP 头int iInnerIPHdrLen = ((IP_HEADER *) pInnerIpHdr)->hdr_len * 4; //载荷中的 IP 头长ICMP_HEADER *pInnerIcmpHdr = (ICMP_HEADER *) (pInnerIpHdr + iInnerIPHdrLen);//载荷中的 ICMP头usID = pInnerIcmpHdr->id; //报文 IDusSquNo = pInnerIcmpHdr->seq; //序列号} else {return false;}//检查 ID 和序列号以确定收到期待数据报if (usID != (USHORT) GetCurrentProcessId() || usSquNo != DecodeResult.usSeqNo) {return false;}//记录 IP 地址并计算往返时间DecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime;//处理正确收到的 ICMP 数据报if (pIcmpHdr->type == ICMP_ECHO_REPLY || pIcmpHdr->type == ICMP_TIMEOUT) {//输出往返时间信息if (DecodeResult.dwRoundTripTime)cout << " " << DecodeResult.dwRoundTripTime << "ms" << flush;elsecout << " " << "<1ms" << flush;}return true;
}// 数字转String
string num2str(int &num) {string str;stringstream ss;ss << num;ss >> str;return str;
}//提取IP
string GetIP(string ip) {char separator = '.';int i = 0, count = 3;// Temporary string used to split the string.string s;while (ip[i] != '\0') {if (ip[i] != separator) {s += ip[i];} else {s += ip[i];count--;}if (count == 0) {break;}i++;}return s;
}int main() {cout<<" *======Tracert 与 Ping 程序设计与实现======* "<<endl;//定义=====================================================================int iTimeout = 10;                    //超时时间const int DEF_MAX_HOP = 1;             //检查次数BOOLEAN Show_All = false;              //是否输出所有信息string Scan_ip;//ICMP 类型字段const BYTE ICMP_ECHO_REQUEST = 8;      //请求回显const BYTE ICMP_ECHO_REPLY = 0;        //回显应答const BYTE ICMP_TIMEOUT = 11;          //传输超时//其他常量定义const int DEF_ICMP_DATA_SIZE = 32;     //ICMP 报文默认数据字段长度const int MAX_ICMP_PACKET_SIZE = 1024; //ICMP 报文最大长度(包括报头)const DWORD DEF_ICMP_TIMEOUT = 100;    //回显应答超时时间int OnlineNum = 0;//=======================================================================cout << "请输入路由器IP:";cin >> Scan_ip;//初始化 Windows sockets 网络环境WSADATA wsa;WSAStartup(MAKEWORD(2, 2), &wsa);const char *IpAddress;string IpA;cout <<endl<<" 开始扫描:"<<endl;cout << "----------------------------------------------------------------" << endl;cout << "     耗时         机器IP           序号           状态          |" << endl;cout << "----------------------------------------------------------------" << endl;for (int i = 1; i < 254; i++) {IpA = GetIP(Scan_ip)+ num2str(i);IpAddress = IpA.c_str();// cout << IpA << endl;//得到 IP 地址u_long ulDestIP = inet_addr(IpAddress);//转换不成功时按域名解析if (ulDestIP == INADDR_NONE) {hostent *pHostent = gethostbyname(IpAddress);if (pHostent) {ulDestIP = (*(in_addr *) pHostent->h_addr).s_addr;} else {cout << "输入的 IP 地址或域名无效!" << endl;WSACleanup();return 0;}}//填充目地端 socket 地址sockaddr_in destSockAddr;ZeroMemory(&destSockAddr, sizeof(sockaddr_in));destSockAddr.sin_family = AF_INET;destSockAddr.sin_addr.s_addr = ulDestIP;//创建原始套接字SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);//接收超时setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char *) &iTimeout, sizeof(iTimeout));//发送超时setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char *) &iTimeout, sizeof(iTimeout));//填充 ICMP 报文中每次发送时不变的字段char IcmpSendBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE];//发送缓冲区memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf)); //初始化发送缓冲区char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE]; //接收缓冲区memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf)); //初始化接收缓冲区ICMP_HEADER *pIcmpHeader = (ICMP_HEADER *) IcmpSendBuf;pIcmpHeader->type = ICMP_ECHO_REQUEST; //类型为请求回显pIcmpHeader->code = 0; //代码字段为 0pIcmpHeader->id = (USHORT) GetCurrentProcessId(); //ID 字段为当前进程号memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据字段USHORT usSeqNo = 0; //ICMP 报文序列号int iTTL = 1; //TTL 初始值为 1BOOL bReachDestHost = FALSE; //循环退出标志int iMaxHot = DEF_MAX_HOP; //循环的最大次数DECODE_RESULT DecodeResult; //传递给报文解码函数的结构化参数while (!bReachDestHost && iMaxHot--) {//设置 IP 报头的 TTL 字段setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char *) &iTTL, sizeof(iTTL));//  cout << iTTL << flush; //输出当前序号//填充 ICMP 报文中每次发送变化的字段((ICMP_HEADER *) IcmpSendBuf)->cksum = 0; //校验和先置为 0((ICMP_HEADER *) IcmpSendBuf)->seq = htons(usSeqNo++); //填充序列号((ICMP_HEADER *) IcmpSendBuf)->cksum = checksum((USHORT *) IcmpSendBuf,sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE); //计算校验和//记录序列号和当前时间DecodeResult.usSeqNo = ((ICMP_HEADER *) IcmpSendBuf)->seq; //当前序号DecodeResult.dwRoundTripTime = GetTickCount(); //当前时间//发送 TCP 回显请求信息sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr *) &destSockAddr, sizeof(destSockAddr));//接收 ICMP 差错报文并进行解析处理sockaddr_in from; //对端 socket 地址int iFromLen = sizeof(from); //地址结构大小int iReadDataLen; //接收数据长度while (true) {//接收数据iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr *) &from, &iFromLen);if (iReadDataLen != SOCKET_ERROR)//有数据到达{//对数据包进行解码if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, DecodeResult, ICMP_ECHO_REPLY, ICMP_TIMEOUT)) {//到达目的地,退出循环if (DecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)bReachDestHost = true;break;}} else if (WSAGetLastError() == WSAETIMEDOUT & Show_All) //接收超时,输出*号{cout << '\t' << "请求超时 " << DEF_MAX_HOP - iMaxHot << "次" << endl;break;} else {break;}}iTTL++; //递增 TTL 值}if (WSAGetLastError() != WSAETIMEDOUT) {cout << "         " << IpA << "            " << i << "            在线" << endl;OnlineNum++;if (Show_All)cout << "================================================================" << endl;} else if (WSAGetLastError() == WSAETIMEDOUT & Show_All) {cout << "         " << IpA << "             " << i << "           离线" << endl;cout << "================================================================" << endl;}}cout << "================================================================" << endl;cout << endl << "检测到 " << OnlineNum << " 台设备在线" << endl<< endl;system("pause");WSACleanup();return 0;
}

4.导入ws2_32库到Clion :

导入ws2_32库到Clion项目-CSDN博客

 2024 HNUST计算机网络课程设计-(ᕑᗢᓫ∗)˒芒果酱-参考文章

(代码可以参考,૮₍ ˃ ⤙ ˂ ₎ა 但同学们要认真编写哦)
-------------------------------------------------------------------------
1、网络聊天程序的设计与实现
C++ Socket 多线程 网络聊天室 支持用户端双向交流(2023)-CSDN博客
2、Tracert 与 Ping 程序设计与实现
Tracert 与 Ping 程序设计与实现(2024)-CSDN博客
3、滑动窗口协议仿真
滑动窗口协议仿真(2024)-CSDN博客
4、OSPF 路由协议原型系统设计与实现
OSPF 路由协议原型系统设计与实现-CSDN博客
5、基于 IP 多播的网络会议程序
基于 IP 多播的网络会议程序(2024)-CSDN博客
6、编程模拟 NAT 网络地址转换
编程模拟 NAT 网络地址转换(2024)-CSDN博客
7、网络嗅探器的设计与实现
网络嗅探器的设计与实现(2024)-转载-CSDN博客
8、网络报文分析程序的设计与实现
网络报文分析程序的设计与实现(2024)-CSDN博客
9、简单 Web Server 程序的设计与实现
简单 Web Server 程序的设计与实现 (2024)-CSDN博客
10、路由器查表过程模拟

计算机网络 - 路由器查表过程模拟 C++(2024)-CSDN博客

这篇关于Tracert 与 Ping 程序设计与实现(2024)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/579509

相关文章

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti