为了了解Icmp协议,自己写了一个

using System;
using System.Text;
using Pierce.AttributeClass;
namespace Pierce.Icmp
{
    
/// <summary>
    
/// Icmp:Icmp Protocol
    
/// </summary>

    [LastModified("2005-10-11","Icmp Protocol")]
    
public class Icmp
    
{
        
private eType type;                            //1 Byte Type
        private eCode code;                            //1 Byte Code
        private UInt16 Checksum;                    //2 Bytes CheckSum
        private int Messagesize;                    //Message Size,Include Identify,Seq,Content
        private int Packetsize;                        //Icmp package Size
        public  byte[] Message = new byte[1024];    //接收回复的byte[]
        private byte[] Data=null;                    //存储Icmp包信息
        private static UInt16 sequence=0;            //顺序号
        private static UInt16 identify=0;            //认证号
        private string content;                        //发送信息
        private byte[] byContent;                    //发送信息的数组
        /// <summary>
        
/// Icmp Type 枚举
        
/// </summary>

        public enum eType:byte
        
{
            EchoReply
=0,
            DestinationUnreachable
=3,
            SourceQuench
=4,
            Redirect
=5,
            EchoRequest
=8,
            TimeExceeded
=11,
            ParameterProblem
=12,
            TimeStampRequest
=13,
            TimeStampReply
=14,
            InformationRequest
=15,
            InformationReply
=16
        }

        
/// <summary>
        
/// Icmp Code 枚举
        
/// </summary>

        public enum eCode:byte
        
{
            NetworkUnreachable
=0,
            HostUnreachable
=1,
            ProtocolUnreachable
=2,
            PortUnreachable
=3,
            FragmentationNeededAndDFFlagSet
=4,
            SourceRouteFailed
=5,
            DestinationNetworkUnknow
=6,
            DestionationHostUnknow
=7,
            SourceHostIsolated
=8,
            CommunicationWithDestinationNetworkProhibited
=9,
            CommunicationWithDestinationHostProhibited
=10,
            NetworkUnreachableForTypeOfService
=11,
            HostUnreachableForTypeOfService
=12
        }

        
/// <summary>
        
/// 属性,读写,获取,设置Type
        
/// </summary>

        internal eType Type
        
{
            
get{return type;}
            
set{type=value;}
        }

        
/// <summary>
        
/// 属性,读写,获取、设置Code
        
/// </summary>

        internal eCode Code
        
{
            
get{return code;}
            
set{code=value;}
        }

    
        
/// <summary>
        
/// 属性,只读,返回认证号
        
/// </summary>

        internal static UInt16 Identify
        
{
            
get{return identify;}
            
        }

        
/// <summary>
        
/// 属性,制度,返回序列号
        
/// </summary>

        internal static UInt16 Sequence
        
{
            
get{return sequence;}
            
        }

        
/// <summary>
        
/// 属性,读写,设置,返回发送内容
        
/// </summary>

        internal string Content
        
{
            
get{return content;}
            
set
            
{
                
//获取发送信息内容,并转换为字节数组
                content=value;
                byContent
=Encoding.ASCII.GetBytes(content);
                
//获取发送信息长度,包括Identify+Sequence+Content
                Messagesize=byContent.Length+4;
                
//因为MessageSize不包括type,code,checksum,所以要+4
                Packetsize=Messagesize+4;    
                
//保证是2的整数倍
                
                
if(IfDivision2(Packetsize)==false)
                
{
                    Data 
= new byte[Packetsize +1];
                }

                
else
                
{
                    Data
=new byte[Packetsize];
                }

            }

        }

        
/// <summary>
        
/// 生成认证号
        
/// </summary>
        
/// <returns>返回认证号</returns>

        private UInt16 getIdentify()
        
{
            
try
            
{
                identify
++;
            }

            
catch
            
{
                identify
=0;
            }

            
                
return identify;
            
        }

        
/// <summary>
        
/// 生成顺序号
        
/// </summary>
        
/// <returns>返回顺序号</returns>

        private UInt16 getSequence()
        
{
            
try
            
{
                sequence
++;
            }

            
catch
            
{
                sequence
=0;
            }

            
            
return sequence;
            
        }

        
/// <summary>
        
/// 获取整个Icmp包的字节数组
        
/// </summary>
        
/// <returns></returns>

