计算机网络

您所在的位置:网站首页 计算机网络编程实验报告 计算机网络

计算机网络

2023-09-09 23:43| 来源: 网络整理| 查看: 265

计算机网络——Tracert与Ping程序设计与实现 一、实验目的二、总体设计1. 基本原理2. 设计步骤 三、详细设计1. 程序流程图2. 实验代码 四、实验结果

一、实验目的

了解Tracert程序的实现原理,并调试通过。

二、总体设计 1. 基本原理

tracert(跟踪路由)是路由跟踪实用程序,用于确定IP数据包访问目标所采取的路径。tracert 有一个固定的时间等待响应(ICMP TTL到期消息)。如果这个时间过了,它将打印出一系列的*号表明:在这个路径上,这个设备不能在给定的时间内发出ICMP TTL到期消息的响应。然后,Tracert给TTL记数器加1,继续进行。

2. 设计步骤

(1)加载套接字,创建套接字库; 使用Socket的程序在使用Socket之前必须调用WSAStartup函数,以后应用程序就可以调用所请求的Socket库中的其他Socket函数了。 (2)用inet_addr()将输入的点分十进制的IP地址转换为无符号长整型数,转换不成功时,按域名解析得到IP地址; gethostbyname()是查找主机名最基本的函数,如果调用成功,就返回一个指向hosten结构的指针,该结构中含有对应于给定主机名的主机名字和地址信息,用来承接域名解析的结构。 (3)设置发送接收超时时间,即请求超时,设置接收、发送超时的套接字; (4)构造ICMP回显请求消息,并以TTL递增顺序发送报文,填充ICMP报文中每次发送时不变的字段,构造ICMP头; (5)设置IP报头的TTL字段,填充ICMP报文中每次发送变化的字段,记录序列号和当前时间; (6)指定对方信息,发送TCP回显请求信息; sendto()函数利用数据表的方式进行数据传输,指定哪个socket发送给对方 (7)接收ICMP差错报文并进行解析:如果有数据到达,解析数据包,如果到达目的地址,输出IP地址;如果没有数据到达,输出接收超时,递增TTL值,TTL增为最大时,若还没有到达目的地址,退出循环,输出目的地址不在线; recvform()利用数据报方式进行数据传输,当recvfrom()返回时,(sockaddr*)&from包含实际存入from中的数据字节数。Recvfrom函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。 (8)重复(2)-(7),实现查找一个范围内的IP地址。

三、详细设计 1. 程序流程图

在这里插入图片描述

2. 实验代码 #include #include #include #include #include using namespace std; #pragma comment(lib, "Ws2_32.lib") const int ipAddressSize = 14; //int count11=0; //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 位片偏移: 标志:MF 1是否还有分配 0 没有分片了 // DF 0 可以分片 // 片偏移:分片后的相对于原来的偏移 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 报头,一共八个字节,前四个字节为:类型(1字节)、代码(1字节)和检验和(2字节)。后四个字节取决于类型 typedef struct { BYTE type; //8 位类型字段:标识ICMP的作用 BYTE code; //8 位代码字段 USHORT cksum; //16 位校验和 USHORT id; //16 位标识符 USHORT seq; //16 位序列号 } ICMP_HEADER; //报文解码结构 //接收到的数据缓存是字符数组char bufRev[],因此需要通过特定的解析(也就是拆成一段一段的)获取想要的信息 //把信息封装到结构体中,就比较方便的得到序列号、往返时间和目的IP了。 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); } //对数据包进行解码 // 1)接收到的Buf 2)接收到的数据长度 3)解析结果封装到Decode 4)ICMP回显类型 5)TIMEOUT时间 BOOL DecodeIcmpResponse2(char * pBuf, int iPacketSize, DECODE_RESULT &DecodeResult, BYTE ICMP_ECHO_REPLY, BYTE ICMP_TIMEOUT) { //查找数据报大小合法性 //pBuf的首地址,就是IP报的首地址 IP_HEADER *pIpHdr = (IP_HEADER*)pBuf; int iIpHdrLen = pIpHdr->hdr_len * 4; if(iPacketSize type == ICMP_ECHO_REPLY) // ICMP 回显应答报文 { usID = pIcmpHdr->id;//报文 ID usSquNo = pIcmpHdr->seq;//报文序列号 } else if (pIcmpHdr->type == ICMP_TIMEOUT)//ICMP超时差错报文 { // 如果是TIMEOUT ,那么在ICMP数据包中,会夹带一个IP报(荷载IP) 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_un.S_addr = pIpHdr->sourceIP; DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime; //处理正确收到的 ICMP 数据包 if (pIcmpHdr->type == ICMP_ECHO_REPLY || pIcmpHdr->type == ICMP_TIMEOUT) { // 输出往返时间信息 if (DecodeResult.dwRoundTripTime) cout // 执行,单线程执行,实现后改成多线程 //得到IP地址 u_long ulDestIP = inet_addr(nextIpAddress);//inet_addr()的功能是将一个点分十进制的IP转换成一个无符号长整型数 //转换不成功时按域名解析 if (ulDestIP == INADDR_NONE) { //gethostbyname()是查找主机名最基本的函数 //如果调用成功,就返回一个指向hosten结构的指针 //该结构中含有对应于给定主机名的主机名字和地址信息,用来承接域名解析的结构 hostent * pHostent = gethostbyname(nextIpAddress); if (pHostent)//调用成功 { //得到IP地址 //套了两层,IP和ICMP,ICMP是套在IP里面的 //h_addr返回主机IP地址 //in_addr返回报文的IP地址 //sin_addr.s_addr指向IP地址 ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr; } else { cout //接收数据 //recvfrom()利用数据报方式进行数据传输 //当recvfrom()返回时,(sockaddr*)&from包含实际存入from中的数据字节数。 //Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。 iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, & iFromLen); if (iReadDataLen != SOCKET_ERROR) // 有数据到达 { //解析数据包 // 1)接收到的Buf 2)接收到的数据长度 3)解析结果封装到Decode 4)ICMP回显类型 5)TIMEOUT时间 if (DecodeIcmpResponse2(IcmpRecvBuf, iReadDataLen, DecodeResult, ICMP_ECHO_REPLY, DEF_ICMP_TIMEOUT)) { // 到达目的地,退出循环 //返回报文的IP地址等于输入的IP地址 if (DecodeResult.dwIPaddr.S_un.S_addr == destSockAddr.sin_addr.S_un.S_addr) { bReachDestHost = true; // 输出 IP 地址 //inet_ntoa()功能是将网络地址转换成“.”点隔的字符串格式。 cout break; } } iTTL++;//递增TTL值 } cout if (nowIp[i] == '.') { z[idxIp][idxj] = '\0'; idxIp++; idxj = 0; continue; } z[idxIp][idxj++] = nowIp[i]; } z[idxIp][idxj] = '\0'; //for (int i = 0; i < 4; i++) //{ // puts(z[i]); //} //cout = 0; i--) { if (strcmp("254", z[i]) == 0) { strcpy(z[i], "1"); // 这里让ip 1-254 } else { int x; x = atoi(z[i]) + 1; //stringstream ss; //ss


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3