栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

TraceRouteC++实现

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

TraceRouteC++实现

TraceRouteC++实现 使用cmd自带的TraceRoute

使用C++实现tracert

traceroute

关掉公用网络防火墙:

再关掉域防火墙:

TraceRoute.h
#pragma once
//TraceRoute.h
#ifndef TRACEROUTE_H_INCLUDED
#define TRACEROUTE_H_INCLUDED

#pragma pack(1)
//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位协议 (TCP, UDP etc)
    unsigned short checksum;  // 16位首部校验和
    unsigned long sourceIP;  // 32位原地址
    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;
#pragma pack()
//ICMP类型字段
const BYTE ICMP_ECHO_REPLY = 0; //回显应答
const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
const BYTE ICMP_TIMEOUT = 11; //传输超时
const DWORD DEF_ICMP_TIMEOUT = 3000; //默认超时时间,单位ms
const int DEF_ICMP_DATA_SIZE = 32; //默认ICMP数据部分长度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP数据报的大小
const int DEF_MAX_HOP = 30;    //最大跳站数
USHORT GenerateChecksum(USHORT* pBuf, int iSize);
BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult);



#endif // TRACEROUTE_H_INCLUDED

TraceRoute.cpp
//TraceRoute.cpp

#include 
#include 
#include 
#include 
#include 
#include "TraceRoute.h"
#pragma comment(lib,"ws2_32")
using namespace std;

int main()
{
    while (1)
    {
        
        char ipString[100];
        cout << "TraceRoute:";
        scanf_s("%s", ipString,sizeof(ipString));
        //char *ipString = "www.baidu.com";

        
        WSADATA wsa;//一种数据结构。这个结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)//进行相应的socket库绑定,MAKEWORd(2,2)表示使用WINSOCK2版本
        {
            cerr << "nFailed to initialize the WinSock2 DLLn" << "error code: " << WSAGetLastError() << endl;//Cerr通常用于输出错误信息与其他不属于正常逻辑的输出内容
            return -1;
        }

        
        u_long ulDestIP = inet_addr(ipString);//将一个点分十进制的IP转换成一个长整数型数(u_long类型)

        //cout<<"测试————"<<"长整型数"<h_addr).s_addr;//in_addr 用来表示一个32位的IPv4地址.            /cout<<"测试————"<<"ip地址"<type = ICMP_ECHO_REQUEST;
        pIcmpHeader->code = 0;
        pIcmpHeader->id = (USHORT)GetCurrentProcessId();
        memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据部分填充
//开始探测路由
        DECODE_RESULT stDecodeResult;
        BOOL bReachDestHost = FALSE;
        USHORT usSeqNo = 0;
        int iTTL = 1;
        int iMaxHop = DEF_MAX_HOP;
        while (!bReachDestHost && iMaxHop--)
        {
            //设置IP数据报头的ttl字段
            setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));//为0(IPPROTO_IP)的raw socket。用于接收任何的IP数据包。其中的校验和和协议分析由程序自己完成。
            //输出当前跳站数作为路由信息序号
            cout << setw(3) << iTTL << flush;//setw(3)设置域宽,cout<cksum = 0;
            ((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);//将无符号短整型主机字节序转换为网络字节序,将一个数的高低位互换, (如:12 34 --> 34 12)
            ((ICMP_HEADER*)IcmpSendBuf)->cksum = GenerateChecksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE);

            //记录序列号和当前时间
            stDecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;
            stDecodeResult.dwRoundTripTime = GetTickCount();//返回从操作系统启动到当前所经过的毫秒数,常常用来判断某个方法执行的时间

            //发送ICMP的EchoRequest数据报
            if (sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr*)&destSockAddr, sizeof(destSockAddr)) == SOCKET_ERROR)
            {
                //如果目的主机不可达则直接退出
                if (WSAGetLastError() == WSAEHOSTUNREACH)
                    cout << '/t' << "Destination host unreachable.n" << "nTrace complete.n" << endl;
                closesocket(sockRaw);
                WSACleanup();
                return 0;
            }
            //接收ICMP的EchoReply数据报
            //因为收到的可能并非程序所期待的数据报,所以需要循环接收直到收到所要数据或超时
            sockaddr_in from;
            int iFromLen = sizeof(from);
            int iReadDataLen;
            while (1)
            {
                //等待数据到达
                iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &iFromLen);
                if (iReadDataLen != SOCKET_ERROR) //有数据包到达
                {
                    //解码得到的数据包,如果解码正确则跳出接收循环发送下一个EchoRequest包
                    if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, stDecodeResult))
                    {
                        if (stDecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)
                            bReachDestHost = TRUE;
                        cout << 't' << inet_ntoa(stDecodeResult.dwIPaddr) << endl;//将网络地址转换成“.”点隔的字符串格式
                        break;
                    }
                }
                else if (WSAGetLastError() == WSAETIMEDOUT) //接收超时,打印星号
                {
                    cout << setw(9) << '*' << 't' << "Request timed out." << endl;
                    break;
                }
                else
                {
                    cerr << "nFailed to call recvfromn"
                        << "error code: " << WSAGetLastError() << endl;
                    closesocket(sockRaw);
                    WSACleanup();
                    return -1;
                }
            }
            //TTL值加1
            iTTL++;
            //cout<<"测试————"< 1)//40
    {
        cksum += *pBuf++;
        iSize -= sizeof(USHORT);
    }
    if (iSize)
        cksum += *(UCHAR*)pBuf;//8
    //printf("测试——cksum——测试:%xn",cksum);
    //printf("测试——cksum>>16——测试:%xn",cksum>>16);
    //printf("测试——cksum & 0xffff——测试:%xn",cksum & 0xffff);
    cksum = (cksum >> 16) + (cksum & 0xffff);
    //printf("测试——cksum——测试:%xn",cksum);
    //printf("测试——cksum>>16——测试:%xn",cksum>>16);
    cksum += (cksum >> 16);
    //printf("测试——cksum——测试:%xn",cksum);
    //printf("测试——(USHORT)(~cksum)——测试:%xn",(USHORT)(~cksum));
    return (USHORT)(~cksum);//~ 按位取反
}


BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult)
{
    //检查数据报大小的合法性
    IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
    int iIpHdrLen = pIpHdr->hdr_len * 4;//单位4个字节
    if (iPacketSize < (int)(iIpHdrLen + sizeof(ICMP_HEADER)))
        return FALSE;
    //按照ICMP包类型检查id字段和序列号以确定是否是程序应接收的Icmp包
    ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf + iIpHdrLen);
    USHORT usID, usSquNo;//ICMP头 标识符和序列号
    if (pIcmpHdr->type == ICMP_ECHO_REPLY)
    {
        usID = pIcmpHdr->id;
        usSquNo = pIcmpHdr->seq;
    }
    else if (pIcmpHdr->type == ICMP_TIMEOUT)
    {
        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;
        usSquNo = pInnerIcmpHdr->seq;
    }
    else
        return FALSE;
    if (usID != (USHORT)GetCurrentProcessId() || usSquNo != stDecodeResult.usSeqNo)
        return FALSE;
    //处理正确收到的ICMP数据报
    if (pIcmpHdr->type == ICMP_ECHO_REPLY ||
        pIcmpHdr->type == ICMP_TIMEOUT)
    {
        //返回解码结果
        stDecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;
        stDecodeResult.dwRoundTripTime = GetTickCount() - stDecodeResult.dwRoundTripTime;
        //打印屏幕信息
        if (stDecodeResult.dwRoundTripTime)
            cout << setw(6) << stDecodeResult.dwRoundTripTime << " ms" << flush;
        else
            cout << setw(6) << "<1" << " ms" << flush;
        return TRUE;
    }
    return FALSE;
}
参考文章

https://blog.csdn.net/qq_41577750/article/details/109196890

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/457153.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号