利用Visual C#实现ICMP网络协议

来源:互联网 发布:国泰安数据库使用视频 编辑:程序博客网 时间:2024/05/17 22:24

本文详细介绍利用Visual C#实现ICMP网络协议

<script type="text/javascript"><!--google_ad_client = "pub-5977682010997732";/* 336x280, 创建于 08-7-26 */google_ad_slot = "8221152630";google_ad_width = 336;google_ad_height = 280;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script><script> window.google_render_ad(); </script><iframe name="google_ads_frame" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-5977682010997732&amp;dt=1225987224093&amp;lmt=1221220895&amp;output=html&amp;slotname=8221152630&amp;correlator=1225987224092&amp;url=http%3A%2F%2Ftech.ddvip.com%2F2006-08%2F11545024636787.html&amp;ea=0&amp;ref=http%3A%2F%2Fwww.google.cn%2Fsearch%3Fq%3Dc%2523%2BSniffer%2Bicmp%26btnG%3DGoogle%2B%25E6%2590%259C%25E7%25B4%25A2%26hl%3Dzh-CN%26newwindow%3D1%26sa%3D2&amp;frm=0&amp;ga_vid=1294659671.1225950945&amp;ga_sid=1225987224&amp;ga_hid=2039213032&amp;ga_fc=true&amp;flash=9.0.124&amp;u_h=800&amp;u_w=1280&amp;u_ah=772&amp;u_aw=1280&amp;u_cd=32&amp;u_tz=480&amp;u_his=1&amp;u_nplug=8&amp;u_nmime=23" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" width="336" frameborder="0" height="280"></iframe>

  ICMP就是所谓的Internet控制报文协议(Internet Control MessageProtocol),在网络中,一般用它来传递差错报文以及其他应注意的信息。ICMP一般被认为是和IP协议同一层的协议,IMCP报文通常被IP层或者更高层的协议(如:TCP或者UDP)使用,ICMP对于互联网以及其他基于IP协议的网络的正常运行起着非常重要的作用。有许多重要的网络程序都是基于ICMP协议上的,最为著名如Ping和Tracert等。本文就来介绍用Visual C#实现基于ICMP协议重要的网络命令Ping的方法。

  Ping命令是可以说是一个"跨平台"程序,这是因为Ping命令不仅存在Windows系统上,在Unix系统上也有Ping命令,其实对其他只要是支持网络的操作系统,一般也都存在该命令。Ping命令的主要作用是检测网络上主机的状态。要是在几年前,我们还可以下如此断言,如果不能Ping通某台主机,那么也就无法Telnet或者FTP到这台主机,但随着互联网的安全意识的增加,出现了访问控制清单的路由器和防火墙,由于ICMP报文是在IP数据包中被传输的,而到达一台主机不仅取决于IP层是否到达,还取决于使用何种协议和端口。譬如金山公司的金山网镖就可以禁止其他机器Ping通这台主机。所以在现在的情况下,即时Ping不通某台机器,但也有可能FTP登陆到这台机器,或者通过HTTP来浏览这台机器上的Web页面。

  一.Ping命令简介

  首先进入Windows系统中的命令提示符,输入"Ping/?"后,单击回车键,您就可以了解Ping命令的各种参数的使用方法。最为常见的使用方法是"Ping 远程计算机名称(或者远程计算机的IP地址)",如果在Ping命令的返回字符中有"Replyfrom",说明此主机在线,具体如图01:

  图01:Ping通主机时的运行界面

  如果返回字符中有"Request timeout",一般情况此主机不在线,具体如图02:

  图02:Ping不通主机时的运行界面

  二.Ping命令、ICMP报文和IP数据包

  Ping命令基于的是TCP/IP协议簇中的ICMP协议,在编写基于ICMP协议的网络应用程序时,应注意下面二点:

  1. ICMP报文是封装在IP数据包中传输的。

  了解这一点对后面编程非常重要,图03是IP数据包的组成结构图:

  图03:IP数据包的组成结构图

  习惯上把IP数据包划分为三个部分:

  (1).IP数据包中的前二十个字节的数据,即图03中从【4位版本】到【32位目的地址IP】,这称为IP首部。

  (2).选项,即图03中的【选项(如果有)】部分。

  (3).数据,即图03中的【数据】部分。

  其中后面二个部分组成的就是ICMP报文。ICMP报文的具体组成结构如图04所示:

  图04:ICMP报文组成结构图

  2. ICMP协议没有固定的端口号。

  ICMP协议和其他协议不同,其他协议基本都对应固定的端口号,如HTTP协议是通过80端口号来交换数据的。

  了解上面的二点对后面在Visual C#实现Ping命令是非常有用的。因为在下面的在编写VisualC#实现Ping命令的程序中,程序中定义一个名称为"IcmpPacket"类,通过这个类来构造ICMP报文,而定义"IcmpPacket"类依据的就是图03所示的ICMP报文组成结构。同样由于ICMP协议没有对应固定的端口号,这就意味着,编写VisualC#实现Ping命令中可以随意选择端口号,本文选择的端口号是"30"。

  由于ICMP协议是一个复杂的协议,而本文由于篇幅所限,对ICMP的很多细节,就不能一一介绍,如果你对ICMP协议感兴趣或对上面的介绍的仍然感觉有点模糊,那就请参阅探讨ICMP协议的相关书籍,它们一般介绍的都很详细。

  三.简介Visual C#实现Ping命令使用的类:

  VisualC#实现Ping命令中涉及到很多的类,其中最重要的是Socket类。这是因为程序中发送含有ICMP报文的IP数据包,接收含有ICMP超时或ICMP会显报文的IP数据包和设定IP数据包中的TTL数值都会使用到Socket类。表01和表02是Socket类中的常用属性和方法及其简要说明。

  