        internal byte[] IcmpByteArray()
        
{
            
//设置checksum的初始值为0,计算实际的Checksum
            UInt16 identi=0;
            UInt16 seq
=0;
            
this.Checksum=0;
            
this.Checksum=this.getChecksum(out identi,out seq);
            
//获取Icmp包的数组
            byte[] byteIcmp=this.getBytes(identi,seq);
            
//返回Icmp包的数组
            return byteIcmp;
            
        }

        
/// <summary>
        
/// 属性,只读,获取MessageSize, 包括Identi+Seq+Content,
        
/// 读取收到的Icmp包时使用
        
/// </summary>

        internal int MessageSize
        
{
            
get
            
{
                
return Messagesize;
            }

        
        }

        
/// <summary>
        
/// default Constructor
        
/// </summary>

        internal Icmp()
        
{
            
        }

        
/// <summary>
        
/// Constructor Override,Receive Response and Parse It
        
/// Because Received Packet include Ip Packet,so we should get our content from position 20
        
/// </summary>
        
/// <param name="data">接收到的IP Package,包含了IP Header共20bytes长度</param>
        
/// <param name="size">总长度</param>    

        internal Icmp(byte[] data, int size)
        
{
            type 
= (eType)Enum.Parse(typeof(eType),data[20].ToString());
            
            code 
= (eCode)Enum.Parse(typeof(eCode),data[21].ToString());
            Checksum 
= BitConverter.ToUInt16(data, 22);            //校验和
            Messagesize = size - 24;                            //Message的长度
            Message=new byte[Messagesize];                        //设置Message长度
            Buffer.BlockCopy(data, 24, Message, 0, Messagesize);
        }

        
/// <summary>
        
/// 检查所给的参数是否可以被2整除,是:True,否:False
        
/// </summary>
        
/// <param name="Number">给定参数</param>
        
/// <returns>如果是,返回True,不是,返回False</returns>

        [LastModified("2005-10-12","计算给定参数是否是2的整数倍")]
        
private bool IfDivision2(int Number)
        
{
            
int yu=Number%2;
            
if(yu==0)
            
{
                
return true;
            }

            
else
            
{
                
return false;
            }

        }

        
/// <summary>
        
/// 把Icmp包转换为byte[],除了checksum的值是0外,其他的都是实际值,    
        
/// </summary>
        
/// <param name="identi">认证号</param>
        
/// <param name="seq">序列号</param>
        
/// <returns>返回Icmp的字节数组</returns>

        [LastModified("2005-10-12","将Checksum=0时,把Icmp包转换为Byte[],发送请求时用")]
        
private byte[] getBytes(UInt16 identi,UInt16 seq)
        
{
            
//设置初始值,如果内容为空,那么设置为空,
            
//type=0,那么位8,即Echo Request
            
//code=0 
            if(content==null)
            
{
                content
="";
            }

            
if(type==0)
            
{
                type
=eType.EchoRequest;
                
            }

            
if(code==0)
            
{
                code
=eCode.NetworkUnreachable;
            }

            
//位移变量
            int cursor=0;    
            
//加入Type
            Buffer.BlockCopy(BitConverter.GetBytes((byte)type), 0, Data, cursor, 1);
            cursor
++;
            
//加入Code
            Buffer.BlockCopy(BitConverter.GetBytes((byte)code), 0, Data, cursor, 1);
            cursor
++;
            
//加入CheckSum
            Buffer.BlockCopy(BitConverter.GetBytes(Checksum), 0, Data, cursor, 2);
            cursor
+=2;
            
//加入Message Content的Identify,2 Bytes
            Buffer.BlockCopy(BitConverter.GetBytes(identi),0, Data, cursor,2);
            cursor
+=2;
            
//加入MessageContent的Sequence,2 Bytes
            Buffer.BlockCopy(BitConverter.GetBytes(seq),0,Data,cursor,2);
            cursor
+=2;
            
//加入content            
            Buffer.BlockCopy
                (
                            byContent,
0,Data,cursor,byContent.Length
                );
            
//
            return Data;
        }

        
/// <summary>
        
/// 生成Icmp包的byte[],同时传出认证号和序列号
        
/// </summary>
        
/// <param name="identi">认证号</param>
        
/// <param name="seq">序列号</param>
        
/// <returns>返回的Byte[]</returns>

