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; //报文 ID
        usSquNo = 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; //报文 ID
        usSquNo = 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;
        else
            cout << " " << "<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; //代码字段为 0
        pIcmpHeader->id = (USHORT) GetCurrentProcessId(); //ID 字段为当前进程号
        memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据字段
        USHORT usSeqNo = 0; //ICMP 报文序列号
        int iTTL = 1; //TTL 初始值为 1
        BOOL 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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/303277.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于深度学习的PCB板缺陷检测系统(含UI界面、yolov5、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov5 yolov5主要包含以下几种创新&#xff1a;         1. 添加注意力机制&#xff08;SE、CBAM、CA等&#xff09;         2. 修改可变形卷积&#xff08;DySnake-主…

linux执行.sh文件出现问题--排查以及解决

阿丹问题描述&#xff1a; 今天在运行.sh文件的时候出现 现象1&#xff1a; 现象2&#xff1a; 现象3&#xff1a; 出现这三种问题 问题解释以及问题排查&#xff1a; 现象1&#xff1a; 排查&#xff1a; 1、判断文件是否存在 2、判断权限是否足够 解决&#xff1…

【AI视野·今日Sound 声学论文速览 第四十二期】Fri, 5 Jan 2024

AI视野今日CS.Sound 声学论文速览 Fri, 5 Jan 2024 Totally 10 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers PosCUDA: Position based Convolution for Unlearnable Audio Datasets Authors Vignesh Gokul, Shlomo Dubnov深度学习模型需要大量干净的…

1.8 day6 IO进程线程

使用有名管道实现两个进程之间的通信 进程A #include <myhead.h> int main(int argc, const char *argv[]) {//创建两个文件描述符用于打开两个管道int fd1-1;int fd2-1;//创建一个子进程int pid-1;if((fd1open("./mkfifo1",O_RDWR))-1){perror("open er…

java中常见的一些小知识(1)

1.数组转List 1.1. Arrays.asList public class Tesr {public static void main(String[] args) {String[] ary new String[]{ "1", "a"};List<String> list Arrays.asList((ary));list.add("ddsdsa");System.out.println(list);}}但是…

SCI一区级 | Matlab实现RIME-CNN-LSTM-Mutilhead-Attention多变量多步时序预测

SCI一区级 | Matlab实现RIME-CNN-LSTM-Mutilhead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-LSTM-Mutilhead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-LSTM-Mutilhead-Attention霜冰算法…

简单几步,实现餐厅扫码点餐

越来越多的人选择外出就餐&#xff0c;而餐厅的点餐方式也随着科技的发展而不断进步。其中&#xff0c;扫码点餐是最为常见的一种方式&#xff0c;它不仅方便快捷&#xff0c;还能节省人力成本。本文将介绍一种简单易行的餐厅扫码点餐解决方案。 打开乔拓云平台&#xff0c;登录…

华为认证 | 这门HCIE认证正式发布!

华为认证openEuler专家HCIE-openEuler V1.0&#xff08;中文版&#xff09;自2023年12月29日起&#xff0c;正式在中国区发布。 01 发布概述 基于“平台生态”战略&#xff0c;围绕“云-管-端”协同的新ICT技术架构&#xff0c;华为公司打造了覆盖ICT领域的认证体系&#xff0…

【JavaSE】Java中的反射动态代理

本篇文章整理的内容来源于: 反射原理 文章目录 一. 动态代理1. 优点2. 动态代理三要素3. 创建代理对象并使用 二. 反射1. 什么是反射2. 获取字节码文件对象的三种方式(1) Class.forName()获取 (源代码阶段)(2) 通过class属性获取(3) 通过对象获取字节码文件对象 3. 获取构造方…

基于深度学习的停车位关键点检测系统(代码+原理)

摘要&#xff1a; DMPR-PS是一种基于深度学习的停车位检测系统&#xff0c;旨在实时监测和识别停车场中的停车位。该系统利用图像处理和分析技术&#xff0c;通过摄像头获取停车场的实时图像&#xff0c;并自动检测停车位的位置和状态。本文详细介绍了DMPR-PS系统的算法原理、…

django websocket实现聊天室功能

注意事项channel版本 django2.x 需要匹配安装 channels 2 django3.x 需要匹配安装 channels 3 Django3.2.4 channels3.0.3 Django3.2.* channels3.0.2 Django4.2 channles3.0.5 是因为最新版channels默认不带daphne服务器 直接用命令 python manage.py runsever 默认运行的是w…

数据库系统-甘晴void学习笔记

数据库系统笔记 计科210X 甘晴void 202108010XXX 教材&#xff1a;《数据库系统概论》第6版 &#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文章目录 数据库系统<br>笔记第一篇 基础篇1 绪论1.1数据库系统概述1.2数据模型1.3数据库系统的结构(三级模式结构…

JSP内置对象:forward动作标记和sendRedirect()方法实现页面跳转的区别

1.forward为服务器跳转&#xff0c;浏览器的地址栏不变&#xff1b; sendRedirect&#xff08;&#xff09;为客户端跳转&#xff0c;浏览器的地址栏改变变新页面的URL。 2.执行到forward标记出现处停止当前JSP页面的继续执行&#xff0c;而转向标记中的page属性指定的页面&am…

k8s的三种发布方式

三种常见的发布方式 应用程序升级面临最大挑战是新旧业务切换&#xff0c;将软件从测试的最后阶段带到生产环境&#xff0c;同时要保证系统不间断提供服务。而最为常见三种发布方式分别为&#xff1a;蓝绿发布&#xff0c;灰度发布和滚动发布。 三种发布方式的最终目的都是为了…

12 月 NFT 市场动态:强劲增长塑造年终趋势

作者&#xff1a;stellafootprint.network 数据来源&#xff1a;NFT Research 12 月加密货币和 NFT 领域出现了显著的上涨趋势&#xff0c;比特币和以太坊价格的大幅上涨标志着市场的复苏。与此同时&#xff0c;NFT 领域的交易量飙升&#xff0c;独立用户&#xff08;钱包&am…

508基于51单片机的火灾检测与报警系统设计

基于51单片机的火灾检测与报警系统设计[proteus仿真] 火灾检测与报警系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的火灾检测与报警系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 …

【开源硬件篇】STM32F103C8T6核心板

STM32F103C8T6核心板 文章目录 STM32F103C8T6核心板一、STM32F103C8T6芯片1.1 STM32F103C8T6简介1.2 芯片引脚说明 二、去耦电路2.1 原理图设计2.2 原理分析2.2.1 结论2.2.2 去耦效果图2.2.3 放置距离问题2.2.3 放置位置问题 2.3 PCB设计示例 三、晶振电路3.1 原理图设计3.2 原…

龙芯+RT-Thread+LVGL实战笔记(29)——电子琴弹奏

【写在前面】临近期末,笔者工作繁忙,因此本系列教程的更新频率有所放缓,还望订阅本专栏的朋友理解,请勿催更。笔者在此也简要声明几点: 有些硬件模块笔者并没有,如LED点阵、压力传感模块、RFID模块等,因此这些模块的相关任务暂时无法给出经过验证的代码。其实,教程进行…

uniapp地图兼容小程序和APP(高德地图),点击marker并弹框详情

1.uniapp地图页面兼容小程序和APP 2.小程序使用map组件APP使用高德地图 3.点击定位按钮地图自动移动到定位点 4.APP地图逻辑是视图层交互使用renderjs 5.点击地图marker弹框详情 6.全部代码 <template><page-meta :page-style"overflow:(show?hidden:visible)&…

视频号小店怎么上架商品?实操分享,干货满满!

我是电商珠珠 视频号小店从22年7月到现在也不过才发展了一年&#xff0c;它的风口才刚刚开始。 平台为了吸引商家入驻&#xff0c;会将大量红利向商家倾斜&#xff0c;只要把握住风口&#xff0c;就会很快起飞。 视频号小店对于很多人来说&#xff0c;都是新平台&#xff0c…