c# - 当 Ping 往返时间超过 50 毫秒时,如何更改 PictureBox 的图像?

标签 c# .net winforms ping

我正在开发一个可以 ping 多个主机的应用程序。主机列表是从 CSV 文件中读取的。
当有响应时,程序显示绿色勾号,当 ping 失败时显示红色十字。

这很好用,但当往返时间超过 50ms 时,我需要显示第三个图像(如黄色解释标记)。

这是我目前的代码:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(500);
    Parallel.For(0, ipAddress.Count(), (i, loopState) =>
    {
        Ping ping = new Ping();
        PingReply pingReply = ping.Send(ipAddress[i].ToString());
        this.BeginInvoke((Action)delegate ()
        {
            pictureboxList[i].BackgroundImage = (pingReply.Status == IPStatus.Success) ? Image.FromFile(@"C:\Users\PC\Downloads\green.png") : Image.FromFile(@"C:\Users\PC\Downloads\red.png");
        });
    });
}

有办法吗?

最佳答案

使用 List<Task> 的简单示例使用调用者提供的 IP 地址集合(以字符串形式)生成一系列 Ping 请求,并使用 IProgress<T>委托(delegate)来更新 UI(Progress<T> 捕获当前 SynchronizationContext,因此委托(delegate)执行的代码将在初始化它的线程中执行;此处为 UI 线程)。
对于传递给该方法的每个地址,PingAsync()任务已添加到列表中。

PingAsync()方法调用Ping.SendPingAsync()并将结果(成功或失败)报告为可以表示 PingReply 的对象。 ,一个PingExceptionSocketExceptionSocketError 的形式( Progress() 方法将 SocketError 转换为 IPStatus ,以仅处理一种类型的结果。如果您需要更详细/详细的响应,请添加更多案例)。

任务生成一个序列(int 值),该序列发送到 Progress<T>委托(delegate),以备不时之需。在这里,它用于从传递给PingAll()的集合中选择特定的Control。方法。

然后您可以在 Progress<T> 中处理这些结果委托(delegate),查看当前 Ping 请求发生了什么并更新您的控件。

Task.WhenAll()然后等待。当所有任务完成时它将返回。当 Ping 成功或失败或指定的超时时间已过时,任务即完成。

用于显示结果状态的 3 个图像:

  • 绿色 - IPStatus.Success 和 RoundtripTime <= 30
  • 黄色 - IPStatus.Success 和 RoundtripTime > 30
  • 红色 - IPStatus != IPStatus.Success

取自项目资源。最好不要从这里的文件系统获取它们,您可能会引入不必要的复杂性,而没有任何好处。

假设您初始化 MassPing上课并等待PingAll()的结果使用 Button.Click 的处理程序(请注意,处理程序是 async ):

private async void btnMassPing_Click(object sender, EventArgs e)
{
    btnMassPing.Enabled = false;
    // Create a collection of existing Controls that have a BackgroundImage property
    var controls = new Control[] { /* a list of existing Controls */ };
    // The Addresses count must match the Controls'
    var addresses = [An array of strings representing IpAddresses or Host names]
    var massPing = new MassPing();
    await massPing.PingAll(addresses, controls, 2000);
    btnMassPing.Enabled = true;
}

注意:为简单起见,PingAll()方法创建 IProgress<T>全部自行委托(delegate)。您可能更愿意从初始化 MassPing 的过程中将委托(delegate)传递给此方法。类。
这样,您不需要将 Controls 集合传递给该方法。
如果您在 WinForms 应用程序中使用此类并不重要,但如果您想将该类移至库,则确实(或可能)很重要。

using System.Collections.Generic;
using System.Drawing;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;

public class MassPing
{
    private Bitmap imageRed = Properties.Resources.Red;
    private Bitmap imageGreen = Properties.Resources.Green;
    private Bitmap imageYellow = Properties.Resources.Yellow;

    public async Task PingAll(string[] addresses, Control[] controls, uint timeout = 2000)
    {
        // Add more checks on the arguments
        if (addresses.Length != controls.Length) {
            throw new ArgumentException("Collections length mismatch");
        }

        var obj = new object();
        var tasks = new List<Task>();

        var progress = new Progress<(int sequence, object reply)>(report => {
            lock (obj) {
                // Use the reply Status value to set any other Control. In this case, 
                // it's probably better to have a UserControl that shows multiple values
                var status = IPStatus.Unknown;
                if (report.reply is PingReply pr) {
                    status = pr.Status;
                    Bitmap img = status is IPStatus.Success
                        ? pr.RoundtripTime > 30 ? imageYellow : imageGreen
                        : imageRed;
                    controls[report.sequence].BackgroundImage?.Dispose();
                    controls[report.sequence].BackgroundImage = img;
                }
                else if (report.reply is SocketError socErr) {
                    if (socErr == SocketError.HostNotFound) {
                        status = IPStatus.DestinationHostUnreachable;
                    }
                    controls[report.sequence].BackgroundImage?.Dispose();
                    controls[report.sequence].BackgroundImage = imageRed;
                }
            }
        });

        // Add all tasks
        for (int seq = 0; seq < addresses.Length; seq++) {
            tasks.Add(PingAsync(addresses[seq], (int)timeout, seq, progress));
        }
        // Could use some exception handling 
        await Task.WhenAll(tasks);
    }

    private async Task PingAsync(string ipAddress, int timeOut, int sequence, IProgress<(int seq, object reply)> progress)
    {
        var buffer = new byte[32];
        var ping = new Ping();

        try {
            var options = new PingOptions(64, true);
            PingReply reply = await ping.SendPingAsync(ipAddress, timeOut, buffer, options);
            progress.Report((sequence, reply));
        }
        catch (PingException pex) {
            if (pex.InnerException is SocketException socEx) {
                progress.Report((sequence, socEx.SocketErrorCode));
            }
        }
        finally {
            ping.Dispose();
        }
    }
}

关于c# - 当 Ping 往返时间超过 50 毫秒时,如何更改 PictureBox 的图像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68832882/

相关文章:

c# - 如何设置 LUISDialog 转到 .NET 中的“无意图”的最小阈值?

.net - 如何模拟 System.Data.Linq.Table<MyClass>

MySQLCommandBuilder 和外键

c# - 在 Winform 的 Devexpress 网格中选中取消选中复选框列

c# - 如果我有鼠标悬停,我如何获得 div 不透明效果

c# - Telerik Sitefinity 密码哈希函数

c# - 使用 ServiceStack.Text : determine JSON is Array, 对象或字符串?

c# - 在保持图像 C# 的纵横比的同时打印边距

c# - 浏览器成功但 HttpWebRequest 失败(超时)

c# - 向窗口窗体添加水平滚动条