属性 说明AddressFamily 获取Socket的地址族。Available获取已经从网络接收且可供读取的数据量。Blocking获取或设置一个值,该值指示Socket是否处于阻塞模式。Connected 获取一个值,该值指示Socket是否已连接到远程资源。Handle 获取Socket的操作系统句柄。LocalEndPoint 获取本地终结点。ProtocolType获取Socket的协议类型。RemoteEndPoint 获取远程终结点。SocketType获取Socket的类型。         表01:Socket类的常用属性及其说明

  

方法说明Accept 创建新的Socket以处理传入的连接请求。BeginAccept开始一个异步请求,以创建新的Socket来接受传入的连接请求。BeginConnect开始对网络设备连接的异步请求。BeginReceive开始从连接的Socket中异步接收数据。BeginReceiveFrom 开始从指定网络设备中异步接收数据。BeginSend将数据异步发送到连接的BeginSendTo 向特定远程主机异步发送数据。Bind 使Socket与一个本地终结点相关联。Close强制Socket连接关闭。Connect建立到远程设备的连接。EndAccept结束异步请求以创建新的Socket来接受传入的连接请求。EndConnect结束挂起的异步连接请求。EndReceive结束挂起的异步读取。EndReceiveFrom结束挂起的、从特定终结点进行异步读取。EndSend 结束挂起的异步发送。EndSendTo结束挂起的、向指定位置进行的异步发送。GetSocketOption 返回Socket选项的值。IOControl为Socket设置低级别操作模式。Listen将Socket置于侦听状态。Poll 确定Socket的状态。Receive接收来自连接Socket的数据。ReceiveFrom 接收数据文报并存储源终结点。Select确定一个或多个套接字的状态。Send 将数据发送到连接的SendTo 将数据发送到特定终结点。SetSocketOption 设置Socket选项。Shutdown 禁用某Socket上的发送和接收。       表02:Socket类的常用方法及其说明

  其中包含六组异步方法,它们是:

  ·BeginAccept和EndAccept

  ·BeginConnect和EndConnect

  ·BeginReceive和EndReceive

  ·BeginReceiveFrom和EndReceiveFrom"

  ·BeginSend和EndSend

  ·BeginSendTo"和"EndSendTo

  其功能分别相当于"Accept"、"Connect"、"Receive"、"ReceiveFrom"、"Send"和"SendTo"方法。

  四.Visual C#实现Ping命令的关键步骤及其解决方法

  根据Ping命令的执行过程,可以把Ping命令分成三个主要的步骤:

  1. 定义ICMP报文。

  2. 客户机发送封装ICMP回显请求报文的IP数据包。

  3. 客户机接收封装ICMP应答报文的IP数据包。

  解决了上述三个步骤,Visual C#实现Ping命令就基本可以完成了。下面是这三个步骤的具体的解决方法。

  1. 定义ICMP报文:

  根据图05所示的ICMP报文组成结构,定义了一个类--IcmpPacket类。IcmpPacket类通过实例化就能够得到ICMP报文。下面代码是定义IcmpPacket类:

  public class IcmpPacket
{
 private Byte _type ;
 // 类型
 private Byte _subCode ;
 // 代码
 private UInt16 _checkSum ;
 // 校验和
 private UInt16 _identifier ;
 // 识别符
 private UInt16 _sequenceNumber ;
 // 序列号
 private Byte [ ] _data ;
 //选项数据
 public IcmpPacket ( Byte type , Byte subCode , UInt16 checkSum , UInt16 identifier , UInt16 sequenceNumber , int dataSize )
 {
  _type = type ;
  _subCode = subCode ;
  _checkSum = checkSum ;
  _identifier = identifier ;
  _sequenceNumber = sequenceNumber ;
  _data=new Byte [ dataSize ] ;
  //在数据中,写入指定的数据大小
  for ( int i = 0 ; i < dataSize ; i++ )
  {
   //由于选项数据在此命令中并不重要,所以你可以改换任何你喜欢的字符
   _data [ i ] = ( byte )'#' ;
  }
 }
 public UInt16 CheckSum
 {
  get
  {
   return _checkSum ;
  }
  set
  {
   _checkSum=value ;
  }
 }
 //初始化ICMP报文
 public int CountByte ( Byte [ ] buffer )
 {
  Byte [ ] b_type = new Byte [ 1 ] { _type } ;
  Byte [ ] b_code = new Byte [ 1 ] { _subCode } ;
  Byte [ ] b_cksum = BitConverter.GetBytes ( _checkSum ) ;
  Byte [ ] b_id = BitConverter.GetBytes ( _identifier ) ;
  Byte [ ] b_seq = BitConverter.GetBytes ( _sequenceNumber ) ;
  int i = 0 ;
  Array.Copy ( b_type , 0 , buffer , i , b_type.Length ) ;
  i+= b_type.Length ;
  Array.Copy ( b_code , 0 , buffer , i , b_code.Length ) ;
  i+= b_code.Length ;
  Array.Copy ( b_cksum , 0 , buffer ,i , b_cksum.Length ) ;
  i+= b_cksum.Length ;
  Array.Copy ( b_id , 0 , buffer , i , b_id.Length ) ;
  i+= b_id.Length ;
  Array.Copy ( b_seq , 0 , buffer , i , b_seq.Length ) ;
  i += b_seq.Length ;
  Array.Copy ( _data , 0 , buffer , i , _data.Length ) ;
  i += _data.Length ;
  return i ;
 }
 //将整个ICMP报文信息和数据转化为Byte数据包
 public static UInt16 SumOfCheck ( UInt16 [ ] buffer )
 {
  int cksum = 0 ;
  for ( int i = 0 ; i < buffer.Length ; i++ )
   cksum += ( int ) buffer [ i ] ;
   cksum = ( cksum >> 16 ) + ( cksum & 0xffff ) ;
   cksum += ( cksum >> 16 ) ;
   return ( UInt16 ) ( ~cksum ) ;
 }
}

  下列代码是利用IcmpPacket类来创建ICMP报文:

  IcmpPacket packet = new IcmpPacket ( 0 , 0 , 0 , 45 , 0 , 4 ) ;

  此代码定义的ICMP报文中的数据段长度为4个字节,所以整个ICMP报文长度为12个字节(即:8+4),而封装此ICMP报文的IP数据包长度就是32个字节(即:8+4+20)。在后面介绍的程序中,从客户端发送的ICMP会显请求报文的数据长度为4个字节,但从服务器介绍到的数据却是32个字节的原因。

  2. 发送封装ICMP回显请求报文的IP数据包:

  发送IP数据包首先要创建一个能够发送封装ICMP回显请求报文的IP数据包Socket实例,然后调用此Socket实例中的"SendTo"方法就可以了。下列代码是创建并初始化一个发送封装ICMP回显请求报文的IP数据包的Socket实例:

  Socket socket = new Socket ( AddressFamily.InterNetwork , SocketType.Raw , ProtocolType.Icmp ) ;

  创建初始化Socket实例有三个参数,下面是这些参数的说明:

  第一个参数定义目前网络的寻址方案,目前还是IPV4,所有只有定义为"AddressFamily.InterNetwork"。

  第二个参数定义Socket实例的类型,由于Socket的通讯协议是ICMP,所以选择枚举值"Raw Socket"。

  第三个参数是定义Socket实例有效的协议类型,由于此Socket实例要传送的是ICMP报文,所以选定枚举值为"ProtocolType.Icmp"。

  3.客户机接收封装ICMP应答报文的IP数据包:

  接收服务器端返回的封装ICMP应答报文的IP数据包只需调用Socket实例中的"ReceiveFrom"方法就可以了,具体可参阅下面介绍的程序实现中的代码。

  五.Visual C#实现Ping命令的设计、调试、运行的软件环境:

  (1).微软公司视窗2000服务器版

  (2).Visual Studio .Net正式版,.Net FrameWork SDK版本号3705

  六.Visual C#实现Ping命令的实现步骤:

  下面是Visual C#实现Ping命令的具体实现步骤:

  1. 启动Visual Studio .Net。

  2. 选择菜单【文件】|【新建】|【项目】后,弹出【新建项目】对话框。

  3. 将【项目类型】设置为【Visual C#项目】。

  4. 将【模板】设置为【Windows应用程序】。

  5. 在【名称】文本框中输入【Visual C#实现Ping命令】。

  6. 在【位置】的文本框中输入【E:/VS.NET项目】,然后单击【确定】按钮,具体如图05所示:

  图05:【Visual C#实现Ping命令】项目的【新建项目】对话框

  7. 【解决方案资源管理器】窗口中,双击Form1.cs文件,进入Form1.cs文件的编辑界面。

  8. 在Form1.cs文件的开头的导入命名空间的代码区,添加下列代码,下列代码是导入下面程序中使用到的类所在的命名空间:

  using System.Net ;
