TCP-IP详解卷1-07:Ping程序

TCP-IP详解卷1-07:Ping程序

Ping目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答。
Ping程序是对两个TCP/IP系统连通性进行测试的基本工具。它只利用ICMP回显请求和回显应答报文,而不用经过传输层( TCP/UDP)。Ping服务器一般在内核中实现ICMP的功能。

一:纯C#代码的Ping程序
     
    public class IcmpPacket      
    {          
        public Byte Type;               //类型   
        public Byte SubCode;            //代码      
        public UInt16 CheckSum;         //校验和      
        public UInt16 Identifier;       //表示      
        public UInt16 SequenceNumber;   //序列号 
        public Byte[] Data;             //数据
    } 
    //计算校验和函数
    //首部检验和字段是根据IP首部计算的检验和码。它不对首部后面的数据进行计算。
    //ICMP、IGMP、UDP和TCP在它们各自的首部中均含有同时覆盖首部和数据检验和码。
    //为了计算一份数据报的IP检验和,
    //首先把检验和字段置为0。计算完校验码后,再用计算后的校验码替代
    //然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。
    //当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。
    //由于接收方在计算过程中包含了发送方存在首部中的检验和,
    //因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。
    //如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
   

    //一般的相加肯定要溢出或进位的,那溢出的部分信息就丢掉了。
    //为了保留溢出的信息,需要把进位信息保留下来,也就是移到低位上相加,而这个一般加法是很难实现的,所以需要别的改进后的加法(反码加)。
    //想考虑8位时的情况:     
    //0xF0 + 0xF0 = 0xE0 + 溢出;
    //如何保留溢出位,与最低位先加就得到:
    //0xF0 + 0xF0 = 0xE1;
    //上面运算是错误的,但有什么加法可以时上式成立呢???
    //答案就是反码加(计算校验和):
    //~0xF0 + (~0xF0) = ~0xE1; ==> (0xF0 +' 0xF0 = ~0xE1 = 0x1E), 0x1E就是校验和。    检验校验和:
    //0xF0 +' 0xF0 +' 0x1E = 0xFF; 校验时同时计算校验字节,结果为0xFF,为正确。
    public static UInt16 checksum(UInt16[] buffer, int size)          
    {              
        Int32 cksum = 0;         
        while (size > 0)          
        {                  
            cksum += buffer[counter];           
            size -= 1;             
        }              
        cksum = (cksum >> 16) + (cksum & 0xffff);  /* 高位低位相加 */    
        cksum += (cksum >> 16); /* 上一步溢出时,将溢出位也加到sum中 */            
        return (UInt16)(~cksum);  /* 注意类型转换,现在的校验和为16位 */   
    }
    //将IcmpPacket类的数据填入数组中Buffer,
    public static Int32 Serialize(IcmpPacket packet,
        Byte[] Buffer,           
        Int32 PacketSize,
        Int32 PingData)         
    {              
        Int32 cbReturn = 0;              
        // serialize the struct into the array              
        int Index = 0;               
        Byte[] b_type = new Byte[1];   //类型           
        b_type[0] = (packet.Type);               
        Byte[] b_code = new Byte[1];   //代码           
        b_code[0] = (packet.SubCode);               
        Byte[] b_cksum = BitConverter.GetBytes(packet.CheckSum);     //校验和,初始全0,计算完后,替换         
        Byte[] b_id = BitConverter.GetBytes(packet.Identifier);      //标识符        
        Byte[] b_seq = BitConverter.GetBytes(packet.SequenceNumber); //流水号              
        Array.Copy(b_type, 0, Buffer, Index, b_type.Length);              
        Index += b_type.Length;               
        Array.Copy(b_code, 0, Buffer, Index, b_code.Length);              
        Index += b_code.Length;               
        Array.Copy(b_cksum, 0, Buffer, Index, b_cksum.Length);              
        Index += b_cksum.Length;               
        Array.Copy(b_id, 0, Buffer, Index, b_id.Length);              
        Index += b_id.Length;              
        Array.Copy(b_seq, 0, Buffer, Index, b_seq.Length);              
        Index += b_seq.Length;               
        // copy the data              
        Array.Copy(packet.Data, 0, Buffer, Index, PingData);     //数据         
        Index += PingData;              
        if (Index != PacketSize/* sizeof(IcmpPacket)  */)              
        {                  
            cbReturn = -1;                  
            return cbReturn;             
        }              
        cbReturn = Index;             
        return cbReturn;          
    }
    //PING,host对方主机IP或主机名,主要的难度在于计算校验和
    public string PingHost(string host)          
    {                       
        IPHostEntry ServerHE, fromHE;              
        int nBytes = 0;              
        int dwStart = 0, dwStop = 0;               
        //初始化ICMP的Socket              
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);               socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000); // 得到Server EndPoint              
        try              
        {                  
            ServerHE = Dns.GetHostByName(host);              
        }              
        catch (Exception)              
        {                   
            return "没有发现主机";              
        }               
        // 把 Server IP_EndPoint转换成EndPoint              
        IPEndPoint ipepServer = new IPEndPoint(ServerHE.AddressList[0], 0);              
        EndPoint epServer = (ipepServer);   
           
        // 设定客户机的接收Endpoint              
        fromHE = Dns.GetHostByName(Dns.GetHostName());              
        IPEndPoint ipEndPointFrom = new IPEndPoint(fromHE.AddressList[0], 0);              
        EndPoint EndPointFrom = (ipEndPointFrom);               
        int PacketSize = 0;              
        IcmpPacket packet = new IcmpPacket();  
            
        // 构建要发送的包              
        packet.Type = 0; //消息应答             
        packet.SubCode = 0;              
        packet.CheckSum =0;              
        packet.Identifier = 45;              
        packet.SequenceNumber = 0;              
        int PingData = 24; // sizeof(IcmpPacket) - 8;              
        packet.Data = new Byte[PingData];    
          
        // 初始化Packet.Data              
        for (int i = 0; i <  PingData; i++)              
        {                  
            packet.Data[i] = (byte)'#';              
        }               
        //Variable to hold the total Packet size              
        PacketSize = 32;            

        /*************************************中间这段都是为计算校验和****************************************/

        //将类填入字节数组中
        Byte[] icmp_pkt_buffer = new Byte[PacketSize];              
        Int32 Index = 0;                          
        Index = Serialize(packet, icmp_pkt_buffer,PacketSize,PingData);                           
        if (Index == -1)              
        {                  
            return "Error Creating Packet";               
        }              

        // 将字节数组转换成UINT16数组                           
        Double double_length = Convert.ToDouble(Index);              
        Double dtemp = Math.Ceiling(double_length / 2);              
        int cksum_buffer_length = 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++)              
        {                  
            cksum_buffer[i] = BitConverter.ToUInt16(icmp_pkt_buffer, icmp_header_buffer_index);  
            icmp_header_buffer_index += 2;              
        }              

        //计算校验和              
        //注意,这个代码这处是否问题的,强行进行截断,当Index为奇数时,在进行校验和计算的加和过程中,应该考虑进去
        //而此处之所以能成功,主要是因为数据部分都是0,所以加和没加都是一样的
        UInt16 u_cksum = checksum(cksum_buffer, cksum_buffer_length); 

        /*****************************************************************************/
            
        //替换校验和             
        packet.CheckSum = u_cksum;               
           
        Byte[] sendbuf = new Byte[PacketSize];              
        //将获得校验和的类转换成数组发送           
        Index = Serialize( packet, sendbuf, PacketSize,PingData);                        
        if (Index == -1)              
        {                  
            return "Error Creating Packet";               
        }               
        dwStart = System.Environment.TickCount;
           
        if ((nBytes = socket.SendTo(sendbuf, PacketSize, 0, epServer)) == SOCKET_ERROR)              
        {                  
            return "Socket Error: cannot send Packet";              
        }              
            
        Byte[] ReceiveBuffer = new Byte[256];              
        nBytes = 0;             
       
        bool recd = false;            
        int timeout = 0;              
         
        while (!recd)              
        {                
            nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref EndPointFrom);       
            if (nBytes == SOCKET_ERROR)                 
            {                    
                return "主机没有响应";       
            }                  
            else if (nBytes > 0)    
            {                     
                dwStop = System.Environment.TickCount - dwStart;
                  
                return "Reply from " + epServer.ToString() + " in " + dwStop + "ms.  Received: " + nBytes + " Bytes.";            
            }                  
            timeout = System.Environment.TickCount - dwStart;       
            if (timeout > 1000)                 
            {                     
                return "超时";       
            }             
        }              
           
        socket.Close();              
        return "";          
    }