        [LastModified("2005-10-12","把Icmp包装换位Byte[]")]
        
internal byte[] getBytes(out UInt16 identi,out UInt16 seq)
        
{
            
//设置初始值
            if(content==null)
            
{
                content
="";
            }

            
if(type==0)
            
{
                type
=eType.EchoRequest;
                
            }

            
if(code==0)
            
{
                code
=eCode.NetworkUnreachable;
            }

            
//位移变量
            int cursor=0;    
            
//加入Type
            Buffer.BlockCopy(BitConverter.GetBytes((byte)type), 0, Data, cursor, 1);
            cursor
++;
            
//加入Code
            Buffer.BlockCopy(BitConverter.GetBytes((byte)code), 0, Data, cursor, 1);
            cursor
++;
            
//加入CheckSum
            Buffer.BlockCopy(BitConverter.GetBytes(Checksum), 0, Data, cursor, 2);
            cursor
+=2;
            
//加入Message Content的Identify,2 Bytes
            identi=getIdentify();
            Buffer.BlockCopy(BitConverter.GetBytes(identi),
0, Data, cursor,2);
            cursor
+=2;
            
//加入MessageContent的Sequence,2 Bytes
            seq=getSequence();
            Buffer.BlockCopy(BitConverter.GetBytes(seq),
0,Data,cursor,2);
            cursor
+=2;
            
//加入content            
            Buffer.BlockCopy
                (
                byContent,
0,Data,cursor,byContent.Length
                );
            
//
            return Data;
        }

        
/// <summary>
        
/// Method to Calcu CheckSum
        
/// 每16位循环相加,进位加在末尾,和再求反        
        
/// </summary>
        
/// <param name="identi">传出值的参数,做认证用</param>
        
/// <param name="seq" >传出值的参数,标记序列号</param>
        
/// <returns>return UInt16 value</returns>

        [LastModified("2005-10-12","计算Checksum")]
        
private UInt16 getChecksum(out UInt16 identi,out UInt16 seq)
        
{
            UInt32 chcksm 
= 0;
            
byte[] data = getBytes(out identi,out seq);            
            
int index = 0;
            
//每16位循环相加
            while ( index < Packetsize)
            
{
                
//把data中的每2个byte,转换为UInt16,然后加到chcksm中
                chcksm += Convert.ToUInt32(BitConverter.ToUInt16(data, index));
                index 
+= 2;
            }

            
//chcksm右移16位,然后同chcksm同0xffff相与的值相加
            
//和值的小16位同大16位相加,即进位加在末尾
            chcksm = (chcksm >> 16+ (chcksm & 0xffff);
            
//chchsm在加chcksm右移16位的值
            
//同样是保证进位加在末尾
            chcksm += (chcksm >> 16);
            
//chchsm取反,转换为UInt16
            return (UInt16)(~chcksm);
        }

        
/// <summary>
        
/// 计算整个Icmp包的Uint16值,如果为0,说明包没有出现传输错误,
        
/// 因为Checksum的值为其他字段的值的和再求反,所以如果在加上Checksum,
        
/// 那么得到全1的byte,再求反,结果为0
        
/// </summary>
        
/// <param name="data">接收到的数组</param>
        
/// <param name="packetsize">数组长度</param>
        
/// <returns></returns>

        [LastModified("205-10-13","检验收到的数组传输中是否发生错误")]
        
internal UInt16 CalcuIcmpPack(byte[] data,int packetsize)
        
{
            UInt32 chcksm 
= 0;                
            
int index = 0;
            
//每16位循环相加
            while ( index < packetsize)
            
{
                
//把data中的每2个byte,转换为UInt16,然后加到chcksm中
                chcksm += Convert.ToUInt32(BitConverter.ToUInt16(data, index));
                index 
+= 2;
            }

            
//chcksm右移16位,然后同chcksm同0xffff相与的值相加
            
//和值的小16位同大16位相加,即进位加在末尾
            chcksm = (chcksm >> 16+ (chcksm & 0xffff);
            
//chchsm在加chcksm右移16位的值
            
//同样是保证进位加在末尾
            chcksm += (chcksm >> 16);
            
//chchsm取反,转换为UInt16
            return (UInt16)(~chcksm);
        }

    }

}

以上是协议的实现,下面是协议的使用
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Pierce.AttributeClass;
namespace Pierce.Icmp
{
    
/// <summary>
    
/// UseIcmp :使用Icmp协议
    
/// </summary>

