You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步Ping成功时如何触发函数更新XML的最后在线时间?

异步Ping设备并更新XML在线时间的实现方案

我加载了一份包含IP及其他数据的XML文档,使用Ping.SendAsync进行异步Ping操作(因设备数量可能过万,必须异步)。需求是Ping成功时更新XML中对应设备的“Last online”日期,但当前存在两个问题:

  1. 原事件回调方式无法将设备在XML中的标识(如行号、节点)传递给回调函数
  2. 不清楚如何结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:25:29