问题提出,内网数千台设备,想知道他们是否在线。需然网上很多软件可以实现,但是俺还是想通过自己编程解决,最常用的方法是发ICMP包,看看设备回应。技术要点
编程环境
俺参考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); // ping的Option属性
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; // ping的IP地址
publicString PingStatus; // 返回状态
publicint PingStatusValue; //返回状态成功计数
publiclong RoundTripTime; // 保存RoundTripTime平均值
publicint TimeToLive; // 保存TimeToLive平均值
publicfloat rate; // 计算成功率值
publicint Connectivity; //计算ping是否通
publicint C2011ID; // 数据库表C2011的ID
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#的数据库对象
运行
问题,如果编译在.NETFramework 4.0以下版本,.Net垃圾回收机制不会回收内存,直到内存不足;如果编译在.NET Framework 4.0以上版本(包括.NET Framework 4.0),垃圾回收机制回收内存。
.NET Framework4.0截图