Sooo... I found an instance where the specified timeout with Ping.Send() or Ping.SendAsync do not adhere to the specified timeouts or TTLs and I am trying to work around it. When an invalid host is attempted to be pinged the specified timeout is ignored and in my case a full 2 seconds passes before the ping fails. For the project I am working on I need that to be something like 100 ms or less (if the host was valid it would only be a hop or 2 away so 100 ms seems plenty).
I've tried Ping.Send() and Ping.SendAsync and cannot for the life me figure out how to do this. The closest I got was doing a .Close on the AutoResetEvent but I couldn't catch/suppress the error that was created doing that. (Code not shown here)
Here is the current iteration of the code, as you can see I use both .Send and .SendAsync; I would prefer to use SendAsync I think... If you run the code below everything is executed in under 100ms (usually 60ms) for a host on the same network, but if the host in invalid the code doesn't exit till after 2000ms.
//.NETCore.App\3.1.4
// Microsoft Visual Studio Community 2019 - Version 16.5.5
// Microsoft Windows [Version 10.0.18362.836]
using System;
using System.Text;
using System.Net.NetworkInformation;
using System.Threading;
using System.Text.RegularExpressions;
class Program
{
public static void Main(string[] args)
{
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
string who = String.Empty;
//who = "firewall0"; //Hostname of my local firewall
//who = "www.yahoo.com";
who = "not-a-valid-host";
//who = "10.1.1.151"; //My interface
//who = "localhost"; //Should succeed but error has to be suppressed because of IPv6
//who = "2a03:2880:f131:83:face:b00c:0:25de"; //Facebook IPv6, should fail quickly
//who = "127.0.0.1";
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 1;
int numMaxHops = 1; //Supposedly this is TTL, but after some experimenting it appears to be number of hops not TTL in ms
Ping pingSender = new Ping();
PingOptions options = new PingOptions(numMaxHops, true);
if (true) //Use false to toggle off this section of code while testing
{
AutoResetEvent waiter = new AutoResetEvent(false);
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
Console.WriteLine("Time to live set to: {0}", options.Ttl);
Console.WriteLine("");
pingSender.SendAsync(who, timeout, buffer, options, waiter);
waiter.WaitOne();
Console.WriteLine("Ping example completed.");
}
if (true) //Use false to toggle off this section of code while testing
{
try
{
PingReply reply = pingSender.Send(who, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
if (reply.Address.ToString() != "::1")
{
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
}
}
else
{
Console.WriteLine(reply.Status);
}
}
catch (Exception ex)
{
Regex noHostMatch = new Regex("No such host is known");
if (noHostMatch.IsMatch(ex.InnerException.ToString()))
//if (false)
{
Console.WriteLine("No such host is known.");
}
else
{
throw;
}
}
}
watch.Stop();
Console.WriteLine("");
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
Console.WriteLine("Press the Enter key");
Console.ReadLine();
}//End Main()
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
// If the operation was canceled, display a message to the user.
if (e.Cancelled)
{
Console.WriteLine("Ping canceled.");
// Let the main thread resume.
// UserToken is the AutoResetEvent object that the main thread
// is waiting for.
((AutoResetEvent)e.UserState).Set();
}
// If an error occurred, display the exception to the user.
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
//Console.WriteLine(e.Error.ToString());
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
else
{
PingReply reply = e.Reply;
DisplayReply(reply);
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
}//End PingCompletedCallback()
public static void DisplayReply(PingReply reply)
{
if (reply == null)
return;
Console.WriteLine("Ping Status: {0}", reply.Status);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
if (reply.Address.ToString() != "::1")
{
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
}
}
}//End DisplayReply()
Out out from running the above while pinging my local firewall.
Time to live set to: 1
Ping Status: Success
Address: 10.1.1.1
RoundTrip time: 0
Time to live: 64
Don't fragment: False
Buffer size: 32
Ping example completed.
Address: 10.1.1.1
RoundTrip time: 0
Time to live: 64
Don't fragment: False
Buffer size: 32
Execution Time: 61 ms
Press the Enter key
Output when pinging not-a-valid-host
Time to live set to: 1
Ping failed:
Ping example completed.
No such host is known.
Execution Time: 2139 ms
Press the Enter key
^ I want to get that execution time down to under 100ms with an invalid host.