using System.Net.Sockets ;

  9. 把Visual Studio .Net的当前窗口切换到【Form1.cs(设计)】窗口,并从【工具箱】中的【Windows窗体组件】选项卡中拖入下列组件到设计窗体,并执行相应的操作:

  一个TextBox组件,用来输入进行Ping操作的远程主机名称或IP地址。

  一个ListBox组件,用以显示Ping操作结果。

  一个Label组件。

  一个Button组件,名称为button1,并在它拖入窗体后,双击它,则Visual Studio .Net会在Form1.cs文件中产生其Click事件对应的处理代码。

  10. 把Visual Studio .Net的当前窗口切换到Form1.cs的代码编辑窗口,并用下列代码替换Form1.cs中的InitializeComponent过程对应的处理代码:

  private void InitializeComponent ( )
{
this.textBox1 = new System.Windows.Forms.TextBox ( ) ;
this.label1 = new System.Windows.Forms.Label ( ) ;
this.listBox1 = new System.Windows.Forms.ListBox ( ) ;
this.button1 = new System.Windows.Forms.Button ( ) ;
this.SuspendLayout ( ) ;
this.textBox1.Location = new System.Drawing.Point ( 116 , 14 ) ;
this.textBox1.Name = "textBox1" ;
this.textBox1.Size = new System.Drawing.Size ( 148 , 21 ) ;
this.textBox1.TabIndex = 0 ;
this.textBox1.Text = "" ;
this.textBox1.TextChanged += new System.EventHandler ( this.textBox1_TextChanged ) ;
this.label1.Location = new System.Drawing.Point ( 12 , 14 ) ;
this.label1.Name = "label1" ;
this.label1.TabIndex = 1 ;
this.label1.Text = "请输入主机名:" ;
this.listBox1.BackColor = System.Drawing.SystemColors.WindowText ;
this.listBox1.ForeColor = System.Drawing.SystemColors.Window ;
this.listBox1.ItemHeight = 12 ;
this.listBox1.Location = new System.Drawing.Point ( 6 , 42 ) ;
this.listBox1.Name = "listBox1" ;
this.listBox1.Size = new System.Drawing.Size ( 400 , 280 ) ;
this.listBox1.TabIndex = 2 ;
this.button1.Location = new System.Drawing.Point ( 274 , 12 ) ;
this.button1.Name = "button1" ;
this.button1.TabIndex = 3 ;
this.button1.Text = "Ping" ;
this.button1.Click += new System.EventHandler ( this.button1_Click ) ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ;
this.ClientSize = new System.Drawing.Size ( 410 , 333 ) ;
this.Controls.AddRange ( new System.Windows.Forms.Control[ ] {
this.button1 ,
this.listBox1 ,
this.label1 ,
this.textBox1 } ) ;
this.MaximizeBox = false ;
this.Name = "Form1" ;
this.Text = "Visual C#实现Ping" ;
this.ResumeLayout ( false ) ;
}

  至此【Visual C#实现Ping命令】项目的界面设计工作和功能实现的前期准备工作就完成了,设计后的界面如图06所示:

  图06:【Visual C#实现Ping命令】项目的设计界面

  11. 用下列代码替换Form1.cs文件中的button1组件的Click事件对应的处理代码,下列代码的作用是创建、发送ICMP报文,实现Ping命令:

  Private Void Button1_click ( Object Sender , System.eventargs E )
{
 Listbox1.items.clear ( ) ;
 String Hostclient = Textbox1.text ;
 Int K ;
 For ( K = 0 ; K < 3 ; K++ )
 {
  Socket Socket = New Socket ( Addressfamily.internetwork , Sockettype.raw , Protocoltype.icmp ) ;
  Iphostentry Hostinfo ;
  Try
  {
   //解析主机ip入口
   Hostinfo = Dns.gethostbyname ( Hostclient ) ;
  }
  Catch ( Exception )
  {
   //解析主机名错误。
   Listbox1.items.add ( "没有发现此主机!" ) ;
   Return ;
  }
  // 取服务器端主机的30号端口
  Endpoint Hostpoint = ( Endpoint ) New Ipendpoint ( Hostinfo.addresslist[ 0 ] , 30 ) ;
  Iphostentry Clientinfo ;
  Clientinfo = Dns.gethostbyname ( Hostclient ) ;
  // 取客户机端主机的30端口
  Endpoint Clientpoint = ( Endpoint ) New Ipendpoint ( Clientinfo.addresslist[ 0 ] , 30 ) ;
  //设置icmp报文
  Int Datasize = 4 ; // Icmp数据包大小 ;
  Int Packetsize = Datasize + 8 ;//总报文长度
  Const Int Icmp_echo = 8 ;
  Icmppacket Packet = New Icmppacket ( Icmp_echo , 0 , 0 , 45 , 0 , Datasize ) ;
  Byte [ ] Buffer = New Byte [ Packetsize ] ;
  Int Index = Packet.countbyte ( Buffer ) ;
  //报文出错
  If ( Index != Packetsize )
  {
   Listbox1.items.add ( "报文出现问题!" ) ;
   Return ;
  }
  Int Cksum_buffer_length = ( Int ) Math.ceiling ( ( ( Double )index )/ 2 ) ;
  Uint16 [ ] Cksum_buffer = New Uint16 [ Cksum_buffer_length ] ;
  Int Icmp_header_buffer_index = 0 ;
  For ( Int I = 0 ; I < Cksum_buffer_length ; I++ )
  {
   //将两个byte转化为一个uint16
   Cksum_buffer[ I ] = Bitconverter.touint16 ( Buffer , Icmp_header_buffer_index ) ;
   Icmp_header_buffer_index += 2 ;
  }
  //将校验和保存至报文里
  Packet.checksum = Icmppacket.sumofcheck ( Cksum_buffer ) ;
  // 保存校验和后,再次将报文转化为数据包
  Byte [ ] Senddata = New Byte [ Packetsize ] ;
  Index = Packet.countbyte ( Senddata ) ;
  //报文出错
  If ( Index != Packetsize )
  {
   Listbox1.items.add ( "报文出现问题!" ) ;
   Return ;
  }
  Int Nbytes = 0 ;
  //系统计时开始
  Int Starttime = Environment.tickcount ;
  //发送数据包
  If ( ( Nbytes = Socket.sendto ( Senddata , Packetsize , Socketflags.none , Hostpoint ) ) == -1 )
  {
   Listbox1.items.add ( "无法传送报文!" ) ;
  }
  Byte [ ] Receivedata = New Byte[ 256 ] ; //接收数据
  Nbytes = 0 ;
  Int Timeout = 0 ;
  Int Timeconsume = 0 ;
  While ( True )
  {
   Nbytes = Socket.receivefrom ( Receivedata , 256 , Socketflags.none , Ref Clientpoint ) ;
   If ( Nbytes == -1 )
   {
    Listbox1.items.add ( "主机没有响应!" ) ;
    Break ;
   }
   Else If ( Nbytes > 0 )
   {
    Timeconsume = System.environment.tickcount - Starttime ;
    //得到发送报文到接收报文之间花费的时间
    Listbox1.items.add ( "reply From " + Hostinfo.addresslist[ 0 ].tostring ( ) + " In "
+ Timeconsume + "ms :bytes Received " + Nbytes ) ;
    Break ;
   }
   Timeconsume = Environment.tickcount - Starttime ;
   If ( Timeout > 1000 )
   {
    Listbox1.items.add ( "time Out" ) ;
    Break ;
   }
  }
  //关闭套接字
  Socket.close ( ) ;
 }
}

  12. 在Form1.cs文件中的Main函数之后,添加下列代码,下列代码是在Form1.cs中定义IcmpPacket类,程序是通过此类来构造ICMP报文:

  {
 private Byte _type ;
 // 类型
 private Byte _subCode ;
 // 代码
 private UInt16 _checkSum ;
 // 校验和
 private UInt16 _identifier ;
 // 识别符
 private UInt16 _sequenceNumber ;
 // 序列号
 private Byte [ ] _data ;
 //选项数据
 public IcmpPacket ( Byte type , Byte subCode , UInt16 checkSum , UInt16 identifier , UInt16 sequenceNumber , int dataSize )
 {
  _type = type ;
  _subCode = subCode ;
  _checkSum = checkSum ;
  _identifier = identifier ;
  _sequenceNumber = sequenceNumber ;
  _data=new Byte [ dataSize ] ;
  //在数据中,写入指定的数据大小
  for ( int i = 0 ; i < dataSize ; i++ )
  {
   //由于选项数据在此命令中并不重要,所以你可以改换任何你喜欢的字符
   _data [ i ] = ( byte )'#' ;
  }
 }
 public UInt16 CheckSum
 {
  get
  {
   return _checkSum ;
  }
  set
  {
   _checkSum=value ;
  }
 }
 //初始化ICMP报文
 public int CountByte ( Byte [ ] buffer )
 {
  Byte [ ] b_type = new Byte [ 1 ] { _type } ;
  Byte [ ] b_code = new Byte [ 1 ] { _subCode } ;
  Byte [ ] b_cksum = BitConverter.GetBytes ( _checkSum ) ;
  Byte [ ] b_id = BitConverter.GetBytes ( _identifier ) ;
  Byte [ ] b_seq = BitConverter.GetBytes ( _sequenceNumber ) ;
  int i = 0 ;
  Array.Copy ( b_type , 0 , buffer , i , b_type.Length ) ;
  i+= b_type.Length ;
  Array.Copy ( b_code , 0 , buffer , i , b_code.Length ) ;
  i+= b_code.Length ;
  Array.Copy ( b_cksum , 0 , buffer ,i , b_cksum.Length ) ;
  i+= b_cksum.Length ;
  Array.Copy ( b_id , 0 , buffer , i , b_id.Length ) ;
  i+= b_id.Length ;
  Array.Copy ( b_seq , 0 , buffer , i , b_seq.Length ) ;
  i+= b_seq.Length ;
  Array.Copy ( _data , 0 , buffer , i , _data.Length ) ;
  i+= _data.Length ;
  return i ;
 }
 //将整个ICMP报文信息和数据转化为Byte数据包
 public static UInt16 SumOfCheck ( UInt16 [ ] buffer )
 {
  int cksum = 0 ;
  for ( int i = 0 ; i < buffer.Length ; i++ )
   cksum += ( int ) buffer [ i ] ;
   cksum = ( cksum >> 16 ) + ( cksum & 0xffff ) ;
   cksum += ( cksum >> 16 ) ;
   return ( UInt16 ) ( ~cksum ) ;
 }
}

  13. 至此,在上述步骤都正确完成,并全部保存后,【VisualC#实现Ping命令】项目的全部工作就完成了。此时单击【F5】快捷键运行程序。在程序的【请输入主机名】文本框中输入远程主机名,这里输入的是互联网主机"WWW.163.com",单击【Ping】按钮,则程序把Ping操作后的信息显示出来。具体如图07所示:

  图06:【Visual C#实现Ping命令】的运行界面

  七.总结:

  在运行上述程序时,如果网络状况良好,则ICMP报文发送和返回时间差就很短,"in"后面带的时间就小,这也就是所谓的"离"的"近";如果网络状况不好,则ICMP报文发送和返回的时间差就长,"in"后面带的时间就大,甚至可能出现timeout,即超时。这表明"离"的"远"。当然如果对方没有开机,也会出现超时情况,所以实际操作要具体情况,具体对待。

  细心的读者可能多次运行此程序的时候,就会发现,第一次发送时所耗时间往往比本程序紧接着的几次大得多。这是程序数据缓存造成的。这也就是说ping命令的第一次数据是不准确的。这种情况不仅在本文中Ping命令中存在,对于Windows系统的Ping也存同样的问题。

原创粉丝点击