Я разрабатываю приложение, которое пингует несколько хостов. Список хостов читается из файла CSV.
Когда есть ответ, программа показывает зеленую галочку, красный крестик, когда пинг не проходит.

Это отлично работает, но мне нужно отобразить третье изображение (например, желтую отметку с пояснением), когда время приема-передачи превышает 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");
        });
    });
}

Как это сделать?

2
Keith 18 Авг 2021 в 15:58

2 ответа

Лучший ответ

Простой пример использования List<Task> для генерации последовательности запросов Ping с использованием коллекции IP-адресов (в форме строк), предоставленных вызывающим пользователем, и использования Делегат IProgress для обновления пользовательского интерфейса (Progress<T> захватывает текущий контекст синхронизации, поэтому код выполняется делегатом, выполняется в потоке, который его инициализировал; здесь поток пользовательского интерфейса).
Для каждого адреса, переданного в метод, в список добавляется задача PingAsync().

Метод PingAsync() вызывает Ping. SendPingAsync () и сообщает результаты, успешные или неудачные, в виде объекта, который может представлять PingReply, a PingException или SocketException в виде SocketError (метод Progress() переводит SocketError в IPStatus, чтобы обрабатывать только один тип результата. Добавьте больше случаев, если вам нужен более подробный / подробный ответ) .

Задачи генерируют последовательность (значение int), которая отправляется делегату Progress<T>, если ему это нужно. Здесь он используется для выбора определенного элемента управления из коллекции, переданной методу PingAll().

Затем вы можете обработать эти результаты в Progress , чтобы узнать, что случилось с текущим запросом 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>. Вместо этого вы можете предпочесть передать делегат этому методу из процедуры, которая инициализирует класс MassPing.
Таким образом, вам не нужно передавать методу коллекцию элементов управления.
Не имеет большого значения, используете ли вы этот класс в приложении 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();
        }
    }
}
2
Jimi 19 Авг 2021 в 06:21

Чтобы ответить на первоначальный вопрос, я думаю, этого должно быть достаточно:

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 ()
            {
                if (pingReply.Status == IPStatus.Success)
                {
                    if (pingReply.RoundtripTime > 50)
                        Image.FromFile(@"C:\Users\PC\Downloads\yellow.png");
                    else
                        Image.FromFile(@"C:\Users\PC\Downloads\green.png");
                }
                else
                {
                    Image.FromFile(@"C:\Users\PC\Downloads\red.png");
                }
            });
        });
    }

Однако не загружайте изображения с диска снова и снова, сохраните их в переменных, чтобы сохранить доступ к диску.

0
KiwiJaune 18 Авг 2021 в 14:26