C# 利用ICMP协议实现Ping功能

  • Post author:
  • Post category:其他


.net core中有Ping类可以实现Ping功能;Ping.SendPingAsync();

构建ICMP协议数据包:

 /// <summary>
        /// 构建ICMP Ping包
        /// </summary>
        byte[] BuildIcmpPacket(string dataMsg="hello")
        {
            var dataBody = Encoding.ASCII.GetBytes(dataMsg);
            var fixHeadBytes=new byte[] {
                0x08, //Type: 回送请求和回送回答报文: 请求报文:回复Type =0 
                0x00, //SubCode:固定=0
                0x00,//检验和(2字节)
                0x00, 
                0x00,//标识符
                0x01,
                0x00,//序号
                0x01
            };

            //最后构成icmp包
            byte[] icmpPacket = new byte[fixHeadBytes.Length + dataBody.Length];
            Array.Copy(fixHeadBytes, 0, icmpPacket, 0, fixHeadBytes.Length);
            Array.Copy(dataBody, 0, icmpPacket, fixHeadBytes.Length, dataBody.Length);
            //计算校验和
            byte[] chkSumBytes = IcmpCheckSum(icmpPacket);
            Array.Copy(chkSumBytes, 0, icmpPacket,2, chkSumBytes.Length);
            return icmpPacket;
        }

        /// <summary>
        /// ICMP 协议中的检验和
        /// </summary>
        /// <returns>返回2个字节长度的检验和,低位在前,高位在后</returns>
        /// <remarks>
        ///  1:) 将每两个字节(16位)相加,若出现最后还剩一个字节继续与前面结果相加
        ///  2:) 将高16位与低16位相加,直到高16位为0为止。最后的结果取反
        /// </remarks>
        byte[] IcmpCheckSum(byte[] packets)
        {
            #region 每两个字节一组
            int dataIndex = 0;
            int targetIndex = packets.Length / 2;
            int sumLength= (int)Math.Ceiling(packets.Length / 2d);
            uint[] sumBytes = new uint[sumLength];// 用于计算校验和
            for (int i = 0; i < targetIndex; i++)
            {
                sumBytes[i] = BitConverter.ToUInt16(packets, dataIndex);
                dataIndex += 2;
            }
            #endregion
            int checkSumResult = 0;
            for (int i = 0; i < sumBytes.Length; i++)
            {
                checkSumResult += (int)sumBytes[i];
            }
            //如果数据包是奇数
            if (packets.Length % 2 > 0)
            {
                checkSumResult += (int)packets[packets.Length - 1];
            }
            // 高16位与低16位相加
            int calSumResult = (checkSumResult >> 16) + (checkSumResult & 0xffff);
            // 如果高于16位还有值,继续与低16位相加
            calSumResult += (calSumResult >> 16);
            uint finnalResult=  (uint)(~calSumResult & 0xffff);
            var chkSumBytes = new byte[]
            {
                (byte)(finnalResult & 0xFF),
                (byte)(finnalResult>>8 & 0xFF)                
            };
           
            return chkSumBytes;
        }

ICMP协议数据包收发

     #region 发送Ping 包
                    string pingMsg = "abcdefghijklmnopqrstuvwabcdefghi";//可自定义的数据包
                    var icmpPacket = BuildIcmpPacket(pingMsg);
                    //ICMP
                    using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
                    int sendTag = await socket.SendToAsync(icmpPacket, SocketFlags.None, targetServer, tokenSource.Token);
                    if (sendTag == -1)
                    {
                        return;
                    }
                    #endregion

                    #region 收数据包
                    byte[] receiveBuffer = new byte[256];
                    //接收超时
                    using var timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);
                    timeoutCancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(timeout));
                    var receiveFromResult = await socket.ReceiveFromAsync(receiveBuffer, SocketFlags.Peek, targetServer, timeoutCancellationTokenSource.Token);

                    int receiveLen = receiveFromResult.ReceivedBytes;
                    int ipFixHeadLen = 20;//IP 包头部固定20个字节
                    int icmpFixHeadLen = 8;// ICMP包头固定长度=8;
                    if (receiveLen <= (ipFixHeadLen + icmpFixHeadLen))
                    {
                        return;
                    }
                    int dataLeng = receiveLen - ipFixHeadLen - icmpFixHeadLen;
                    var icmpReplyBuffer = new byte[dataLeng];
                    Array.Copy(receiveBuffer, ipFixHeadLen + icmpFixHeadLen, icmpReplyBuffer, 0, dataLeng);
                    #endregion
                  // 验证校验 可选
                     string reply = Encoding.ASCII.GetString(icmpReplyBuffer);
                     if (reply.Contains(pingMsg))
                     {
                         socket.Close();                          
                     }

注意:linux环境下运行:var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);

可能会因为权限不够,报错:临时解决办法可以使用sudio 提升运行权限,或者给当前用户socket权限;



版权声明:本文为elie_yang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。