Why Ping timeout is not working correctly?
Asked Answered
K

3

1

I have 5 pc and i want to ping this pc's are available or no. So I'm using c# Ping class. Two pc are available but the other 3 pc are closed when i ping them my program wait min 7 seconds for response.

I just want to check 1000 miliseconds and returns OK or ERROR...

How can i control ping timeout?

Here my code

        foreach (var item in listofpc)
        {
            Stopwatch timer = Stopwatch.StartNew();
            try
            {
                Ping myPing = new Ping();
                PingReply reply = myPing.Send(ServerName, 500);
                if (reply != null)
                {
                    timer.Stop();
                    TimeSpan timeTaken = timer.Elapsed;
                    Log.append("PING OK TimeTaken="+ timeTaken.ToString() + " Miliseconds", 50);
                }

            }
            catch (Exception ex)
            {
                timer.Stop();
                TimeSpan timeTaken = timer.Elapsed;
                Log.append("PING ERROR  TimeTaken=" +
                   timeTaken.ToString() + " Miliseconds \n" + ex.ToString(), 50);

            }
        }

But when i check my logs I saw response times are 2 seconds. Why ping timeout value is not working?

Any idea?

Krakow answered 2/3, 2018 at 12:43 Comment(13)
Sorry, i deleted my former comment. It was wrong. I was misled by the log message "PING OK" and missed that you actually did not test the ping reply status but rather whether ping.Send returned null. Sorry, my bad :(Somatic
No problem.. Do you have any sugession? @elgonzoKrakow
No, unfortunately not. A little fluctuation is expected with regard to the timeout but not in the order of 4x the original timout period. It really is a bit weird...Somatic
I have run a quick test pinging an unavailable IP address - the timeout appears to have a 500ms resolution - timing out anywhere from the half a second quicker than the specified timeout but never longer than the specified timeout (to the accuracy of Stopwatch). (The quick return is usually the first ping, the rest close to timeout value) Is your log reporting PING OK or PING ERROR. NOTE to find out is the ping was successful you need to check reply.Status.Geraldgeralda
The Ping.Send() has an overload that let's you specify System.Net.NetworkInformation.PingOptions. Keep the TTL to a max of 64 (also try 32) and DontFragment = true. The Buffer must be manually initialized. Use byte[] b = new byte[32];: 32 bytes is the standard. For a timeout of 1000ms, ttl=64 should be fine.Blenheim
@Jimi, I have just tested that, it does not work as OP expects. I have updated my answer to include your suggestion, maybe there is a combination of parameters that works...Freer
I also tried yes it doesn’t worked @BlenheimKrakow
@Jimi, I think it's specific to "hostname not found/unavailable" scenarios. Perhaps some digging into reference sources could shed some light. There might be a legit way to force a timeout, but sometimes blocking calls are blocking calls.Freer
@Krakow If I don't reach the office until Monday, I can try to help by mobile :) battery running low now. Hope you solve your issue. I tested the code I posted, and it behaves as you expect it to!Freer
I'm off now, see you most likely Monday. I just re-read your question and understood the reasoning behind it. If you want to shorten the start time even further, you should really look into doing Ping.SendAsync() or using separate threwads to ping each of the five machines, depending on how the results impact other logic in the rest of the code (unshown).Freer
Just tested what I proposed, and I'm getting consistent results for non-existing/unreachable hosts (inside my network). I'm using framework 4.7.1. I really don't remember if there was a of difference using previous FWs. There could also be something related to how the elapsed time is measured. If you want, I can post the simple test I made.Blenheim
It’s true, the network environment + configuration play a big part in the applicability of a pingoptions solution! Check the links at the bottom of my answer!Freer
Pinging to IP Address like 8.8.8.8 worked absolutely fine, but when pinging to dns address like www.google.com give random time outs, i think these random time outs relates to dns resolution nothing to do with ping time outsPentapody
M
5

UPDATE: The Why: The most probable reason for the Ping timeout not working, as stated by others as well, is DNS resolution. The system call to getaddrinfo (the one used by Dns.GetHostAddresses and Ping - https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo, https://learn.microsoft.com/en-us/dotnet/api/system.net.dns.gethostaddresses?view=netcore-3.1 - similar for Full Framework) does not accept timeouts. As such, a further improvement to the code below would be to separate the dns lookup from the pinging. Do the lookup first with a timeout approach similar to the code below, and only ping IPs rather than host names, with a specified timeout.

I've run into similar issues in the past, and I have some code that might help in working around this issue. I am editing it here, so it might be less than 100% correct as is, and a bit more complicated than your needs. Can you try something like this?

The hammer: (full code with test results also included below)

private static PingReply ForcePingTimeoutWithThreads(string hostname, int timeout)
{
    PingReply reply = null;
    var a = new Thread(() => reply =  normalPing(hostname, timeout));
    a.Start();
    a.Join(timeout); //or a.Abort() after a timeout, but you have to take care of a ThreadAbortException in that case... brrr I like to think that the ping might go on and be successful in life with .Join :)
    return reply;
}

private static PingReply normalPing(string hostname, int timeout)
{
   try
   {
      return new Ping().Send(hostname, timeout);
   }
   catch //never do this kids, this is just a demo of a concept! Always log exceptions!
   {
      return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
    }
 }

Here is a full working sample (Tasks.WhenAny tested and working in version 4.5.2). I also learned that the elegance of Tasks comes at a more significant performance hit than I remember, but Thread.Join/Abort are too brutal for most production environments.

using System;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        //this can easily be async Task<PingReply> or even made generic (original version was), but I wanted to be able to test all versions with the same code
        private static PingReply PingOrTimeout(string hostname, int timeOut)
        {
            PingReply result = null;
            var cancellationTokenSource = new CancellationTokenSource();
            var timeoutTask = Task.Delay(timeOut, cancellationTokenSource.Token);

            var actionTask = Task.Factory.StartNew(() =>
            {
                result = normalPing(hostname, timeOut);
            }, cancellationTokenSource.Token);

            Task.WhenAny(actionTask, timeoutTask).ContinueWith(t =>
            {
                cancellationTokenSource.Cancel();
            }).Wait(); //if async, remove the .Wait() and await instead!
            
            return result;
        }

        private static PingReply normalPing(string hostname, int timeout)
        {
            try
            {
                return new Ping().Send(hostname, timeout);
            }
            catch //never do this kids, this is just a demo of a concept! Always log exceptions!
            {
                return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
            }
        }

        private static PingReply ForcePingTimeoutWithThreads(string hostname, int timeout)
        {
            PingReply reply = null;
            var a = new Thread(() => reply =  normalPing(hostname, timeout));
            a.Start();
            a.Join(timeout); //or a.Abort() after a timeout... brrr I like to think that the ping might go on and be successful in life with .Join :)
            return reply;
        }

        static byte[] b = new byte[32];
        static PingOptions po = new PingOptions(64, true);
        static PingReply JimiPing(string hostname, int timeout)
        {
            try
            {
                return new Ping().Send(hostname, timeout, b, po);
            }
            catch //never do this kids, this is just a demo of a concept! Always log exceptions!
            {
                return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
            }
        }

        static void RunTests(Func<string, int, PingReply> timeOutPinger)
        {
            var stopWatch = Stopwatch.StartNew();
            var expectedFail = timeOutPinger("bogusdjfkhkjh", 200);
            Console.WriteLine($"{stopWatch.Elapsed.TotalMilliseconds} false={expectedFail != null}");
            stopWatch = Stopwatch.StartNew();
            var expectedSuccess = timeOutPinger("127.0.0.1", 200);
            Console.WriteLine($"{stopWatch.Elapsed.TotalMilliseconds} true={expectedSuccess != null && expectedSuccess.Status == IPStatus.Success}");
        }

        static void Main(string[] args)
        {
            RunTests(normalPing);
            RunTests(PingOrTimeout);
            RunTests(ForcePingTimeoutWithThreads);
            RunTests(JimiPing);
            
            Console.ReadKey(false);
        }
    }
}

