C# Ping delay/slow under Mono

2019-08-15 05:31发布

问题:

I'm experiencing a delay issue with Ping.Send in C# .Net 4.5 running under Mono 3.2.8. My code looks like this:

using(var sw = new StreamWriter("/ping.txt"))
{
    var ping = new Ping();
    PingReply reply;

    sw.WriteLine("Pre ping: {0}", DateTime.Now);
    // Ping local machine
    reply = ping.Send("172.16.1.100", 60);
    sw.WriteLine("Post ping: {0}", DateTime.Now);

    if (reply != null && reply.Status == IPStatus.Success)
    {
        sw.WriteLine("Success! RTT: {0}", reply.RoundtripTime);
    }

    sw.WriteLine("Pre ping: {0}", DateTime.Now);
    // Ping Google
    reply = ping.Send("216.58.220.110", 60);
    sw.WriteLine("Post ping: {0}", DateTime.Now);

    if (reply != null && reply.Status == IPStatus.Success)
    {
        sw.WriteLine("Success! RTT: {0}", reply.RoundtripTime);
    }
}

The output from running the above code under Mono on Linux is:

  1. Pre ping: 03/17/2015 15:43:21
  2. Post ping: 03/17/2015 15:43:41
  3. Success! RTT: 2
  4. Pre ping: 03/17/2015 15:43:41
  5. Post ping: 03/17/2015 15:44:01
  6. Success! RTT: 46

You can see that between the "Pre" and "Post" timestamps, there is a delay of 20 seconds (this is consistent, it's always 20 seconds). The machine running Mono is on the same 172.16.1.* network, I threw the Google ping in there for an extra test.

Running the same code locally on my Windows machine produces the following output (no delay on the pings):

  1. Pre ping: 17/03/2015 3:38:21 PM
  2. Post ping: 17/03/2015 3:38:21 PM
  3. Success! RTT: 3
  4. Pre ping: 17/03/2015 3:38:21 PM
  5. Post ping: 17/03/2015 3:38:21 PM
  6. Success! RTT: 46

Any ideas as to what's going on here? I have the need for pinging hundreds of machines, so a delay of 20 seconds between pings isn't acceptable.

UPDATE: I've tried using the Ping.SendAsync method with the code below:

private void PingAsyncTest()
{
    var ipAddresses = new List<String> { "172.16.1.100", "216.58.220.110" };

    foreach (var ipAddress in ipAddresses)
    {
        using (var ping = new Ping())
        {
            ping.PingCompleted += PingCompleted;
            ping.SendAsync(IPAddress.Parse(ipAddress), 1000);
        }
    }
}

private void PingCompleted(object sender, PingCompletedEventArgs e)
{
    if (e.Reply.Status == IPStatus.Success)
    {
        // Update successful ping in the DB.
    }
}

I'm still seeing the 20 second delay between the SendAsync call and when the reply comes into PingCompleted. This is slightly nicer than the original code where the application would wait the 20 seconds before sending off the next ping. This way all pings are sent and received asynchronously, so there is no need to wait 20 seconds for each ping. Still not ideal though.

回答1:

The way this goes depends very much on how the permissions are set up.

If your application gets enough permissions, it will directly try to send an ICMP request. On the other hand, if it's not allowed to send ICMP, it will run ping executable (trying to find it in /bin/ping, /sbin/ping and /usr/sbin/ping).

First thing you might want to check is which of those actually happens. Does ping execute while you're trying to do the pings? Does it help if you sudo your application?

The default timeout is four seconds, so it shouldn't ever take 20 seconds - you should have gotten a timeout long before that. And you're explicitly passing a timeout of 60 milliseconds.

All this (along with a good look at the code handling pings in Mono) suggests one of those:

  • The 20s are required for the initial setup of the Ping class itself - querying for capabilities, finding ping etc. This obviously isn't the case, since you're trying two pings and each of them takes this long.
  • Most of the time is spent outside of the actual ICMP/ping code. The most likely place being for example Dns.GetHostName or Dns.GetHostAddresses. Check both separately from the ping itself.
  • Some other thread / process is interfering with your own pings. The ICMP socket will get all the ICMP responses, since there's no concept of ports etc. in ICMP.

The last point is also alluding to another issue - if you're trying to ping a lot of different hosts, you really don't want to use Ping, at least not on Linux. Instead, you'll want to ensure your application runs priviledged (enough permissions to do raw ICMP), and handle all the ICMP requests and replies over a single Socket. If you send 100 requests in parallel using Ping.Send, each of those Ping.Sends will have to go through all the replies, not just the one they are expecting. Also, using 60ms as a timeout doesn't sound like a good idea, since the code is using DateTime.Now to check the timeouts, which can have very low timeout resolution.

Instead of sending a request and waiting for a reply, you really want to use asynchronous sockets to send and receive all the time, until you go through all the hosts you want to ping, while checking for the ones where you didn't get a reply in time.



标签: c# .net mono ping