https://blog.csdn.net/u013271921/article/details/45488173

#include<winsock2.h>
//#include<iphlpapi.h>
#include <Ws2tcpip.h>
#include<iostream>
#include<conio.h>
//#include<bits/stdc++.h>
using namespace std; const BYTE ICMP_ECHO_REQUEST = ;//请求回显
const BYTE ICMP_ECHO_REPLY = ;//回显应答
const BYTE ICMP_TIMEOUT = ;//传输超时
const int DEF_ICMP_DAtA_SIZE = ;//ICMP报文默认数据字段长度
const int MAX_ICMP_PACKET_SIZE = ;//ICMP报文最大长度(包含报头)
const DWORD DEF_ICMP_TIMEOUT = ;//回显应答超时时间,单位ms
const int DEF_MAX_HOP = ;//最大跳站数 typedef struct {
BYTE hdr_len :;//4位头部长度
BYTE version :;//长度版本号
BYTE tos;//8位服务类型
USHORT total_len;//16位总长度
USHORT identifier;//16位标识符
USHORT frag_and_flags;//3位标志+13位片偏移
BYTE ttl;//8位生存时间
BYTE protocol;//8位上层协议号
USHORT checksum;//16位校验和
ULONG sourceIP;//32位源IP地址
ULONG destIP;//32位目的IP地址
} IP_HEADER; 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;
// 返回报文的IP地址(输出参数)
in_addr dwIpAddr;
} DECODE_RESULT; //计算网际校验和函数
USHORT checksum(USHORT* pBuf, int iSize) {
ULONG cksum = ;
while(iSize > ) {
cksum += *pBuf++;
iSize -= sizeof(USHORT);
}
// 如果 iSize 为正,即为奇数个字节
if(iSize) {
// 则在末尾补上一个字节,使之有偶数个字节
cksum += *(UCHAR*)pBuf;
}
cksum = (cksum>>) +(cksum & 0xffff);
cksum += (cksum >> );
return (USHORT)(~cksum);
} bool DecodeIcmpResponse(char *pBuf, int iPacketSize, DECODE_RESULT& DecodeResult) {
// 计算IP头部长度
int iIpHdrLen = ((IP_HEADER*)pBuf)->hdr_len * ;
// 根据ICMP报文类型提取ID字段和序列号字段
ICMP_HEADER *pIcmpHdr = (ICMP_HEADER *)(pBuf + iIpHdrLen);
USHORT usID;
USHORT usSquNo;
// ICMP回显应答报文
if(pIcmpHdr->type == ICMP_ECHO_REPLY) {
// 报文ID
usID = pIcmpHdr->id;
// 序列号
usSquNo = pIcmpHdr->seq;
}
// ICMP超时差错报文
else if(pIcmpHdr->type == ICMP_TIMEOUT) {
// 载荷中的IP头
char *pInnerIpHdr = pBuf + iIpHdrLen + sizeof(ICMP_HEADER);
// 载荷中的IP头长
int iInnerIpHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * ;
// 载荷中的ICMP头
ICMP_HEADER *pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr + iInnerIpHdrLen);
// 报文ID
usID = pInnerIcmpHdr->id;
// 序列号
usSquNo = pInnerIcmpHdr->seq;
}
else {
return false;
}
// printf("usID: %d == currentID: %d\n", usID, (USHORT)GetCurrentProcessId());
// printf("usSquNo: %d == DecodeResult: %d\n", usSquNo, DecodeResult.usSeqNo);
// 检查ID和序列号以确定收到期待数据报
if(usID != (USHORT)GetCurrentProcessId() || usSquNo != DecodeResult.usSeqNo) {
return false;
}
// 记录IP地址并计算往返时间
DecodeResult.dwIpAddr.s_addr = ((IP_HEADER*)pBuf)->sourceIP;
DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime;
// 打印往返时间信息
if(DecodeResult.dwRoundTripTime) {
cout << " " << DecodeResult.dwRoundTripTime << "ms" << flush;
}
else {
cout << " " << "<1" << "ms" << flush;
}
return true;
} WSADATA wsa;
char IcmpSendBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//发送缓冲区
char IcmpRecvBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//接收缓冲区
char argv[][];
int main() {
WSAStartup(MAKEWORD(, ), &wsa);
//将命令行参数转换成IP地址
scanf("%s", argv[]);
ULONG ulDestIP = inet_addr(argv[]);
if(ulDestIP == INADDR_NONE) {
//转换不成功时按域名解析
hostent *pHostent = gethostbyname(argv[]);
if(pHostent) ulDestIP = (*(in_addr*) pHostent->h_addr).s_addr;
else {
WSACleanup();
return -;
}
}
//填充目的端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, , WSA_FLAG_OVERLAPPED);
int iTimeout = ;
//设置超时机制
setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*) &iTimeout, sizeof(iTimeout));
setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*) &iTimeout, sizeof(iTimeout));
ICMP_HEADER *pIcmpHeader = (ICMP_HEADER*) IcmpSendBuf;
pIcmpHeader->type = ICMP_ECHO_REQUEST;//类型为请求回显
pIcmpHeader->code = ;//代码字段为0
pIcmpHeader->id=(USHORT)GetCurrentProcessId();//ID字段为当前进程号
memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DAtA_SIZE);//数据字段
USHORT usSeqNo = ;//ICMP报文序列号
int iTTL = ;//TTL初始值为1
bool bReachDestHost = false;//循环退出标志
int iMaxHop = DEF_MAX_HOP;//循环的最大次数
DECODE_RESULT DecodeResult;//传递给报文解码函数的结构化参数
while(!bReachDestHost && iMaxHop--) {
//设置IP报头的TTL字段
setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));
cout << iTTL << flush;//输出当前序号
//填充ICMP报文中每次发送时需要变化的字段
((ICMP_HEADER*)IcmpSendBuf)->cksum = ;//校验和先置为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();//当前时间
//发送ICMP回显请求消息
sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), , (sockaddr*)&destSockAddr, sizeof(destSockAddr));
// 接收ICMP报文
// 对端Socket地址
sockaddr_in from;
// 地址结构大小
int iFromLen = sizeof(from);
// 接收数据长度
int iReadDataLen;
// 循环接收直到收到所需数据或超时
while() {
iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, , (sockaddr*)&from, &iFromLen);
// 有数据到达
if(iReadDataLen != SOCKET_ERROR) {
// 对数据包进行解析
if(DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, DecodeResult)) {
// 到达目的地,退出循环
if(DecodeResult.dwIpAddr.s_addr == destSockAddr.sin_addr.s_addr) {
bReachDestHost = true;
// printf("reach\n");
}
// 打印IP地址
cout << '\t' << inet_ntoa(DecodeResult.dwIpAddr) << endl;
break;
}
}
// 接收超时,打印*号
else if(WSAGetLastError() == WSAETIMEDOUT) {
cout << " " << '*' << '\t' << "Request timed out." << endl;
break;
}
}
iTTL++;
}
cout << "reach: " << bReachDestHost << endl;
return ;
} //cqupt 202.202.32.35
//csdn 39.96.126.153
//cnblogs 101.37.113.127

