c#多线程ping实战

论坛 期权论坛 脚本     
匿名技术用户   2021-1-7 09:29   502   0
瀚海星云 -- 文章阅读 [讨论区: DotNet]
[ 上一篇][ 本讨论区][ 下一篇][ 同主题阅读]
发信人: SharperC.bbs@bbs.sjtu.edu.cn (Wind 疾风步 Walker), 信区: DotNet
标  题: 【原创】c#多线程ping实战
发信站: 饮水思源 (Thu May  5 14:03:22 2005)
转信站: USTC!bbsnews.sdu!SJTU


终于还是弄出来了

关于发ICMP包进行ping,以检测网络各主机的连接状况
网上已有相关的C#程序

但是给出的代码是不能正常工作的,原因在于阻塞式socket的
sendto方法,当目标机不存在时,会挂起ping线程

第二,多线程下,对于收到的packet没有检查sequence,这样
就可能把ping主机a的ECHO当作ping主机b的ECHO,起不到监视
指定网络连接的作用


参考C/C++中的socket编程,作了如下改动,


1)通过SetSocketOption函数设置发送、接收超时
2)对于收到的字节流,提取其中的sequence,并与发送该包的sequence
作比较,以判断是否对应;具体在我的程序中,是添加了
public UInt16 GetSequenceNum(Byte[] buffer)函数。

测试该程序,(ping函数返回-1表示链路不通,0表示通畅)
开了8个线程ping,可以正常工作,完整代码如下:

【附C#实现ping的其他方法及优缺点】

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


namespace pingConsole
{
    /// <summary>
    /// Class1 的摘要说明。
    /// </summary>
    class Class1
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //
            // TODO: 在此处添加代码以启动应用程序
            //
            string[] target1 = {"127.0.0.1"};
            string[] target2 = {"10.253.0.100"};
//          string[] target3 = {"127.0.0.1"};
//          string[] target4 = {"10.254.0.100"};
//          string[] target5 = {"127.0.0.1"};
//          string[] target6 = {"10.254.0.100"};

            ThreadPing tp1, tp2, tp3, tp4, tp5, tp6, tp7, tp8;
            tp1 = new ThreadPing(target1, 400);
            tp2 = new ThreadPing(target2, 400);
            tp3 = new ThreadPing(target1, 400);
            tp4 = new ThreadPing(target2, 400);
            tp5 = new ThreadPing(target1, 400);
            tp6 = new ThreadPing(target2, 400);
            tp7 = new ThreadPing(target1, 400);
            tp8 = new ThreadPing(target2, 400);