Some results from my testing:

>Running ping timeout tests timeout = 200. method=normal
>
> - host: bogusdjfkhkjh elapsed: 2366,9714 expected: false=False
> - host: 127.0.0.1 elapsed: 4,7249 expected: true=True
>
>Running ping timeout tests timeout = 200. method:ttl+donotfragment (Jimi)
>
> - host: bogusdjfkhkjh elapsed: 2310,836 expected: false actual: False
> - host: 127.0.0.1 elapsed: 0,7838 expected: true actual: True
>
>Running ping timeout tests timeout = 200. method:tasks
>
> - host: bogusdjfkhkjh elapsed: 234,1491 expected: false actual: False
> - host: 127.0.0.1 elapsed: 3,2829 expected: true=True
>
>Running ping timeout tests timeout = 200. method:threads
>
> - host: bogusdjfkhkjh elapsed: 200,5357 expected: false actual:False
> - host: 127.0.0.1 elapsed: 5,5956 expected: true actual: True

Caution For the Tasks version, even if the calling thread is "unblocked", the action itself, in this case the ping, might linger until it actually times out. That is why I suggest putting in a timeout for the ping command itself as well.

UPDATE Researching the why too, but thought a workaround would help you for now.

New findings:

Millicent answered 2/3, 2018 at 14:48 Comment(22)
I want to try it but when i copy and paste i saw Task has no Delay() function and whenany()Krakow
Add using to System.Threading.Tasks I think (sent from iPhone) will reach home in 1hr and I can help you to get this running!Freer
I already add that but i dont see instance anyway i’m waiting your help thanks @AlexC.Krakow
Ok I am at home. now forgot laptop charger at work so let's move fast. What framework version are you targeting? @Krakow A similar approach can be done with threads rather than Tasks and async/await, but it would lose some of the beauty in my opinion :)Freer
I edited my answer to include a full working sample targeting version 4.5.2Freer
But i’m using 4.0 so i dont have Task.Delay()Krakow
My target version is 4.0Krakow
@Krakow Just remove the code for the Tasks version in my code then. The Threads approach should work just fine.Freer
@Krakow the "minimal" methods you want from my sample are normalPing and ForcePingTimeoutWithThreads.Freer
You can't possibly have those results pinging the loopback address. The elapsed time must always be 10~15ms. There's something off with the time measure.Blenheim
@Jimi: Huh? The results are <15 ms, in fact <6 for loop back in all cases! In .ro Culture comma is the fraction separator, and we do not have a thousands separator, maybe that confused you.Freer
So, "0,7838" means 0.7838 milli-seconds? What measure is that? (btw, with 10~15ms I meant "a max of". The Max got lost in translation).Blenheim
Exactly, less than 1 ms. I should have written the units too tough on mobile :)))Freer
Measured with stopwatch as seen in code. Btw, never use Timer for such measures, too inaccurate #31293616. #1486339Freer
First thing. I saw this by chance. You know you have to prefix a nick with @ to address someone? Second, who's using a Timer other than StopWatch? Never said that. Third, You are using Stopwatch.Elapsed.TotalMillisenconds, right? So, this is MSDN => This property represents elapsed time rounded down to the nearest whole millisecond value. And you are measuring a value below a ms. => 0.7838. How is that? Fourth, the Jimi's code doesn't look like the one I've been using for years. And that is returning after the Timeout I've set, all the time.Blenheim
@jimi can you please add an answer with your code or edit my answer with the actual code you use? About Stopwatch, see OP's question, he uses Timer. I knew about the tagging, but forgot to, also it does not seem to always work. Be careful, the MSDN you are probably mentioning is for Stopwatch.ElapsedMilliseconds not Stowatch.Elapsed.TotalMilliseconds, two different animals. There is no rounding for the latter!Freer
@jimi real MSDN for the property I use is: msdn.microsoft.com/en-us/library/…, since sw.Elapsed is a TimeSpanFreer
@jimi Again, apologies if the code does not look like what you described, I tried to follow your guidance for setting the parametersFreer
No need to apologize. It's a talk between "colleagues". If it comes out that, in some circumstances/environments, what I think I've tested enough and find reliable, is less obvious, then I've learnt something and I'm good with it. Btw, you're right about the TotalMillisenconds. I use ElapsedMilliseconds. When I've a moment, I'll post the two methods (sync and async) that I use (part of a library, there's logging and other stuff, I have to edit it) so you can test them. (I will not post this as an answer, I will delete it after a while. You can take what you want if you think is useful).Blenheim
@jimi that would be nice! I am curious to see! I've had to do a lot of editing myself, the production code for the async waitforaction is much more involved :)Freer
Ping give Accurate time out if used with IpAddress Like 8.8.8.8 but give random time out if used with dns addresses like www.google.com, this is the issue with dns resolution not with the ping timeoutsPentapody
@MuhammadWaqasAziz DNS resolution which is implicitly done by PingFreer
B
2

