用ping方式探测网络连通性--一个小程序

问题提出,内网数千台设备,想知道他们是否在线。需然网上很多软件可以实现,但是俺还是想通过自己编程解决,最常用的方法是发ICMP包,看看设备回应。技术要点

  • 如何发ICMP包,接收设备ECHO信息;
  • 采用多线程设计;
  • ECHO信息在数据库持久化;

编程环境

  • Microsoft Visual Studio 2013
  •  Microsoft Windows 7
  • Microsoft .NET FrameWork 4.5

 

俺参考Ping函数 Microsoft 例子,建立一个发ping的函数JobForAThread(object state),传入的参数是数据库表的实例化对象,程序如下:

  staticvoid JobForAThread(object state)

        {

            int pingcount = 3;  //定义ping的次数

            C2011 who = ((C2011)state); //获取数据库实例

            AutoResetEvent waiter = newAutoResetEvent(false); //

            ConnectedFeedback feedback = newConnectedFeedback(who); //一个自定义类,存放返回ECHO数据

            WoodenStick waitcallbackinstance = newWoodenStick(waiter, feedback); //一个自定义类,封装两个类,下面介绍

 

            Ping pingSender = newPing();

            pingSender.PingCompleted+= newPingCompletedEventHandler(PingCompletedCallback); //聆听ping响应,定义处理函数PingCompletedCallback

 

            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //定义发送ping的缓存区

            byte[] buffer = Encoding.ASCII.GetBytes(data);

            int timeout = 5000;  //ping 超时阙值

            PingOptions options = newPingOptions(64, true); // pingOption属性

 

            for (int i = 0; i < pingcount; i++)

            {

               pingSender.SendAsync(who.IP, timeout, buffer, options,waitcallbackinstance);//ping包,将类waitcallbackinstance传递到ping响应处理函数

                waiter.WaitOne(); //ping响应无这么快回来,线程无事干,阻塞等候

            }

 

           feedback.Average(pingcount); //计算多次ping的结果,取平均值

 

            //将结果实例化到CO_PingRecord 对象,准备持久化到数据库

CO_PingRecord copingrecord = newCO_PingRecord();

            copingrecord.IP_Adress =feedback.NetIPaddress.ToString();

           copingrecord.Connectivity = feedback.Connectivity;

           copingrecord.RoundTripTime = (int)feedback.RoundTripTime;

            copingrecord.TTL =feedback.TimeToLive;

            copingrecord.CreatDate =DateTime.Now;

           copingrecord.ConnectivityRate = feedback.rate;

            copingrecord.C2011_ID =feedback.C2011ID;

           

            //定义一个DataContext作为数据库连接

            DBAppDataContext db = newDBAppDataContext();

 

            //使用事务处理数据库数据写入

            using (TransactionScope ts = newTransactionScope())

            {

                try

                {   // 插入一个对象,该对象保存计算ECHO结果

                   db.CO_PingRecords.InsertOnSubmit(copingrecord);

                   db.SubmitChanges();

                    ts.Complete();

                }

                catch (SqlException e)

                {

                    Console.WriteLine(e.Message);

                }

                catch (Exception e)

                {

                    Console.WriteLine(e.Message);

                }

            }

//输出信息

            Console.WriteLine("IP: {0}/Connectivity: {1}/PingStatusValue:{2}",feedback.NetIPaddress, feedback.Connectivity, feedback.rate);

            Console.WriteLine("IP: {0} Ping example completed.DateTime: {1}", feedback.NetIPaddress, DateTime.Now);

//释放资源,不写 .NET也有垃圾回收机制

            pingSender.Dispose();

            db.Dispose();

        }

 

上述提到一个ping处理响应函数PingCompletedCallback,他是这样的

