计算机网络课程设计-Tracert 与 Ping 程序设计与实现

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

目录

前言

1 实验题目

2 实验目的

3 实验内容

3.1 步骤

3.2 关键代码

3.2.1 发送ICMP数据报

3.2.2 解析收到的数据报

4 实验结果与分析

5 代码

5.1 ping代码

5.2 Tracert代码


前言

        本实验为计算机网络课程设计内容,基本上所有代码都是根据指导书给的附录写出来的。有些实验需要实现图形界面,但是出于期末考试压力,我所有实验均是在控制台输入输出的,没有花额外时间去学习qt了,有精力的同学可以自学一下qt实现简单的图形界面。同时,该博客内容为部分报告内容,仅为大家提供参考,请勿直接抄袭。另外,本次实验所用平台是dev c++5.11

1 实验题目

        实验二 Tracert 与 Ping 程序设计与实现

2 实验目的

        了解 Tracert 程序的实现原理,并调试通过。然后参考 Tracert 程序和教材 4.4.2 节,编写一个 Ping 程序,并能测试本局域网的所有机器是否在线。

3 实验内容

3.1 步骤

        (1)使用Socket建立网络连接。

        (2)构造ICMP报文。

        (3)发送ICMP请求并接收回显应答。

        (4)解析回显应答,进行主机在线状态的判断。

3.2 关键代码

3.2.1 发送ICMP数据报

//发送 TCP 回显请求信息
sendto(sockRaw,IcmpSendBuf,sizeof(IcmpSendBuf),0,(sockaddr*)&destSockAddr,sizeof(destSockAddr));

3.2.2 解析收到的数据报

//对数据包进行解码
BOOL DecodeIcmpResponse(char * pBuf,int iPacketSize,DECODE_RESULT &DecodeResult,BYTEICMP_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;
}

4 实验结果与分析

对地址范围192.168.0.106-108进行测试,可以从打印的结果看到哪些主机在线,同时可以验证程序的正确性。

图1.1 对某个地址范围进行测试

5 代码

5.1 ping代码

刚开始我是用traceroute直接改造的,但是有问题,后面我又改了代码,这个代码的前一个版本在输入不同跳数的时候有点问题,这个最新的代码我测试不同跳数的时候是没有问题,但是不知道有没有改出其他的毛病,所以下面附上源程序在5.2