This implementation of System.Net.NetworkInformation.Ping has been tested with
Frameworks 4.0/4.5.1/4.7.1, Console and Winforms versions.

The results are always the same (as reported below).

This is the .NET Framework Ping() implementation of IcmpSendEcho2 and Icmp6SendEcho2

Synchronous version (Output Type is Console, but it's not relevant):

(The original version of this method does not return IPStatus. It returns a Class Object with full exception information. The Host Name or address is verified/translated with the DNS resolver:
IPAddress _HostIPAddress = Dns.GetHostAddresses(HostAddress).First(); which throws a SocketException if the Host is not found and a No such host is known notification.
The Result: BadDestination for an unknown host is set here just for this test).

static void Main(string[] args)
{
    List<string> HostAdrr = new List<string>() { "192.168.2.1", "192.168.2.201", 
                                                 "192.168.1.99", "200.1.1.1", 
                                                 "www.microsoft.com", "www.hfkhkhfhkf.com" };
    IPStatus _result;;
    foreach (string _Host in HostAdrr)
    {
        Stopwatch timer = Stopwatch.StartNew();
        _result = PingHostAddress(_Host, 1000);
        timer.Stop();
        Console.WriteLine("Host: {0}  Elapsed time: {1}ms  Result: {2}", _Host, timer.ElapsedMilliseconds, _result);
        Console.WriteLine();
    }
    Console.ReadLine();
}

public static IPStatus PingHostAddress(string HostAddress, int timeout)
{
    if (string.IsNullOrEmpty(HostAddress.Trim()))
        return IPStatus.BadDestination;

    byte[] buffer = new byte[32];
    PingReply iReplay = null;
    using (Ping iPing = new Ping())
    {
        try
        {
            //IPAddress _HostIPAddress = Dns.GetHostAddresses(HostAddress).First();
            iReplay = iPing.Send(HostAddress,
                                    timeout,
                                    buffer,
                                    new PingOptions(64, true));
        }
        catch (FormatException)
        {
            return IPStatus.BadDestination;
        }
        catch (NotSupportedException nsex)
        {
            throw nsex;
        }
        catch (PingException pex)
        {
            //Log/Manage pex
        }
        //catch (SocketException soex)
        //{
        //
        //}
        catch (Exception ex)
        {
            //Log/Manage ex
        }
        return (iReplay != null) ? iReplay.Status : IPStatus.BadDestination;
    }
}

The asynchronous version uses the .SendPingAsync() method and has the usual Async signature.

public async Task<IPStatus> PingHostAddressAsync(string HostAddress, int timeout)
{
    //(...)
    iReplay = await iPing.SendPingAsync(HostAddress,
                                        timeout,
                                        buffer,
                                        new PingOptions(64, false));
    //(...)
}

The results don't change when the async version is used. Tested with Winforms. No matter how much one tries to mess with the UI.

How to interpret the results:

Parameters:
- Host Name resolved by the Ping.Send() method. (Resolving the Host Name beforehand doesn't change the results)
- Timeout: 1000ms (also tested 500ms and 2000ms)
- Buffer: Standard 32 Bytes
- TTL: 64
- Do Not Fragment: True

Host: 192.168.2.1 => Reachable Host in the same network.
Host: 192.168.2.201 => Unreachable (off) Host in a reachable different subnet.
Host: 192.168.1.99 => Non existent Host in a reachable different network (hardware routed)
Host: 200.1.1.1 => Non existent Internet Address
Host: www.microsoft.com => Reachable existing resolved Internet Host Name
Host: www.hfkhkhfhkf.com => Non existent unresolvable Internet Host Name

Host: 192.168.2.1  Elapsed time: 4  Result: Success

Host: 192.168.2.201  Elapsed time: 991  Result: TimedOut

Host: 192.168.1.99  Elapsed time: 993  Result: TimedOut

Host: 200.1.1.1  Elapsed time: 997  Result: TimedOut

Host: www.microsoft.com  Elapsed time: 57  Result: Success

Host: www.hfkhkhfhkf.com  Elapsed time: 72  Result: BadDestination


As also noted by @PaulF in the comments, the only (persistent) anomaly is the first response if the Host is unreachable: it's always a bit shorter than the imposed timeout. But the Timeout is always respected (the Ping method always returns within the set time interval).

Blenheim answered 5/3, 2018 at 15:12 Comment(7)
Have you tried with "hfkhkhfhkf" rather than "www.hfkhkhfhkf.com"?Freer
I still get 2 second with this code, setting timeout to 200 for non-existent hostnames in the format described above. Tested on 3 separate networks.Freer
Well, the first "Address" won't pass the IP parser. Will return "BadHostAddress". About the 2 seconds. Always 2 seconds? That's peculiar. Have you copied the code as it is? To be on the same page, I mean. If you have, try enabling Network Tracing to see what comes out. Also, try to manually set (if possible) the Google DNS servers in the internet exposed router/dsl/hub/hug (or even locally if that's not an option). A very slow DNS response might actually cause troubles here.Blenheim
"hgfdhgf" is a perfectly valid hostname :) I think there might be some backward compatibility/fallbacks going on with such format. I suspect that is what OP is running intoFreer
Well not always exactly 2s, but always above a 1000 timeoutFreer
Yes, it's a perfectly valid local Machine Name. In the networks I can test, it returns immediately empty handed. But if a DNS server is set to recurse, it tries to reverse lookup the address (doesn't answer PTR queries). The results are impredictable. -- Do you have those results no matter what you pass to the Ping method, IP Address and Host Name in the local network? If that's so, I have no explanation for it at the moment. It shouldn't happen (and I don't remember having seen it).Blenheim
:) exactly, unpredictable. I don’t know why either, it is network specific... My motto: if brute force does not let work , you are probably not using enough of it! I mean if a blocking call does not respect its given timeout, I work around it, too much work to see what the ma code does different in my tests vs yours with the exact same sourcecode. That’s why I made the waitactionortimeout code, for these casesFreer
P
1

Pinging to IP Address like 8.8.8.8 worked absolutely fine, but when pinging to dns address like www.google.com give random time outs.
i think these random time outs relates to dns resolution nothing to do with ping time outs

    private static bool DoPing()
    {
        try
        {
            using (System.Net.NetworkInformation.Ping ping = new Ping())
            {

                PingReply result = ping.Send("8.8.8.8", 500, new byte[32], new PingOptions { DontFragment = true, Ttl = 32 });

                if (result.Status == IPStatus.Success)
                    return true;
                return false;
            }
        }
        catch
        {
            return false;
        }
    }
Pentapody answered 11/12, 2019 at 9:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.