How to Perform Multiple “Pings” in Parallel using C#

前端 未结 6 1409
粉色の甜心
粉色の甜心 2020-12-08 11:48

I am trying to calculate the average round-trip time for a collection of servers. In order to speed things up, I would like to perform the pings in parallel. I have writte

相关标签:
6条回答
  • 2020-12-08 12:20

    This is an async worker that can ping multiples endpoints. You can Start() or Stop() the heartbeat worker and suscribe to the following events :

    • PingUp (edge-triggered when and endpoint gets down)
    • PingDown (edge-triggered when and endpoint gets up)
    • PulseStarted
    • PulseEnded
    • PingError

    -

    public class NetworkHeartbeat
    {
        private static object lockObj = new object();
    
        public bool Running { get; private set; }
        public int PingTimeout { get; private set; }
        public int HeartbeatDelay { get; private set; }
        public IPAddress[] EndPoints { get; private set; }
        public int Count => EndPoints.Length;
        public PingReply[] PingResults { get; private set; }
        private Ping[] Pings { get; set; }
    
        public NetworkHeartbeat(IEnumerable<IPAddress> hosts, int pingTimeout, int heartbeatDelay)
        {
            PingTimeout = pingTimeout;
            HeartbeatDelay = heartbeatDelay;
    
            EndPoints = hosts.ToArray();
            PingResults = new PingReply[EndPoints.Length];
            Pings = EndPoints.Select(h => new Ping()).ToArray();
        }
    
        public async void Start()
        {
            if (!Running)
            {
                try
                {
                    Debug.WriteLine("Heartbeat : starting ...");
    
                    // set up the tasks
                    var chrono = new Stopwatch();
                    var tasks = new Task<PingReply>[Count];
    
                    Running = true;
    
                    while (Running)
                    {
                        // set up and run async ping tasks                 
                        OnPulseStarted(DateTime.Now, chrono.Elapsed);
                        chrono.Restart();
                        for (int i = 0; i < Count; i++)
                        {
                            tasks[i] = PingAndUpdateAsync(Pings[i], EndPoints[i], i);
                        }
                        await Task.WhenAll(tasks);
    
                        for (int i = 0; i < tasks.Length; i++)
                        {
                            var pingResult = tasks[i].Result;
    
                            if (pingResult != null)
                            {
                                if (PingResults[i] == null)
                                {
                                    if (pingResult.Status == IPStatus.Success)
                                        OnPingUp(i);
                                }
                                else if (pingResult.Status != PingResults[i].Status)
                                {
                                    if (pingResult.Status == IPStatus.Success)
                                        OnPingUp(i);
                                    else if (PingResults[i].Status == IPStatus.Success)
                                        OnPingDown(i);
                                }
                            }
                            else
                            {
                                if (PingResults[i] != null && PingResults[i].Status == IPStatus.Success)
                                    OnPingUp(i);
                            }
    
                            PingResults[i] = tasks[i].Result;
                            Debug.WriteLine("> Ping [" + PingResults[i].Status.ToString().ToUpper() + "] at " + EndPoints[i] + " in " + PingResults[i].RoundtripTime + " ms");
                        }
    
                        OnPulseEnded(DateTime.Now, chrono.Elapsed);
    
                        // heartbeat delay
                        var delay = Math.Max(0, HeartbeatDelay - (int)chrono.ElapsedMilliseconds);
                        await Task.Delay(delay);
                    }
                    Debug.Write("Heartbeat : stopped");
                }
                catch (Exception)
                {
                    Debug.Write("Heartbeat : stopped after error");
                    Running = false;
                    throw;
                }
            }
            else
            {
                Debug.WriteLine("Heartbeat : already started ...");
            }
        }
    
        public void Stop()
        {
            Debug.WriteLine("Heartbeat : stopping ...");
            Running = false;
        }
    
        private async Task<PingReply> PingAndUpdateAsync(Ping ping, IPAddress epIP, int epIndex)
        {
            try
            {
                return await ping.SendPingAsync(epIP, PingTimeout);
            }
            catch (Exception ex)
            {
                Debug.Write("-[" + epIP + "] : error in SendPing()");
                OnPingError(epIndex, ex);
                return null;
            }
        }
    
        // Event on ping errors
        public event EventHandler<PingErrorEventArgs> PingError;
        public class PingErrorEventArgs : EventArgs
        {
            public int EndPointIndex { get; private set; }
            public Exception InnerException { get; private set; }
    
            public PingErrorEventArgs(int epIndex, Exception ex)
            {
                EndPointIndex = epIndex;
                InnerException = ex;
            }
        }
        private void OnPingError(int epIndex, Exception ex) => PingError?.Invoke(this, new PingErrorEventArgs(epIndex, ex));
    
        // Event on ping Down
        public event EventHandler<int> PingDown;
        private void OnPingDown(int epIndex)
        {
            Debug.WriteLine("# Ping [DOWN] at " + EndPoints[epIndex]);
            PingDown?.Invoke(this, epIndex);
        }
    
        // Event on ping Up
        public event EventHandler<int> PingUp;
        private void OnPingUp(int epIndex)
        {
            Debug.WriteLine("# Ping [UP] at " + EndPoints[epIndex] );
            PingUp?.Invoke(this, epIndex);
        }
    
        // Event on pulse started
        public event EventHandler<PulseEventArgs> PulseStarted;
        public class PulseEventArgs : EventArgs
        {
            public DateTime TimeStamp { get; private set; }
            public TimeSpan Delay { get; private set; }
    
            public PulseEventArgs(DateTime date, TimeSpan delay)
            {
                TimeStamp = date;
                Delay = delay;
            }
        }
        private void OnPulseStarted(DateTime date, TimeSpan delay)
        {
            Debug.WriteLine("# Heartbeat [PULSE START] after " + (int)delay.TotalMilliseconds + " ms");
            PulseStarted?.Invoke(this, new PulseEventArgs(date, delay));
        }
    
        // Event on pulse ended
        public event EventHandler<PulseEventArgs> PulseEnded;
        private void OnPulseEnded(DateTime date, TimeSpan delay)
        {
            PulseEnded?.Invoke(this, new PulseEventArgs(date, delay));
            Debug.WriteLine("# Heartbeat [PULSE END] after " + (int)delay.TotalMilliseconds + " ms");
        }
    } 
    
    0 讨论(0)
  • 2020-12-08 12:22

    use the Parallel.For and a ConcurrentBag

        static void Main(string[] args)
        {
            Console.WriteLine(AverageRoundTripTime("www.google.com", 100));
            Console.WriteLine(AverageRoundTripTime("www.stackoverflow.com", 100));
            Console.ReadKey();
        }
    
        static double AverageRoundTripTime(string host, int sampleSize)
        {
            ConcurrentBag<double> values = new ConcurrentBag<double>();
            Parallel.For(1, sampleSize, (x, y) => values.Add(Ping(host)));
            return values.Sum(x => x) / sampleSize;
        }
        static double Ping(string host)
        {
            var reply = new Ping().Send(host);
            if (reply != null)
                return reply.RoundtripTime;
            throw new Exception("denied");
        }
    
    0 讨论(0)
  • 2020-12-08 12:24

    A solution:

    internal class Utils
    {
        internal static PingReply Ping (IPAddress address, int timeout = 1000, int ttl = 64)
        {
                PingReply tpr = null;
                var p = new Ping ();
                try {
    
                    tpr = p.Send (address,
                        timeout,
                        Encoding.ASCII.GetBytes ("oooooooooooooooooooooooooooooooo"),
                        new PingOptions (ttl, true));
    
                } catch (Exception ex) {
    
                    tpr = null;
    
                } finally {
                    if (p != null)
                        p.Dispose ();
    
                    p = null;
                }
    
                return tpr;
            }
    
            internal static List<PingReply> PingAddresses (List<IPAddress> addresses, int timeout = 1000, int ttl = 64)
            {
                var ret = addresses
                    .Select (p => Ping (p, timeout, ttl))
                    .Where (p => p != null)
                    .Where (p => p.Status == IPStatus.Success)
                    .Select (p => p).ToList ();
    
                return ret;
            }
    
            internal static Task PingAddressesAsync (List<IPAddress> addresses, Action<Task<List<PingReply>>> endOfPing, int timeout = 1000, int ttl = 64)
            {
    
                return Task.Factory.StartNew<List<PingReply>> (() => Utils.PingAddresses (
                    addresses, timeout, ttl)).ContinueWith (t => endOfPing (t));
    
            }   
    }
    

    And using:

    Console.WriteLine ("start");
    
    Utils.PingAddressesAsync (new List<IPAddress> () { 
                        IPAddress.Parse ("192.168.1.1"), 
                        IPAddress.Parse ("192.168.1.13"), 
                        IPAddress.Parse ("192.168.1.49"),
                        IPAddress.Parse ("192.168.1.200")
                    }, delegate(Task<List<PingReply>> tpr) {
    
                        var lr = tpr.Result;
                        Console.WriteLine ("finish with " + lr.Count.ToString () + " machine found");
    
                        foreach (var pr in lr) {
                            Console.WriteLine (pr.Address.ToString ());
            }
    
    });
    
    Console.WriteLine ("execute");
    Console.ReadLine ();
    
    0 讨论(0)
  • 2020-12-08 12:29

    The ping class has a method SendAsync. This follows the Event-based Asynchronous Programming (EAP) pattern. Check out this article: http://msdn.microsoft.com/en-us/library/ee622454.aspx.

    For a quick example here is a method I have that implements that article in a very basic fashion. You can basically call this as many times as you want and all the pings will be done asychronously.

        class Program
        {
        public static string[] addresses = {"microsoft.com", "yahoo.com", "google.com"};
        static void Main(string[] args)
        {
            List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
            foreach (var address in addresses)
            {
                pingTasks.Add(PingAsync(address));
            }
    
            //Wait for all the tasks to complete
            Task.WaitAll(pingTasks.ToArray());
    
            //Now you can iterate over your list of pingTasks
            foreach (var pingTask in pingTasks)
            {
                //pingTask.Result is whatever type T was declared in PingAsync
                Console.WriteLine(pingTask.Result.RoundtripTime);
            }
            Console.ReadLine();
        }
    
        static Task<PingReply> PingAsync(string address)
        {
            var tcs = new TaskCompletionSource<PingReply>();
            Ping ping = new Ping();
            ping.PingCompleted += (obj, sender) =>
                {
                    tcs.SetResult(sender.Reply);
                };
            ping.SendAsync(address, new object());
            return tcs.Task;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 12:44

    // The solution becomes simpler using LINQ

    List<String> hosts = new List<String>();
    for (Int32 i = 0; i < 100; ++i) hosts.Add("www.google.com");
    
    var average = hosts.AsParallel().WithDegreeOfParallelism(64).
                  Select(h => new Ping().Send(h).RoundtripTime).Average();
    
    
    Console.WriteLine(average)
    
    0 讨论(0)
  • 2020-12-08 12:44

    Maybe using SendPingAsync like this:

    using (var ping = new Ping())
    {
        var replies = await Task.WhenAll(hosts.Select(x => ping.SendPingAsync(x)))
                                .ConfigureAwait(false);
                                // false here   ^ unless you want to schedule back to sync context
        ... process replies.
    }
    
    0 讨论(0)
提交回复
热议问题