Ping.SendAsync()总是成功,即使客户端在cmd中不可ping

Tyl*_*ler 6 c# ping task-parallel-library

我尝试使用Ping.SendAsync()方法在C#中ping一些IP地址.我有一个带有5个IP地址的treeView,并为每个节点使用SendAsync()方法.在这里你可以看到:

private void Form1_Load(object sender, EventArgs e)
{
    byte[] buffer = Encoding.ASCII.GetBytes(".");
    PingOptions options = new PingOptions(50, true);
    AutoResetEvent reset = new AutoResetEvent(false);
    Ping ping = new Ping();
    ping.PingCompleted += new PingCompletedEventHandler(ping_Complete);

    foreach (TreeNode node in treeView1.Nodes)
    {
        ping.SendAsync(node.Text, 5000, buffer, options, reset);
    }
}

private void ping_Complete(object sender, PingCompletedEventArgs k)
{
    foreach (TreeNode node in treeView1.Nodes)
    {
        PingReply reply = k.Reply;

        if (reply.Status == IPStatus.Success)
        {
            node.Text = node.Text + " (OK)";
        }

        else
        {
            node.Text = node.Text + " (FAILED)";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,ping总是成功的.我有两个在线和pingable的客户端.其他3个脱机且不可ping(在cmd中我无法ping这些客户端).所以它应该显示:

IP1 (OK)
IP2 (FAILED)
IP3 (FAILED)
IP4 (OK)
IP5 (FAILED)
Run Code Online (Sandbox Code Playgroud)

但输出是"(OK)"的5倍.

有什么建议?:)

Tho*_*que 12

我认为乔恩对你的问题有正确的解释.

我的建议是你改用这个SendPingAsync方法; 它返回一个Task<PingReply>你可以等待的东西(你还需要使你的方法异步):

private async void Form1_Load(object sender, EventArgs e)
{
    byte[] buffer = Encoding.ASCII.GetBytes(".");
    PingOptions options = new PingOptions(50, true);
    AutoResetEvent reset = new AutoResetEvent(false);
    Ping ping = new Ping();
    ping.PingCompleted += new PingCompletedEventHandler(ping_Complete);

    foreach (TreeNode node in treeView1.Nodes)
    {
        var reply = await ping.SendPingAsync(node.Text, 5000, buffer, options, reset);
        if (reply.Status == IPStatus.Success)
        {
            node.Text = node.Text + " (OK)";
        }

        else
        {
            node.Text = node.Text + " (FAILED)";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(请注意,此方法需要.NET 4.5)


正如mike z在评论中指出的那样,上面的方法将连续执行ping,而不是并行执行.如果你想并行执行它们,你可以这样做:

private async void Form1_Load(object sender, EventArgs e)
{
    byte[] buffer = Encoding.ASCII.GetBytes(".");
    PingOptions options = new PingOptions(50, true);
    AutoResetEvent reset = new AutoResetEvent(false);
    Ping ping = new Ping();
    ping.PingCompleted += new PingCompletedEventHandler(ping_Complete);

    var tasks = List<Task>();
    foreach (TreeNode node in treeView1.Nodes)
    {
        var task = PingAndUpdateNodeAsync(ping, node);
        tasks.Add(task);
    }

    await Task.WhenAll(tasks);
}

private async Task PingAndUpdateNodeAsync(Ping ping, TreeNode node)
{
    var reply = await ping.SendPingAsync(node.Text, 5000, buffer, options, reset);
    if (reply.Status == IPStatus.Success)
    {
        node.Text = node.Text + " (OK)";
    }
    else
    {
        node.Text = node.Text + " (FAILED)";
    }
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 8

每次获得任何 PingCompleted事件时,您都会以相同的方式更新树中的所有节点.相反,您应该只更新与特定PingCompletedEventArgs对应的IP地址对应的节点.您可能希望将节点本身用作SendAsync调用中的"state"参数,以便可以在事件处理程序中使用它.

我的猜测是,你要么得到失败,然后通过成功更新所有内容,要么你没有等待足够长的时间来看到失败.

只是为了验证这在诊断上是否正确,我建议你单独记录reply.Status一些不会被覆盖的地方.

此外,您当前正在从非UI线程更新UI,这是一个非常糟糕的主意.您应该在更新之前编组回UI线程.

  • @Tyler:我的意思是你应该只在 UI 线程中执行 `node.Text`。在 Windows 窗体中使用线程有*大量*的资源 - 建议您查看它们。 (2认同)