privatestaticvoid PingCompletedCallback(object sender, PingCompletedEventArgs e)

        {

            // 如果操作取消,显示信息.

            if (e.Cancelled)

            {

                Console.WriteLine("Pingcanceled.");

 

                //唤醒委托线程.

                ((AutoResetEvent)((WoodenStick)e.UserState).AutoResetEventMembers).Set();

            }

 

            // 如果发生错误,显示信息.

            if (e.Error != null)

            {

                Console.WriteLine("Pingfailed:");

                Console.WriteLine(e.Error.ToString());

 

                //唤醒委托线程.

                ((AutoResetEvent)((WoodenStick)e.UserState).AutoResetEventMembers).Set();

            }

 

            ConnectedFeedback feedback = (ConnectedFeedback)((WoodenStick)e.UserState).ConnectedFeedbackMembers;

 

            PingReply reply = e.Reply;

            DisplayReply(reply,feedback);

 

            // 唤醒ping发送线程. 使得waiter.WaitOne()被触发,程序继续运行

            ((AutoResetEvent)((WoodenStick)e.UserState).AutoResetEventMembers).Set();

        }

 

数据处理及显示

publicstaticvoid DisplayReply(PingReply reply, ConnectedFeedback feedback)

        {

            if (reply == null)

                return;

            if (reply.Status == IPStatus.Success)

            {// 计算ECHO返回

               feedback.PingFeedbackCalculate(reply);

            }

        }

Ping的函数已经写好,现在轮到多线程执行它,俺使用线程池

DBAppDataContext db = newDBAppDataContext();          

            IEnumerable<C2011> c2012;

            Console.WriteLine("*********Program PingsStart.************");

 

            int nWorkerThreads;

            int nCompletionPortThreads;

            ThreadPool.SetMaxThreads(64, 128);

            ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);

            Console.WriteLine("Max worker threads: {0}, I/Ocompletion" +

                 "threads:{1}",nWorkerThreads, nCompletionPortThreads);

 

            int workerThreads;

            int completionPortThreads;

            int difference;

 

            bool flag = true;

 

            while (flag)

            {

                //立即执行查询,数据库IP是动态变化的,需要每周期取一次;

                c2012 =db.GetTable<C2011>().Where(c => c.IP != "0.0.0.0").ToList();

                //这儿有点奇怪,上述语句可以使c2012刷新数据库增加和删除的记录,但不会刷新更行记录,所以增加这语句

db.Refresh(RefreshMode.OverwriteCurrentValues,c2012);

 

                foreach (var item in c2012)

                {

                    try

                    {

                        //按任意键退出

                        if (Console.KeyAvailable)

                        {

                            flag = false;

                            break;

                        }

                        //主线程不要执行得太快,等一下其他线程。

                        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

                        difference =nWorkerThreads - workerThreads;

                        if (difference >= 10)

                        {

                            Thread.Sleep(2000);

                        }

                        else

                        {

                            Thread.Sleep(500);

                        }

                        Console.WriteLine("***Ping  IP : {0} Start.***", item.IP);

                        //调度线程池的线程处理ping

                        ThreadPool.QueueUserWorkItem(newWaitCallback(JobForAThread), item);

                    }

                    catch (ArgumentNullException e)

                    {

                        Console.WriteLine("ArgumentNullExceptioncaught!!!");

                        Console.WriteLine("Source :" +e.Source);

                        Console.WriteLine("Message :" +e.Message);

                    }

 

                    catch (FormatException e)

                    {

                        Console.WriteLine("FormatExceptioncaught!!!");

                        Console.WriteLine("Source :" +e.Source);

                        Console.WriteLine("Message :" +e.Message);

                    }

 

                    catch (Exception e)

                    {

                        Console.WriteLine("Exceptioncaught!!!");

                        Console.WriteLine("Source :" +e.Source);

                        Console.WriteLine("Message :" + e.Message);

                    }

                }

                c2012 = null;

            }

            Console.WriteLine("***************************************");

            Console.WriteLine("*********Programcompleted.************");

            Console.WriteLine("***************************************");

 

            flag = true;

           //线程池是后台线程,主线程结束前需要等待副线程执行完成。

            while (flag)

            {

                ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

                difference =nWorkerThreads - workerThreads;

                if (difference == 0)

                {

                    flag = false;

                }

                else

                {

                    Console.WriteLine("Wait for EXIT...");

                    Thread.Sleep(1000);

                }

            }

        }

 