            new Thread(new ThreadStart(tp1.PingWorker)).Start();
            new Thread(new ThreadStart(tp2.PingWorker)).Start();
            new Thread(new ThreadStart(tp3.PingWorker)).Start();
            new Thread(new ThreadStart(tp4.PingWorker)).Start();
            new Thread(new ThreadStart(tp5.PingWorker)).Start();
            new Thread(new ThreadStart(tp6.PingWorker)).Start();
            new Thread(new ThreadStart(tp7.PingWorker)).Start();
            new Thread(new ThreadStart(tp8.PingWorker)).Start();
        }

    }

    public class ThreadPing 
    {
        private string[] _target;
        private int _timeOutVal;

        public ThreadPing(string[] target, int timeOutVal)
        {
            _target = target;
            _timeOutVal = timeOutVal;
        }

        public void PingWorker()
        {
            for(;;)
            {
                int retval= IcmpPacket.PingHost(_target, _timeOutVal);
                Console.WriteLine(_target[0] + " status:" + 
retval.ToString());
                Thread.Sleep(500);
            }
        }
    }

    public class IcmpPacket
    {
        private Byte _type;
        private Byte _subCode;
        private UInt16 _checkSum;
        private UInt16 _identifier;
        private UInt16 _sequenceNumber;
        private Byte[] _data;

        private static UInt16 UInt16_Seq = 0;

        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;
            }
        }

        public int ConverToByte(Byte[] buffer)
        {
            Byte[] b_type = new Byte[1] {_type};
            Byte[] b_subCode = 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_subCode, 0, buffer, i, b_subCode.Length);
            i += b_subCode.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;
        }

        public UInt16 GetSequenceNum(Byte[] buffer)
        {
            UInt16 i_seq = 0;
            try
            {
                 i_seq = BitConverter.ToUInt16(buffer, 26);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
                
            return i_seq;
        }


        public static UInt16 SumofCheck (UInt16[] buffer)
        {
            int sum = 0;
            for (int i=0; i<buffer.Length; i++)
                sum += (int)buffer[i];
            sum = (sum >> 16) + (sum & 0xffff);
            sum += (sum >> 16);

            return (UInt16) (~sum);
        }

        public static int PingHost(string[] hostclient, int timeOutVal)
        {
            const int ICMP_ECHO = 8;
            const int PING_ERR = -1;
            const int TIME_OUT = 200;

            int retval = 0;

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

            sock.SetSocketOption(SocketOptionLevel.Socket, 
SocketOptionName.SendTimeout, TIME_OUT);
            sock.SetSocketOption(SocketOptionLevel.Socket, 
SocketOptionName.ReceiveTimeout, TIME_OUT);

            IPHostEntry hostInfo = null;
            try
            {
                IPAddress ipaddr = IPAddress.Parse(hostclient[0]);
                hostInfo = Dns.GetHostByName(hostclient[0]);
            }
            catch(Exception)
            {
                retval = PING_ERR;
                goto exit;
            }

            EndPoint hostPoint = (EndPoint) new 
IPEndPoint(hostInfo.AddressList[0], 0);

            IPHostEntry clientInfo = null;
            try
            {
                if (hostclient.Length > 1)
                    clientInfo = Dns.GetHostByAddress(hostclient[1]);
                else
                    clientInfo = Dns.GetHostByName(Dns.GetHostName());
            }
            catch (Exception)
            {
                retval = PING_ERR;
                goto exit;
            }

            EndPoint clientPoint = (EndPoint) new 
IPEndPoint(clientInfo.AddressList[0], 0);

            int dataSize = 32;
            int packetSize = dataSize + 8;

            IcmpPacket packet = new IcmpPacket(ICMP_ECHO, 0, 0, 45, 
UInt16_Seq++, dataSize);

            Byte[] buffer = new Byte[packetSize];
            int index = packet.ConverToByte(buffer);
            if (index != packetSize)
            {
                retval = PING_ERR;
                goto exit;
            }

            int count = (int)Math.Ceiling( (Double)index/2);
            UInt16[] buffer2 = new UInt16[count];

            index = 0;
            for (int i=0; i<count; i++)
            {
                buffer2[i] = BitConverter.ToUInt16(buffer, index);
                index += 2;
            }

            packet.CheckSum = IcmpPacket.SumofCheck(buffer2);

            Byte[] sendData = new Byte[packetSize];
            index = packet.ConverToByte(sendData);

            if (index != packetSize)
            {
                retval = PING_ERR;
                goto exit;
            }

            for (int i=0; i<5; i++)
            {
                int nBytes = 0;
                int startTime = Environment.TickCount;

                try
                {
                    if ((nBytes = sock.SendTo(sendData, packetSize, 
SocketFlags.None, (EndPoint)hostPoint)) == -1)
                    {
                        retval = PING_ERR;
                        goto exit;
                    }
//                  Console.WriteLine(nBytes.ToString());
                }
                catch(Exception ex)
                {
                    Console.WriteLine("Send time out");
                    return -1;
                }

                Byte[] receiveData = new Byte[256];
                nBytes = 0;
                int timeout = 0;
                int timeConsume = 0;
                while (true)
                {
                    try
                    {

                        nBytes = sock.ReceiveFrom(receiveData, 256, 
SocketFlags.None, ref (EndPoint)clientPoint);

                        if (nBytes > 0)
                        {
                            if (packet._sequenceNumber == 
packet.GetSequenceNum(receiveData))
                            {
                                retval = 0;
                                goto exit;
                            }
                        }
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        return -1;
                    }
                    if (nBytes == -1)
                    {
                        retval = PING_ERR;
                        goto exit;
                    }
                    else if (nBytes > 0)
                    {
                        timeConsume = System.Environment.TickCount - 
startTime;
                        retval = timeConsume;

                    }

                    timeout = System.Environment.TickCount - startTime;
                    if (timeout > timeOutVal)
                    {
                        retval = PING_ERR;
                        goto exit;
                    }
                
                }
            }

exit:       sock.Close();
            return retval;
            
        }

    }

}

【C#实现ping的其他方法】 

1。调用cmd

这个比较简单,调用系统的command中ping.exe
输入输出重定向,以获取结果字符流,并进行分析

但是由于操作中每次ping需要开关进程操作,运行时间长后
系统资源不足,最终会出现无法进一步开启cmd的局面

2。用c/c++写dll,c#调用

原理也是通过ICMP包收发,只是存在指针,
对包头的数据结构操作比较方便


--
   /  _.-.  .-/,___|  _-| / / //|_/  |    `-._
   `-'  f/ |       / __/ /__  / |__/ |
        `-'    

※ 来源:·饮水思源 bbs.sjtu.edu.cn·[FROM: 211.80.41.99]

[ 上一篇][ 本讨论区][ 下一篇][ 同主题阅读]
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP