科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件利用Visual C#实现ICMP网络协议

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

ICMP就是所谓的Internet控制报文协议,在网络中,一般用它来传递差错报文以及其他应注意的信息。

作者:阿虎 来源:天极开发 2007年11月11日

关键字:

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


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

  七.总结

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

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

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章