上述提到ConnectedFeedback,它是封装Ping命令成功返回累计数据

classConnectedFeedback

    {

        publicIPAddress NetIPaddress;  // pingIP地址

        publicString PingStatus;       // 返回状态

        publicint PingStatusValue;     //返回状态成功计数

        publiclong RoundTripTime;      // 保存RoundTripTime平均值

        publicint TimeToLive;           // 保存TimeToLive平均值

        publicfloat rate;                // 计算成功率值

        publicint Connectivity;         //计算ping是否通

        publicint C2011ID;              // 数据库表C2011ID

 

        privatelong RoundTripTimePrivate;  //返回RoundTripTime

        privateint TimeToLivePrivate;       //返回TimeToLive

 

        public ConnectedFeedback(C2011 _c2011)

        {

            this.NetIPaddress = IPAddress.Parse(_c2011.IP);

            this.C2011ID = _c2011.id;

            this.PingStatus = null;

            this.RoundTripTime = 0;

            this.TimeToLive = 0;

            this.PingStatusValue = 0;

            this.rate = 0.0f;

            this.Connectivity = 2;

        }

 

        #region根据返回成功状态记录返回数据

        ///

        ///方法:根据状态记录ping返回数据

        ///

        ///状态值,RoundTrip时间,TimeToLive值,缓冲区大小

        ///

        publicvoid PingFeedbackCalculate(PingReply reply)

        {

            if (PingStatusdeter(reply))

            {

                PingStatusValue++;

                RoundTripTime =RoundTripTime + RoundTripTimePrivate;

                TimeToLive =TimeToLive + TimeToLivePrivate;

            }

        }

        #endregion

 

//判断ping返回状态

        privatebool PingStatusdeter(PingReply reply)

        {

            if (reply.Status == IPStatus.Success)

            {

                RoundTripTimePrivate= reply.RoundtripTime;

                TimeToLivePrivate =reply.Options.Ttl;

                returntrue;

            }

            else

            {

                returnfalse;

            }

        }

//计算平均值的函数

        publicvoid Average(int totle)

        {

            if (PingStatusValue != 0)

            {

                RoundTripTime =RoundTripTime / PingStatusValue;

                TimeToLive =TimeToLive / PingStatusValue;

                rate = (float)PingStatusValue / totle;

                Connectivity = (rate> 0.33f) ? 1 : 2;

            }

        }

}

 

上述还提到WoodenStick,它是封装AutoResetEvent系统类和ConnectedFeedback自定义类,作为ping异步数据传递

classWoodenStick

    {

        publicAutoResetEvent AutoResetEventMembers { get; set; }

        publicConnectedFeedback ConnectedFeedbackMembers { get; set; }

        public WoodenStick(AutoResetEvent _Event, ConnectedFeedback _Feedback)

        {

            this.AutoResetEventMembers = _Event;

            this.ConnectedFeedbackMembers =_Feedback;

        }

}

 

C#的数据库对象

 用ping方式探测网络连通性--一个小程序_第1张图片

运行

 用ping方式探测网络连通性--一个小程序_第2张图片

 

问题,如果编译在.NETFramework 4.0以下版本,.Net垃圾回收机制不会回收内存,直到内存不足;如果编译在.NET Framework 4.0以上版本(包括.NET Framework 4.0),垃圾回收机制回收内存。

.NET Framework4.0截图

用ping方式探测网络连通性--一个小程序_第3张图片

你可能感兴趣的:(.Net,数据库)