异步Ping成功时如何触发函数更新XML的最后在线时间?
异步Ping设备并更新XML在线时间的实现方案
我加载了一份包含IP及其他数据的XML文档,使用Ping.SendAsync进行异步Ping操作(因设备数量可能过万,必须异步)。需求是Ping成功时更新XML中对应设备的“Last online”日期,但当前存在两个问题:
- 原事件回调方式无法将设备在XML中的标识(如行号、节点)传递给回调函数
- 不清楚如何结合
await来简化异步处理流程
原代码如下:
public int ping(string ip) { //test if IP is valid System.Net.IPAddress ipAddress = null; bool isValidIp = System.Net.IPAddress.TryParse(ip, out ipAddress); if (!isValidIp) { Console.WriteLine("Invalid IP"); return 0; } AutoResetEvent waiter = new AutoResetEvent(false); Ping pingSender = new Ping(); // When the PingCompleted event is raised, // the PingCompletedCallback method is called. // it is essentially used for error checking after the ping is completed pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback); //Create buffer of 32 bytes of data to be transmitted string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; byte[] buffer = Encoding.ASCII.GetBytes(data); //wait 12 seconds for reply int timeout = 12000; //Options for transmission //first one is number of nodes //second is whether the packet cant be fragmented PingOptions options = new PingOptions(64, true); //Console.WriteLine("Time to live: {0}", options.Ttl); //Console.WriteLine("Don't fragment: {0}", options.DontFragment); //Send ping asynchronously, when the callback completes it wakes up this thread pingSender.SendAsync(ip, timeout, buffer, options, waiter); return 1; }
回调函数:
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e) { //if ping canceled, write to console if (e.Cancelled) { Console.WriteLine("Ping canceled."); // Let the main thread resume. // UserToken is the AutoResetEvent object that the main thread // is waiting for. ((AutoResetEvent)e.UserState).Set(); } //If an error occured, write the exception to the console if (e.Error != null) { Console.WriteLine("Ping failed:"); Console.WriteLine(e.Error.ToString()); //Let main thread resume ((AutoResetEvent)e.UserState).Set(); } PingReply reply = e.Reply; DisplayReply(reply); }
方案1:使用async/await + SendPingAsync(推荐)
Ping类提供了支持async/await的SendPingAsync方法,无需事件回调,可直接在方法内处理Ping结果,自然能保留当前设备的XML上下文信息,代码更简洁易维护,也更适合批量异步操作场景。
修改后的异步Ping方法示例
// 假设你已实现更新XML的方法,传入设备在XML中的标识(如行号)即可更新Last online字段 private void UpdateLastOnline(int xmlLineNumber) { // 这里编写更新对应行"Last online"为当前时间的逻辑 } public async Task PingAndUpdateAsync(string ip, int xmlLineNumber) { System.Net.IPAddress ipAddress = null; if (!System.Net.IPAddress.TryParse(ip, out ipAddress)) { Console.WriteLine($"Invalid IP: {ip}"); return; } using (Ping pingSender = new Ping()) { string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; byte[] buffer = Encoding.ASCII.GetBytes(data); int timeout = 12000; PingOptions options = new PingOptions(64, true); try { PingReply reply = await pingSender.SendPingAsync(ip, timeout, buffer, options); // 判断Ping是否成功 if (reply.Status == IPStatus.Success) { // 直接调用更新方法,传入预先保存的XML行号 UpdateLastOnline(xmlLineNumber); Console.WriteLine($"IP {ip} Ping成功,已更新在线时间"); } else { Console.WriteLine($"IP {ip} Ping失败,状态:{reply.Status}"); } } catch (Exception ex) { Console.WriteLine($"IP {ip} Ping出错:{ex.Message}"); } } }
使用方式
遍历XML中的设备时,直接调用异步方法即可,无需阻塞主线程:
// 示例:遍历XML设备节点 foreach (XmlNode deviceNode in xmlDocument.SelectNodes("//Devices/Device")) { string ip = deviceNode.Attributes["IP"].Value; // 假设行号存储在节点属性中,或通过遍历索引记录 int lineNumber = int.Parse(deviceNode.Attributes["LineNumber"].Value); // 异步执行,不阻塞后续遍历 _ = PingAndUpdateAsync(ip, lineNumber); }
方案2:改进事件回调,传递上下文信息
如果坚持使用原有的SendAsync事件回调方式,可以通过userToken参数传递包含XML上下文的自定义对象,解决回调无法获取设备标识的问题。
1. 定义上下文类
封装需要传递的信息(XML标识、等待事件等):
public class PingContext { public AutoResetEvent Waiter { get; set; } public int XmlLineNumber { get; set; } public string IpAddress { get; set; } }
2. 修改Ping方法,传递自定义上下文
public int Ping(string ip, int xmlLineNumber) { System.Net.IPAddress ipAddress = null; bool isValidIp = System.Net.IPAddress.TryParse(ip, out ipAddress); if (!isValidIp) { Console.WriteLine($"Invalid IP: {ip}"); return 0; } Ping pingSender = new Ping(); pingSender.PingCompleted += PingCompletedCallback; string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; byte[] buffer = Encoding.ASCII.GetBytes(data); int timeout = 12000; PingOptions options = new PingOptions(64, true); // 创建上下文对象,包含行号和等待事件 var pingContext = new PingContext { Waiter = new AutoResetEvent(false), XmlLineNumber = xmlLineNumber, IpAddress = ip }; pingSender.SendAsync(ip, timeout, buffer, options, pingContext); return 1; }
3. 修改回调函数,获取上下文并处理
private void PingCompletedCallback(object sender, PingCompletedEventArgs e) { var pingContext = e.UserState as PingContext; if (pingContext == null) { Console.WriteLine("无法获取Ping上下文"); return; } try { if (e.Cancelled) { Console.WriteLine($"IP {pingContext.IpAddress} Ping canceled."); return; } if (e.Error != null) { Console.WriteLine($"IP {pingContext.IpAddress} Ping failed:"); Console.WriteLine(e.Error.ToString()); return; } PingReply reply = e.Reply; DisplayReply(reply); // 判断Ping成功,更新XML if (reply.Status == IPStatus.Success) { UpdateLastOnline(pingContext.XmlLineNumber); Console.WriteLine($"IP {pingContext.IpAddress} Ping成功,已更新在线时间"); } } finally { // 释放资源 pingContext.Waiter.Set(); pingContext.Waiter.Dispose(); ((Ping)sender).Dispose(); } }
内容的提问来源于stack exchange,提问作者Karker Carnesir
相关产品推荐
相关产品推荐

