tracert程序的实现
计算机网络课程设计
开发环境:
操作系统:window 10
编译环境:VC 6.0(用dev无法编译)
编程语言:C++
运行程序时需关闭电脑防火墙以便获取全部数据
代码如下:
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
using namespace std;#pragma comment(lib, "Ws2_32.lib")//IP报头
typedef struct IP_HEADER
{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 ICMP_HEADER
{BYTE type; //8位类型字段BYTE code; //8位代码字段USHORT cksum; //16位校验和USHORT id; //16位标识符USHORT seq; //16位序列号
} ICMP_HEADER;//报文解码结构
typedef struct DECODE_RESULT
{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 )//如果 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; //ip报头的长度是以4字节为单位的//若数据包大小 小于 IP报头 + ICMP报头,则数据报大小不合法if ( iPacketSize < ( int )( iIpHdrLen + sizeof( ICMP_HEADER ) ) )return FALSE;//根据ICMP报文类型提取ID字段和序列号字段ICMP_HEADER *pIcmpHdr = ( ICMP_HEADER * )( pBuf + iIpHdrLen );//ICMP报头 = 接收到的缓冲数据 + IP报头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;
}void main()
{//初始化Windows sockets网络环境WSADATA wsa;WSAStartup( MAKEWORD(2,2), &wsa );char IpAddress[255];cout<<"本程序为tracert程序的实现,输入域名或者IP地址,可以向目标域名或IP地址发送ICMP回显数据报文,收到回显数据报文解析后,可以显示数据报从源端机器传送到目标机器的过程中所经过的路由器的IP地址,并且记录了数据报到达每个路由器所需要的时间。提供了变量n指定最大传输次数。\n";cout<<"请输入一个IP地址或域名:";cin>>IpAddress;//得到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;}}int DEF_MAX_HOP;printf("本程序需要客户指定输出多少次时间记录,请输入一个x(x为整数)");//程序提供了"-n"选项,用以输出指定次数的时间记录。scanf("%d",&DEF_MAX_HOP);cout<<"Tracing roote to "<<IpAddress<<" with a maximum of " << DEF_MAX_HOP <<" hops.\n"<<endl;//填充目的端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 );//超时时间int iTimeout = 3000;//设置接收超时时间setsockopt( sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeout, sizeof( iTimeout ) );//设置发送超时时间setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeout,sizeof(iTimeout));//构造ICMP回显请求消息,并以TTL递增的顺序发送报文//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 = 3000; //回显应答超时时间//填充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; //输出当前序号,flush表示将缓冲区的内容马上送进cout,把输出缓冲区刷新//填充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(1){//接收数据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;//输出IP地址cout<<'\t'<<inet_ntoa( DecodeResult.dwIPaddr )<<endl;break;}}else if( WSAGetLastError() == WSAETIMEDOUT ) //接收超时,输出*号{cout<<" *"<<'\t'<<"Request timed out."<<endl;break;}else{break;}}iTTL++; //递增TTL值}
}
tracert程序的实现相关推荐
- c语言tracert程序一直超时,TRACERT怎么用我用这个命令为什么都是超时啊 爱问知识人...
TRACERT又叫跟踪路由命令,具体用法如下: 步骤一:点击电脑左下角开始--运行,输入cmd,进入命令提示符窗口 步骤二:在命令行中输入"tracert "并在后面加入一个IP地 ...
- C++实现路由追踪(Tracert)程序
目录 ICMP协议介绍 Tracert实现原理 代码部分 测试结果 ICMP协议介绍 ICMP是internet控制报文 ...
- c语言tracert程序一直超时,traceroute – tracert命令返回超时
tracert返回请求的超时时间.我从中理解的是数据包丢失了网络上的某些位置. 这是否意味着问题出在ISP或托管服务提供商或我的Windows系统上? 10 * * * Request timed o ...
- 命令提示符(cmd)中的tracert命令使用
转载自:https://jingyan.baidu.com/article/9c69d48f4df25713c8024e66.html 在命令行中输入"tracert "并在后面加 ...
- 快速排除故障命令除了ping还有tracert
[欢迎关注微信公众号:厦门微思网络] Tracert简介 Tracert是测试报文从发送端到目的地所经过的路由的方法.它能够直观展现报文在转发的时候所经过的路径. Tracert基于ICMP协议来实现 ...
- 计算机网络课程设计之Tracert与Ping程序设计与实现
前言 本实验主要是应用ICMP报文实现Tracert和Ping功能,主要用的是Windows中的库,所以程序只能在Windows下运行. 在博客结束的地方,附上C/C++的Tracert源码和Ping ...
- Tracert与Traceroute[转]
Tracert与Traceroute traceroute是一个检查网络路径的工具,最初由Van Jacobson实现.它现在已经成为Linux.Cisco IOS以及其他很多操作系统的基本网络工具之 ...
- 网络连通性测试ping和tracert命令
ping和tracert命令 Ping简介 Ping是基于ICMP协议实现的.通过从源端向目的端发送ICMP回显请求(Echo Request)报文后,根据是否收到目的端的ICMP回显应答(Echo ...
- 【计算机网络实验02】tracert、arp命令
本系列博客以计算机网络相关实验为主,包括windows命令实验.wireshark实验.Packet Tracer路由交换实验,也是自己整个学习过程中的一个记录,望不吝赐教. 计算机网络实验02 wi ...
最新文章
- MySQL 5.6 Warning - Using a password on the command line interface can be insecur 解决方案
- php的array_multisort()使用
- 澳大利亚量子计算机获突破 首次实现简化逻辑门
- 每日程序C语言25-查找100以内的素数
- C#三层架构第四课之DAL层
- Python基础(8)_迭代器、生成器、列表解析
- Linux的实际操作:文件目录类的实用指令(ln history)
- 火狐浏览器中文乱码怎么办 Firefox中文乱码解决方法
- OpenShift 4 之 配置基于Red Hat SSO的Identity Providers
- 面向流批一体的 Flink Runtime 新进展
- 活动目录实战之六 使用ADMT 3.2迁移用户和计算机
- 使用yolov5训练PASCAL VOC2012数据集以及遇到的坑
- 彻底删除vmware
- css横向导航栏布局,CSS04--对齐、 布局、导航栏
- 电脑重装系统后无法上网怎么办
- 纯js封装一个多功能弹出框
- MAC默认软件安装位置
- 【DBA100人】Payso张耀辉:学材料专业出身的他转身做了“码农”
- 如果iPhone被标记被盗或丢失 苹果将拒绝维修
- 数字人民币试点目前呈现“全面开花”态势