2: 用到了C#中Ping类的代码

    Ping pingClient = new Ping();
    pingClient.PingCompleted += new PingCompletedEventHandler(pingClient_PingCompleted);

    //Occurs when an asynchronous operation to send an Internet Control Message Protocol (ICMP) echo message
    //and receive the corresponding ICMP echo reply message completes or is canceled.
    private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            if (e.Cancelled)
            {
                pingDetailsTextBox.Text += "  Ping cancelled. /r/n";
            }
            else
            {
                if (e.Reply.Status == IPStatus.Success)
                {
                    pingDetailsTextBox.Text += "  " + e.Reply.Address.ToString() + " " + e.Reply.RoundtripTime.ToString(NumberFormatInfo.CurrentInfo) + "ms" + "/r/n";
                }
                else
                {
                    pingDetailsTextBox.Text += "  " + GetStatusString(e.Reply.Status) + "/r/n";
                }
            }
        }
        else
        {
            pingDetailsTextBox.Text += "  Ping error./r/n";
            MessageBox.Show("An error occurred while sending this ping. " + e.Error.InnerException.Message);
        }
        sendButton.Enabled = true;
    }
    //错误码表
    private string GetStatusString(IPStatus status)
    {
        switch (status)
        {
            case IPStatus.Success:
                return "Success.";
            case IPStatus.DestinationHostUnreachable:
                return "Destination host unreachable.";
            case IPStatus.DestinationNetworkUnreachable:
                return "Destination network unreachable.";
            case IPStatus.DestinationPortUnreachable:
                return "Destination port unreachable.";
            case IPStatus.DestinationProtocolUnreachable:
                return "Destination protocol unreachable.";
            case IPStatus.PacketTooBig:
                return "Packet too big.";
            case IPStatus.TtlExpired:
                return "TTL expired.";
            case IPStatus.ParameterProblem:
                return "Parameter problem.";
            case IPStatus.SourceQuench:
                return "Source quench.";
            case IPStatus.TimedOut:
                return "Timed out.";
            default:
                return "Ping failed.";
        }
    }

    private void sendButton_Click(object sender, EventArgs e)
    {
        addressTextBox.SelectAll();

        if (addressTextBox.Text.Length != 0)
        {
            sendButton.Enabled = false;

            pingDetailsTextBox.Text += "Pinging " + addressTextBox.Text + " . . ./r/n";

            pingClient.SendAsync(addressTextBox.Text, null);
        }
        else
        {
            MessageBox.Show("Please enter an IP address or host name.");
        }

    }

    private void cancelButton_Click(object sender, EventArgs e)
    {
        pingClient.SendAsyncCancel();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值