    [LastModified("2005-10-13","使用Icmp协议")]
    
public class UseIcmp
    
{
        
private Socket host;            //Socket连接
        private int receiveTime;        //接收时间上限
        private int sendTime;            //发送时间上限
        /// <summary>
        
/// 属性,读写,接收时间上限
        
/// </summary>

        public int ReceiveTime
        
{
            
get{return receiveTime;}
            
set
            
{
                receiveTime
=value;
                host.SetSocketOption(SocketOptionLevel.Socket,
                    SocketOptionName.ReceiveTimeout, receiveTime);
            }

        }

        
/// <summary>
        
/// 属性,读写,发送时间上限
        
/// </summary>

        public int SendTime
        
{
            
get
            
{
                
return sendTime;
            }

            
set
            
{
                sendTime
=value;
                host.SetSocketOption(SocketOptionLevel.Socket,
                    SocketOptionName.SendTimeout, sendTime);
            }

        }

        
/// <summary>
        
/// 构造函数,初始化Socket
        
/// </summary>

        public UseIcmp()
        
{
            host 
= new Socket(AddressFamily.InterNetwork, SocketType.Raw,
                ProtocolType.Icmp);
        }

        
/// <summary>
        
/// 给指定地址,发送指定内容的Icmp包,并接受恢复
        
/// 如果收到恢复,那么返回True,否则,返回False
        
/// </summary>
        
/// <param name="Ip">指定Ip地址</param>
        
/// <param name="Content">指定发送信息</param>
        
/// <returns>返回值</returns>

        public bool SendIcmp(string Ip,string Content)
        
{
            
bool ifOk=false;                    //是否成功
            
            
int recv;                            //接收到的字节数
        
            
//目的信息
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(Ip), 0);
            EndPoint ep 
= (EndPoint)iep;
            
//设置Type,Code和CheckSum的初始值
            Icmp packet = new Icmp();
            packet.Type 
= Pierce.Icmp.Icmp.eType.EchoRequest;        
            packet.Code 
= Pierce.Icmp.Icmp.eCode.NetworkUnreachable;                
            packet.Content
=Content;
    
            
//发送信息的字节内容
            byte[] icmpbyte=packet.IcmpByteArray();            
            host.SendTo(icmpbyte, icmpbyte.Length, SocketFlags.None, iep);
            
byte[] data = new byte[1024];
            
try
            
{                
                recv 
= host.ReceiveFrom(data, ref ep);
            }
 
            
catch 
            
{
                
                host.Close();
                
return ifOk;
            }

            
//接收回复
            Icmp response=null;
            
try
            
{
                response 
= new Icmp(data, recv);
            }

            
catch
            
{
                
                host.Close();
                
return ifOk;
            }

            
//检验接收到的包是否出现错误
            UInt16 sum=response.CalcuIcmpPack(data,recv);
            
if(sum!=0)
            
{
            
                host.Close();
                
return ifOk;
            }


            
int Identifier = BitConverter.ToInt16(response.Message, 0);
            
int Sequence = BitConverter.ToInt16(response.Message, 2);        
            
string stringData = Encoding.ASCII.GetString(response.Message,
                
4, response.MessageSize - 4);
            
if( response.Type==Icmp.eType.EchoReply &&
                Icmp.Identify
==Identifier && Icmp.Sequence==Sequence && 
                packet.Content
==Content)
            
{
                ifOk
=true;
            }
        
            
            host.Close();
            
return ifOk;
        }

    }

}

posted on 2005-11-03 09:40  Pierce  阅读(680)  评论(0编辑  收藏  举报

导航