C语言tracert源码,TraceRoute(tracert)源码(基于原始套接字实现)
//TraceRoute.h
#ifndef _ITRACERT_H_
#define _ITRACERT_H_
#pragma pack(1)
//IP数据报头
typedefstruct
{
unsignedcharhdr_len :4;// length of the header
unsignedcharversion :4;// version of IP
unsignedchartos;// type of service
unsignedshorttotal_len;// total length of the packet
unsignedshortidentifier;// unique identifier
unsignedshortfrag_and_flags;// flags
unsignedcharttl;// time to live
unsignedcharprotocol;// protocol (TCP, UDP etc)
unsignedshortchecksum;// IP checksum
unsignedlongsourceIP;// source IP address
unsignedlongdestIP;// destination IP address
} IP_HEADER;
//ICMP数据报头
typedefstruct
{
BYTEtype;//8位类型
BYTEcode;//8位代码
USHORTcksum;//16位校验和
USHORTid;//16位标识符
USHORTseq;//16位序列号
} ICMP_HEADER;
//解码结果
typedefstruct
{
USHORTusSeqNo;//包序列号
DWORDdwRoundTripTime;//往返时间
in_addr dwIPaddr;//对端IP地址
} DECODE_RESULT;
#pragma pack()
//ICMP类型字段
constBYTEICMP_ECHO_REQUEST = 8;//请求回显
constBYTEICMP_ECHO_REPLY = 0;//回显应答
constBYTEICMP_TIMEOUT = 11;//传输超时
constDWORDDEF_ICMP_TIMEOUT = 3000;//默认超时时间,单位ms
constintDEF_ICMP_DATA_SIZE = 32;//默认ICMP数据部分长度
constintMAX_ICMP_PACKET_SIZE = 1024;//最大ICMP数据报的大小
constintDEF_MAX_HOP = 30;//最大跳站数
USHORTGenerateChecksum(USHORT* pBuf,intiSize);
BOOLDecodeIcmpResponse(char* pBuf,intiPacketSize, DECODE_RESULT& stDecodeResult);
#endif // _ITRACERT_H_
//TraceRoute.cpp
/*----------------------------------------------------------
功能说明:该程序简单实现了Windows操作系统的tracert命令功能,
可以输出IP报文从本机出发到达目的主机所经过的路由信息。
-----------------------------------------------------------*/
#include
#include
#include
#include
#include
#include "TraceRoute.h"
#pragma comment(lib,"ws2_32")
usingnamespacestd;
intmain(intargc,char* argv[])
{
//检查命令行参数
if(argc != 2)
{
cerr <
return-1;
}
//初始化winsock2环境
WSADATA wsa;
if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
cerr <
<
return-1;
}
//将命令行参数转换为IP地址
u_long ulDestIP = inet_addr(argv[1]);
if(ulDestIP == INADDR_NONE)
{
//转换不成功时按域名解析
hostent* pHostent = gethostbyname(argv[1]);
if(pHostent)
{
ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
//输出屏幕信息
cout <
<
<
}
else//解析主机名失败
{
cerr <
<
WSACleanup();
return-1;
}
}
else
{
//输出屏幕信息
cout <
<
}
//填充目的Socket地址
sockaddr_in destSockAddr;
ZeroMemory(&destSockAddr,sizeof(sockaddr_in));
destSockAddr.sin_family = AF_INET;
destSockAddr.sin_addr.s_addr = ulDestIP;
//使用ICMP协议创建Raw Socket
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if(sockRaw == INVALID_SOCKET)
{
cerr <
<
WSACleanup();
return-1;
}
//设置端口属性
intiTimeout = DEF_ICMP_TIMEOUT;
if(setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeout,sizeof(iTimeout)) == SOCKET_ERROR)
{
cerr <
<
closesocket(sockRaw);
WSACleanup();
return-1;
}
//创建ICMP包发送缓冲区和接收缓冲区
charIcmpSendBuf[sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE];
memset(IcmpSendBuf, 0,sizeof(IcmpSendBuf));
charIcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
memset(IcmpRecvBuf, 0,sizeof(IcmpRecvBuf));
//填充待发送的ICMP包
ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;
pIcmpHeader->type = ICMP_ECHO_REQUEST;
pIcmpHeader->code = 0;
pIcmpHeader->id = (USHORT)GetCurrentProcessId();
memset(IcmpSendBuf+sizeof(ICMP_HEADER),'E', DEF_ICMP_DATA_SIZE);
//开始探测路由
DECODE_RESULT stDecodeResult;
BOOLbReachDestHost = FALSE;
USHORTusSeqNo = 0;
intiTTL = 1;
intiMaxHop = DEF_MAX_HOP;
while(!bReachDestHost && iMaxHop--)
{
//设置IP数据报头的ttl字段
setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL,sizeof(iTTL));
//输出当前跳站数作为路由信息序号
cout <
//填充ICMP数据报剩余字段
((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;
((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);
((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 <
<
closesocket(sockRaw);
WSACleanup();
return0;
}
//接收ICMP的EchoReply数据报
//因为收到的可能并非程序所期待的数据报,所以需要循环接收直到收到所要数据或超时
sockaddr_in from;
intiFromLen =sizeof(from);
intiReadDataLen;
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 <
break;
}
}
elseif(WSAGetLastError() == WSAETIMEDOUT)//接收超时,打印星号
{
cout <
break;
}
else
{
cerr <
<
closesocket(sockRaw);
WSACleanup();
return-1;
}
}
//TTL值加1
iTTL++;
}
//输出屏幕信息
cout <
closesocket(sockRaw);
WSACleanup();
return0;
}
//产生网际校验和
USHORTGenerateChecksum(USHORT* pBuf,intiSize)
{
unsignedlongcksum = 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);
}
//解码得到的数据报
BOOLDecodeIcmpResponse(char* pBuf,intiPacketSize, DECODE_RESULT& stDecodeResult)
{
//检查数据报大小的合法性
IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
intiIpHdrLen = pIpHdr->hdr_len * 4;
if(iPacketSize
returnFALSE;
//按照ICMP包类型检查id字段和序列号以确定是否是程序应接收的Icmp包
ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf+iIpHdrLen);
USHORTusID, usSquNo;
if(pIcmpHdr->type == ICMP_ECHO_REPLY)
{
usID = pIcmpHdr->id;
usSquNo = pIcmpHdr->seq;
}
elseif(pIcmpHdr->type == ICMP_TIMEOUT)
{
char* pInnerIpHdr = pBuf+iIpHdrLen+sizeof(ICMP_HEADER);//载荷中的IP头
intiInnerIPHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4;//载荷中的IP头长
ICMP_HEADER* pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr+iInnerIPHdrLen);//载荷中的ICMP头
usID = pInnerIcmpHdr->id;
usSquNo = pInnerIcmpHdr->seq;
}
else
returnFALSE;
if(usID != (USHORT)GetCurrentProcessId() || usSquNo !=stDecodeResult.usSeqNo)
returnFALSE;
//处理正确收到的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 <
else
cout <
returnTRUE;
}
returnFALSE;
}
C语言tracert源码,TraceRoute(tracert)源码(基于原始套接字实现)相关推荐
- C语言socket accept()函数(提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符)
文章目录 名称 使用格式 功能参数描述 参数 sockfd addr addrlen 返回值 示例 man 2 文档中的accept解释 错误处理 名称 accept() 接收一个套接字中已建立的连接 ...
- 从入门到入土:基于C语言采用UDP协议实现远程控制|详细说明|利用流套接字实现一个简单的远程控制系统|代码展示
此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...
- 从入门到入土:基于C语言采用TCP协议实现远程控制|详细说明|利用流套接字实现一个简单的远程控制系统
此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...
- C语言 socket shutdown()函数(将与 sockfd 关联的套接字上的全双工连接全部或部分关闭)
man 2 文档 [root@ubuntu /arnold_test/20220324_hikflow_demo__socket_server_test]102# man -f shutdown sh ...
- af_packet_C语言中利用AF_PACKET 原始套接字发送一个任意以太网帧 (一)
目标 利用AF_PACKET 套接字发送一个任意的以太网帧 背景 以太网是一个链路层协议.大多数网络程序员关注网络栈的传输层及以上,所以不需要直接处理以太网帧,但是某些场景下关注传输层以下也是有必要的 ...
- tcp/ip 协议栈Linux内核源码分析15 udp套接字接收流程二
内核版本:3.4.39 上篇我们分析了UDP套接字如何接收数据的流程,最终它是在内核套接字的接收队列里取出报文,剩下的问题就是谁会去写入这个队列,当然,这部分工作由内核来完成,本篇剩下的文章主要分析内 ...
- UNIX环境编程(c语言)--套接字--基本TCP套接字编程
目录 准备知识 字节序 字节序转换函数 字节操纵函数 地址转换函数 地址结构 基本TCP套接字编程 概要 socket函数 bind函数 listen函数 accept函数 connect函数 通信函 ...
- 同一主机的多个子进程使用同一个套接字_如何在Go语言中使用Websockets:最佳工具与行动指南...
如今,在不刷新页面的情况下发送消息并获得即时响应在我们看来是理所当然的事情.但是曾几何时,启用实时功能对开发人员来说是一个真正的挑战.开发社区在HTTP长轮询(http long polling)和A ...
- python在abaqus中的应用代码下载_Python-(source-code)-in--Abaqus Python语言在Abaqus中的应用(源码) - 下载 - 搜珍网...
Python语言在Abaqus中的应用 (源码)/ Python语言在Abaqus中的应用 (源码)/Python语言在Abaqus中的应用 (源码)/ Python语言在Abaqus中的应用 (源码 ...
最新文章
- 周怎么换算成月_您每天需要多少能量,怎么知道自己摄入多少能量
- ICLR 2020论文投稿2600篇,GNN、BERT、Transformer领跑热门研究方向
- alter添加多个字段_Oracle中 create , alter , delete ,等关键字的使用
- POJ 2356 Find a multiple (抽屉原理)
- 牵引力教育学校分析UI设计师的薪酬水平
- 【LaTeX】E喵的LaTeX新手入门教程(2)基础排版
- Activiti工作流从入门到入土:入门实例
- 分布式事务:RocketMQ实现分布式事务原理
- 想做硬件开发的人员必看
- (计算机组成原理)第五章中央处理器-第二节:指令执行过程(取指周期、间址周期、执行周期和中断周期)
- 【面试题】HashMap 面试 21 问
- layui 弹出框改变按钮颜色样式 自定义皮肤
- Jmail组件在C#中发送邮件代码:
- C#对文件的操作(创建、获取文件数量、删除)(读、写文件)
- 自定义video的controls
- js网页进度条等待特效
- MySQL原理与实践(六):自增主键的使用
- 计算机电源电压的调整,电压调整电路、电压调整方法及其计算机系统
- CentOS7如何设置屏幕不休眠
- 计算机缓存Cache以及Cache Line详解