基于icmp的tracert路由追踪程序的更多相关文章

  1. ping命令基于ICMP协议的返回信息分析

    Ping是潜水艇人员的专用术语,表示回应的声纳脉冲,在网络中 Ping 是一个十分好用的 TCP/IP 工具.它主要的功能是用来检测网络的连通情况和分析网络速度.可以利用 PING 命令检查网络连通状 ...

  2. 建立tracert路由列表的方法

    建立tracert路由列表的方法:电脑屏幕左下方 选择开始选项运行 输入 CMD在DOS命令行下输入:tracert (你的网站域名)   运行结果中如出现了“*     *     *    req ...

  3. 第二百六十四节,Tornado框架-基于正则的动态路由映射分页数据获取计算

    Tornado框架-基于正则的动态路由映射分页数据获取计算 分页基本显示数据 第一步.设置正则路由映射配置,(r"/index/(?P<page>\d*)", inde ...

  4. 第二百六十三节,Tornado框架-基于正则的动态路由映射

    Tornado框架-基于正则的动态路由映射 1.在路由映射条件里用正则匹配访问路径后缀2.给每一个正则匹配规则(?P<设置名称>)设置一个名称,3.在逻辑处理的get()方法或post() ...

  5. tracert路由跟踪命令分析判断

    可能有的会使用路由跟踪命令 ,可是却看不太明确显示出来的结果.结合我的来说明一下. (1)tracert命令介绍 tracert是路由跟踪命令,通过该命令的返回结果,能够获得本地到达目标主机所经过的网 ...

  6. tracert路由跟踪工具使用方法

    1. 路由跟踪在线Tracert工具说明 Tracert(跟踪路由)是路由跟踪实用程序,用于确定 IP 数据报访问目标所采取的路径.Tracert 命令用 IP 生存时间 (TTL) 字段和 ICMP ...

  7. 基于TCAM 的高速路由查找

    摘要 随着路由器接口速率的提高,传统的软件路由查找机制已经不能满足要求.目前常见的硬件解决方案是采用TCAM实现关键词 TCAM,路由查找,最长前缀匹配. 1.引言 路由器转发IP 分组时,转发引擎需 ...

  8. tracert 路由跟踪程序

    C:\Users\Administrator>tracert 10.0.0.1 通过最多 30 个跃点跟踪到 10.0.0.1 的路由 1 <1 毫秒 1 ms 3 ms 192.168. ...

  9. 基于原生PHP的路由分配实现

    对于由原生PHP写成的独立PHP框架,利用单一入口文件实现路径的访问.这时我们会遇到的首要问题是:文件的相互包含,其次就是路由分配.当我们不利用成熟的PHP框架进行web开发时,我们就会发现上述两个问 ...

随机推荐

  1. linux--nginx学习

    nginx 1.nginx安装编译 1.yum install nginx(自动解决依赖) 2.源代码编译安装(优秀,自由选择软件版本,自定义第三方功能比如开启https) 3.rpm手动安装(垃圾) ...

  2. Mac 多版本 JDK 管理

    Mac 多版本 JDK 管理 1. 准备 ZSH Homebrew Oracle JDK 1.8 安装包(Homebrew 官方源和第三方源不再提供老版本的 Oracle JDK) 2. 安装 JDK ...

  3. 从servlet向jsp中传数据用Java接收js调用

    servlet: response.sendRedirect("showMessage.jsp?ValueA=1"); jsp: var a=<%=request.getPa ...

  4. 二维数组填充datagridview

    public void TwoDArrayShowINDatagridview(string[,] arr) { DataTable dt = new DataTable(); for (int co ...

  5. md基础语法总结

    md即为Markdown,Markdown的目标是实现「易读易写」,可读性,无论如何,都是最重要的.其实md的底层最终还是将我们写的语法转化为html标签了 --------------------- ...

  6. 使用VS2017创建EF框架实例

    本文例子中使用环境:vs2017,sql server 2008 一,创建EF项目 1,解决方案添加.Net Framework类库项目,在新建的项目下新建项:data->ADO.NET实体数据 ...

  7. 马走日的解法(dfs)

    马在中国象棋以日字形规则移动. 请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. Input 第一行为整数T ...

  8. vue小例子-01

    1.在components下建一个 2.代码如下: <template> <!--1.业务是开始有一组数据,序号,姓名,性别,年龄,操作(删除)     2.有三个输入框输入姓名,性 ...

  9. [Codechef TASTR] Level of Difference - 后缀数组,容斥原理

    [Codechef TASTR] Level of Difference Description 给定两个字符串,求恰好在一个字符串中出现过的本质不同的子串数量. Solution 设 \(U(S)\ ...

  10. java基础之I/O操作

    字节流 直接上代码: import java.io.*; class Test{ public static void main(String[] args){ FileInputStream inp ...