/* 程序名称:路由追踪(Tracert)程序
实现原理:Tracert 程序关键是对 IP 头部生存时间(time to live)TTL 字段的使用,程序实现时是向目
地主机发送一个 ICMP 回显请求消息,初始时 TTL 等于 1,这样当该数据报抵达途中的第一个路由器
时,TTL 的值就被减为 0,导致发生超时错误,因此该路由生成一份 ICMP 超时差错报文返回给源主
机。随后,主机将数据报的 TTL 值递增 1,以便 IP 报能传送到下一个路由器,并由下一个路由器生成
ICMP 超时差错报文返回给源主机。不断重复这个过程,直到数据报达到最终的目地主机,此时目地
主机将返回 ICMP 回显应答消息。这样,源主机只需对返回的每一份 ICMP 报文进行解析处理,就可
以掌握数据报从源主机到达目地主机途中所经过的路由信息。
*/
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include<map>
#include<vector>
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;vector< pair<string,string> > IpAddressStatus1;//计算网际校验和函数
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,BYTEICMP_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;}
//	cout<<" pIpHdrLen="<<htons(pIpHdr->total_len); //填充序列号<<" ";cout<<" bytes="<<(int)iPacketSize-iIpHdrLen-8<<" ";cout<<"ttl="<<(int)pIpHdr->ttl<<" ";
//	cout<<"Protocol:"<<(int)pIpHdr->protocol<<"\n";//记录 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;
}
int main(void) {//初始化 Windows sockets 网络环境WSADATA wsa;WSAStartup(MAKEWORD(2,2),&wsa);char IpAddress[255];map<string,string> IpAddressStatus;int  ip1,ip2,ip3,ip4,ip5;int cnt = 255;int maxHops = 20;int maxTimeout=1000;cout<<"请输入一个 IP 地址范围(如192.168.142.119-255,只需要输入192 168 142 119 255):";cin>>ip1>>ip2>>ip3>>ip4>>ip5;cnt=ip5-ip4;while(ip1>255||ip2>255||ip3>255||ip4>255||cnt<0) {cout<<"输入的 IP 地址范围无效!请重新输入:"<<"\n";cin>>ip1>>ip2>>ip3>>ip4>>ip5;cnt=ip5-ip4;}cout<<"请输入超时时间(ms):";cin>>maxTimeout;cout<<"最大路由跳数:";cin>>maxHops;while(cnt>=0) {
//		if(ip1>255||ip2>255||ip3>255||ip4>255||) {
//			cout<<"输入的 IP 地址范围无效!请重新输入:"<<"\n";
//			cin>>ip1>>ip2>>ip3>>ip4>>ip5;
//			cnt=ip5-ip4;
//		}sprintf(IpAddress,"%d.%d.%d.%d",ip1,ip2,ip3,ip5-cnt);cnt--;//得到 IP 地址u_long ulDestIP=inet_addr(IpAddress);cout<<"\n正在 ping 的 ip 地址:"<<IpAddress<<"\n";//转换不成功时按域名解析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;}}
//		cout<<"Tracing route to "<<IpAddress<<" with a maximum of "<<maxHops<<" 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=maxTimeout;//接收超时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=maxTimeout; //回显应答超时时间
//		const int DEF_MAX_HOP=30; //最大跳站数const int DEF_MAX_HOP=maxHops; //最大跳站数//填充 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; //传递给报文解码函数的结构化参数int flag=0;int ping_ttl=4;while(!bReachDestHost&&ping_ttl--) {//设置 IP 报头的 TTL 字段
//			setsockopt(sockRaw,IPPROTO_IP,IP_TTL,(char *)&iTTL,sizeof(iTTL));
//			cout<<iTTL<<flush; //输出当前序号cout<<4-ping_ttl;//填充 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);
//				cout<<"iReadDataLen:"<<iReadDataLen<<"\n";
//				cout<<"IcmpRecvBuf:"<<IcmpRecvBuf<<"\n";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;flag=1;//输出 IP 地址cout<<'\t'<<inet_ntoa(DecodeResult.dwIPaddr)<<"\n";
//						IpAddressStatus[IpAddress] = "在线";
//						IpAddressStatus1.push_back(make_pair(IpAddress,"在线"));break;}
//					else{
//						IpAddressStatus1.push_back(make_pair(IpAddress,"在线"));
//						break;
//					}} else if(WSAGetLastError()==WSAETIMEDOUT) { //接收超时,输出*号cout<<" *"<<'\t'<<"Request timed out."<<endl;
//					IpAddressStatus1.push_back(make_pair(IpAddress,"不可达"));iTTL++;if(iTTL>6)break;break;} else {cout<<"错误\n";break;}}
//			iTTL++; //递增 TTL 值}if(flag) {IpAddressStatus1.push_back(make_pair(IpAddress,"在线"));} else {IpAddressStatus1.push_back(make_pair(IpAddress,"不可达"));}
//		if(iMaxHot <= 0) {
//			//cout<<"地址不可达\n";IpAddressStatus[IpAddress] = "不可达";
//			IpAddressStatus1.push_back(make_pair(IpAddress,"不可达"));
//		}}//迭代cout<<"----------------------------------------------------------\n";cout<<"ip 地址范围 "<<ip1<<"."<<ip2<<"."<<ip3<<"."<<ip4<<"-"<<ip5<<" 的 ping 情况:\n";for(vector< pair<string,string> > ::iterator it = IpAddressStatus1.begin(); it != IpAddressStatus1.end(); it++)cout<<(*it).first<<":\t\t"<<(*it).second<<"\n";//输出key 和value值
}

5.2 Tracert代码

/* 程序名称:路由追踪(Tracert)程序
实现原理:Tracert 程序关键是对 IP 头部生存时间(time to live)TTL 字段的使用,程序实现时是向目
地主机发送一个 ICMP 回显请求消息,初始时 TTL 等于 1,这样当该数据报抵达途中的第一个路由器
时,TTL 的值就被减为 0,导致发生超时错误,因此该路由生成一份 ICMP 超时差错报文返回给源主
机。随后,主机将数据报的 TTL 值递增 1,以便 IP 报能传送到下一个路由器,并由下一个路由器生成
ICMP 超时差错报文返回给源主机。不断重复这个过程,直到数据报达到最终的目地主机,此时目地
主机将返回 ICMP 回显应答消息。这样,源主机只需对返回的每一份 ICMP 报文进行解析处理,就可
以掌握数据报从源主机到达目地主机途中所经过的路由信息。
*/
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
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,BYTEICMP_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;
}
int main() {
//初始化 Windows sockets 网络环境WSADATA wsa;WSAStartup(MAKEWORD(2,2),&wsa);char IpAddress[255];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 0;}}cout<<"Tracing route to "<<IpAddress<<" with a maximum of 30 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; //回显应答超时时间const int DEF_MAX_HOP=30; //最大跳站数
//填充 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(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 与 Ping 程序设计与实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机