用C#实现IP的路由跟踪- -

ICMP协议可以被用来跟踪显示一个从主机发送出去的IP包从发送源到达目标主机所经过的路由IP,下面就利用C#语言,在.Net环境中,使用ICMP协议来实现一个具有IP路由跟踪功能的应用. ICMP(Internet Control Message Protocal)协议 为了编写本实例,了解ICMP协议是非常必要的。 在互联网络系统中,主机与主机之间的寻址使用IP协议。源地址通过IP地址来同目标进行通信,但有的时候,目标主机也会同源地址进行通信,比如,向源主机报告寻址处理的错误等。对于主机探测、路由维护、路由选择以及流量控制等网络功能来说,通常会用到ICMP协议。ICMP建立在IP协议的基础上,好像它是更高级的协议,而实际上ICMP是IP的一个主要部分,在每一个IP模块它都必须被执行。 当出现下面的某个情况时(仅仅是部分情况),ICMP会有信息被反馈: 寻址无法到达目标 网关对于到来的寻址数据包没有了缓冲能力 网关遇到生存周期为0的报文 事实上,IP协议本身并没有被设计成绝对的可靠,上面的一些控制信息被用来反馈通信环境中出现的问题,但这并不能保证IP包的可靠传输。更高级的协议为了保证绝对可靠传输,通常都加入了新的传输机制。ICMP设计的目的是使网络用户可以检测网络连接的状况以及保证连接的准确性。 实现原理 利用ICMP协议实现路由跟踪的原理如下。 对于ICMP的超时报文,如果网关发现报文的生存周期(TTL)为0,则该数据报必须被抛弃,且网关会向源主机发送超时通知。通过构造ICMP包的IP包头中的TTL字段可以实现这样的发送包。 具体过程是这样: 假设IP将要到达的目标地址需要经过K个路由器(K > 1),则进行下面工作。 1) 构造一个ICMP包,TTL = 1,发送后得到第一个数据路由器的超时报文。 2) 构造第S(S < K)个ICMP包,TTL = S,则可以得到第S个数据路由器的超时报文。 具体实现 添加名称空间 为了使用C#的某些网络函数,需要添加下面的名称空间。 using System.Net; using System.Net.Sockets; 定义ICMP结构 在构造ICMP包时,需要首先定义几个ICMP包用到的结构。 struct ICMPConstants { public const int ICMP_ECHOREPLY= 0; public const int ICMP_TIMEEXCEEDED= 11; public const int ICMP_ECHOREQ= 8; public const int MAX_TTL= 256; } //ICMP 包头 struct ICMP { public byte type; public byte code; public ushort checksum; public ushort id; public ushort seq; // 次序 } // ICMP 应答请求 struct REQUEST { public ICMP m_icmp; public byte []m_data; } 函数CreatePacket 函数CreatePacket用来构造一个符合要求的ICMP数据包,具体实现如下: public static byte[] CreatePacket( REQUEST req ) { Byte[] ByteSend= new Byte[PACKET_SIZE+ 8]; ByteSend[0]= req.m_icmp.type; ByteSend[1]= req.m_icmp.code; Array.Copy(BitConverter.GetBytes(req.m_icmp.checksum), 0, ByteSend, 2, 2); Array.Copy(BitConverter.GetBytes(req.m_icmp.id), 0, ByteSend, 4, 2); Array.Copy(BitConverter.GetBytes(req.m_icmp.seq), 0, ByteSend, 6, 2); for(int i=0; i< req.m_data.Length; i++) ByteSend[i+8]= req.m_data[i]; //计算校验和 int iCheckSum = 0; for (int i= 0; i < ByteSend.Length; i+= 2) iCheckSum += Convert.ToInt32( BitConverter.ToUInt16(ByteSend,i)); iCheckSum = (iCheckSum >> 16) + (iCheckSum & 0xffff); iCheckSum += (iCheckSum >> 16); Array.Copy(BitConverter.GetBytes((ushort)~iCheckSum), 0, ByteSend, 2, 2); return ByteSend; } 主函数 以下是主函数Main()的具体实现。 static void Main(string[] args) { try { if(args.Length== 0) { Console.WriteLine("使用方法: tracedemo "); return; } Socket s= new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); //目标 IPEndPoint ipdest= new IPEndPoint(Dns.Resolve(args[0]).AddressList[0],80); //源 IPEndPoint ipsrc= new IPEndPoint(Dns.GetHostByName(Dns.GetHostName()).AddressList[0],80); EndPoint epsrc= (EndPoint)ipsrc; ICMP ip= new ICMP(); ip.type = ICMPConstants.ICMP_ECHOREQ; ip.code = 0; ip.checksum = 0; ip.id = (ushort)DateTime.Now.Millisecond; ip.seq = 0; REQUEST req= new REQUEST(); req.m_icmp= ip; req.m_data = new Byte[PACKET_SIZE]; //初始化数据 for (int i = 0; i < req.m_data.Length; i++) { req.m_data[i] = (byte)'S'; } Byte[] ByteSend= CreatePacket(req); //发送请求 for(int ittl=1; ittl<= ICMPConstants.MAX_TTL; ittl++) { Byte[] ByteRecv = new Byte[256]; //Socket options to set TTL and Timeouts s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, ittl); s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout,10000); s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout,10000); //取得当前时间 DateTime dt= DateTime.Now; int iRet= s.SendTo(ByteSend, ByteSend.Length, SocketFlags.None, ipdest); //check for Win32 SOCKET_ERROR if(iRet== -1) Console.WriteLine("error sending data"); //接收 iRet= s.ReceiveFrom(ByteRecv, ByteRecv.Length, SocketFlags.None, ref epsrc); TimeSpan ts= DateTime.Now - dt; //检验申请返回的数据 if(iRet== -1) Console.WriteLine("error getting data"); Console.WriteLine("TTL= {0,-5} IP= {1,-20} Time= {2,3}ms",ittl,((IPEndPoint)epsrc).Address,ts.Milliseconds); if((iRet == PACKET_SIZE+ 8 +20)&& (BitConverter.ToInt16(ByteRecv,24) == BitConverter.ToInt16(ByteSend,4))&& (ByteRecv[20] == ICMPConstants.ICMP_ECHOREPLY)) break; //超时 if(ByteRecv[20] != ICMPConstants.ICMP_TIMEEXCEEDED) { Console.WriteLine("unexpected reply, quitting..."); break; } } } catch(SocketException e) { Console.WriteLine(e.Message); } catch(Exception e) { Console.WriteLine(e.Message); } } 以上就是本例的完整代码实现,代码在Visual Stuido.Net 2002 C#编译环境中,Windows2000 Profession操作系统下调试通过。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stoneson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值