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

本文详细介绍了如何使用C++实现Tracert程序并结合计算机网络原理编写Ping程序,包括IP和ICMP报文结构、报文发送与接收、以及网络通信的相关概念。
摘要由CSDN通过智能技术生成

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博客

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: tracert和ping是两种常用的网络诊断工具。 tracert(Windows)或traceroute(Linux/Unix)是一种命令行工具,用于查找网络数据包经过的路由器。它通过发送ICMP回显请求和接收ICMP回显应答来实现。每当数据包经过一个路由器时,路由器都会在应答中添加自己的IP地址。tracert收到的应答可以用来构建路由路径的图像。 ping是一种网络诊断工具,用于测试两台计算机之间的网络连通性。它通过发送ICMP回显请求和接收ICMP回显应答来实现。如果目标主机能够收到请求并正确应答,则说明网络连通。 ### 回答2: TRACERT(跟踪路由)和PING网络包探测器)都是网络诊断工具,在网络故障排除过程中有很好的作用。以下是两者的程序设计实现方式。 TRACERT程序设计实现TRACERT程序的主要功能是确认数据包从源地址(发起机器)到达目标地址(终端机器)所经过的路由器路由路径。以下是TRACERT程序的主要设计实现: 1. 发送数据包:使用ICMP数据报,发送的数据包TTL(存活时间)为1,目标地址为目标机器IP地址。 2. 接收数据包:同时执行ping操作来接收回传ICMP数据报。 3. 分析路由路径:当数据包弹回到本机时,TRACERT分析ICMP数据报中的路由器IP地址。然后使用相同的方法(含TTL加1)向目标地址发送另一个数据包,并重复这个步骤直到到达目标机器,随后分析整个路由历史记录以得到完整的路由路径。 4. 显示路由路径:将分析得到的路由路径显示出来。 PING程序设计实现PING程序的主要功能是检测目标机器是否可达,以及确认网络时延和丢包率等网络参数。以下是PING程序的主要设计实现: 1. 发送数据包:使用ICMP数据报,其中有一字段是标识码,另一字段是序列号,这些信息在接收到回声回报后被记录。 2. 接收数据包:当目标机器接收到数据包,立刻发送回答数据包(称为“回声回报”),由源机器接收。 3. 计算时延:PING程序使用RTT(往返时间)来计算往返时延。源机器记录下发送数据包的时刻和接收到回声回报的时刻,然后计算RTT。 4. 显示网络参数:PING程序根据接收到的回声回报,确认网络是否存在丢包现象,并计算丢包率。将计算得到的网络参数显示出来。 总结: TRACERT和PING都是网络故障排除工具,它们的实现方式非常相似。不同之处在于TRACERT主要用于确认路由路径,而PING主要用于测量网络连接的状态。这些工具的设计实现需要充分理解TCP/IP的基础概念,可以通过深入的网络诊断掌握这些知识点。 ### 回答3: tracert与ping是计算机网络中常用的诊断工具,用于检测网络中连接问题以及提供网络连接的质量。这两个工具都可以在Windows、Linux以及其他操作系统中使用,并且都可以通过控制台或命令行启动。 tracert(或traceroute)工具主要用于检测网络中的网络路径和延迟问题。它通过将特殊的IP数据包发送到目标主机,然后在它们传输时,每个网络路由器都记录对应的路由表。这些数据包的“跳数”(即经过多少个路由器)会逐渐增加,直到它们到达目标主机或被时间限制截断。这些记录可以帮助诊断网络中的连接问题,例如路由器断开连接或网络拥塞。 为了实现tracert程序,需要使用一种能够控制IP数据包的协议,例如ICMP(Internet控制消息协议)。该协议可以发送控制信息(例如ping响应)给远程主机,并收集远程主机的响应数据。通过控制这些数据包并检测响应,tracert程序可以记录网络上的所有路由器地址,并计算每个路由器之间的延迟时间。这些信息可以帮助诊断连接问题并优化网络结构。 相比之下,ping工具更为简单,主要用于检测网络上的主机是否可达。该工具发送ICMP回声请求消息到目标主机,并等待目标主机返回相应的回声响应。如果目标主机能够正常响应,则意味着目标主机在线并且网络连接正常。如果收到超时或错误响应,则可能意味着网络存在断开连接或受干扰等问题。 因为ping工具需要发送ICMP数据包并等待目标主机响应,所以实现ping程序需要使用一种能够操作这些数据包的协议,例如ICMP或UDP。程序应该能够在目标主机响应之前设置超时时间,以避免长时间等待响应。同时,应该能够解析远程主机发回的响应消息,以检测连接问题。 综上所述,tracert与ping网络工程师中必备的网络诊断工具。它们的实现主要涉及操作网络数据包的协议。利用它们可以在网络中检测连接问题以及创建网络结构的优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值