Skip to content

Instantly share code, notes, and snippets.

@guitarrapc
Last active March 17, 2017 09:31
Show Gist options
  • Save guitarrapc/5eba8a5467c26663c9b8 to your computer and use it in GitHub Desktop.
Save guitarrapc/5eba8a5467c26663c9b8 to your computer and use it in GitHub Desktop.
Asynchronous Ping/DNSResolver by C#. Also PowerShell OnTheFly compile Asynchronous Ping
void Main()
{
var computerName = Enumerable.Range(1, 250).Select(x => "192.10.10." + x).ToArray();
// var computerName = Util.Cache(() => Enumerable.Range(1, 254).SelectMany(i => Enumerable.Range(10, 4).SelectMany(j => new[] { $"192.10.{j}.{i}" })).ToArray());
var sw = Stopwatch.StartNew();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromMilliseconds(1), false).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = false, PingTimeout = 1");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, false).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = false, PingTimeout = 10");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromMilliseconds(100), false).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = false, PingTimeout = 100");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(10)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 10, DnsTimeOut = 10");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 10, DnsTimeOut = 20");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromMilliseconds(20), TimeSpan.FromMilliseconds(20)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 20, DnsTimeOut = 20");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromMilliseconds(20), TimeSpan.FromSeconds(1)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 20, DnsTimeOut = 1000");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 1000, DnsTimeOut = 1000");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 2000, DnsTimeOut = 1000");
sw.Restart();
NetworkInformationExtensions.PingAsync(computerName, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)).Result.Where(x => x.Status == IPStatus.Success);
sw.Elapsed.TotalMilliseconds.Dump("Resolve = true, PingTimeout = 1000, DnsTimeOut = 2000");
}
public class DnsResponse
{
public string HostName { get; private set; }
public IPAddress IPAddress { get; private set; }
public DnsResponse(string hostName, IPAddress ip)
{
this.HostName = hostName;
this.IPAddress = ip;
}
}
public class DnsResolver
{
public static DnsResponse ResolveIP(IPAddress ip, TimeSpan timeout)
{
Func<IPAddress, IPHostEntry> callback = s => Dns.GetHostEntry(s);
var result = callback.BeginInvoke(ip, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout, false))
{
return new DnsResponse(ip.ToString(), ip);
}
var hostEntry = callback.EndInvoke(result);
return new DnsResponse(hostEntry.HostName, ip);
}
public static DnsResponse ResolveHostName(string hostNameOrAddress, TimeSpan timeout)
{
Func<string, IPHostEntry> callback = s => Dns.GetHostEntry(s);
var result = callback.BeginInvoke(hostNameOrAddress, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout, false))
{
return new DnsResponse(hostNameOrAddress, null);
}
var hostEntry = callback.EndInvoke(result);
var ip = hostEntry.AddressList.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork);
return new DnsResponse(hostNameOrAddress, ip);
}
}
public class PingResponse
{
public string HostNameOrAddress { get; set; }
public IPAddress IPAddress { get; set; }
public IPStatus Status { get; set; }
public bool IsSuccess { get; set; }
public long RoundTripTime { get; set; }
public bool IsResolved { get; set; }
}
public class NetworkInformationExtensions
{
private static readonly byte[] _buffer = new byte[16];
private static readonly PingOptions _options = new PingOptions(64, false);
private static readonly TimeSpan _pingTimeout = TimeSpan.FromMilliseconds(10);
private static readonly TimeSpan _dnsTimeout = TimeSpan.FromMilliseconds(20);
private static bool _resolveDns = true;
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress)
{
return await PingAsync(hostNameOrAddress, _pingTimeout, _resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout)
{
return await PingAsync(hostNameOrAddress, pingTimeout, _resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, bool resolveDns)
{
return await PingAsync(hostNameOrAddress, _pingTimeout, resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, bool resolveDns)
{
return await PingAsync(hostNameOrAddress, pingTimeout, resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, TimeSpan dnsTimeout)
{
return await PingAsync(hostNameOrAddress, pingTimeout, _resolveDns, _dnsTimeout);
}
private static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, bool resolveDns, TimeSpan dnsTimeout)
{
var pingResult = await Task.WhenAll(hostNameOrAddress.Select(async x =>
{
// Resolve only when incoming is HostName.
IPAddress ip = null;
DnsResponse resolve = null;
var isIpAddress = IPAddress.TryParse(x, out ip);
if (!isIpAddress)
{
resolve = DnsResolver.ResolveHostName(x, dnsTimeout);
ip = resolve.IPAddress;
}
// Execute PingAsync
PingReply reply = null;
using (var ping = new Ping())
{
try
{
reply = await ping.SendPingAsync(ip, (int)pingTimeout.TotalMilliseconds, _buffer, _options);
}
catch
{
// ping throw should never stop operation. just return null.
}
}
// set RoundtripTime
long roundTripTime = 0;
if (reply != null) roundTripTime = reply.RoundtripTime;
// set Status
var status = IPStatus.DestinationHostUnreachable;
if (reply != null) status = reply.Status;
// set IsSuccess
var isSuccess = status == IPStatus.Success;
// return when PingFailed || HostName || OmitResolveDns
if (!isSuccess || !isIpAddress || !resolveDns)
return new PingResponse
{
HostNameOrAddress = x,
IPAddress = ip,
Status = status,
RoundTripTime = roundTripTime,
IsSuccess = isSuccess,
IsResolved = resolve != null,
};
// Resolve Dns only for success host entry.
var host = x;
resolve = DnsResolver.ResolveIP(ip, dnsTimeout);
if (resolve != null) host = resolve.HostName;
return new PingResponse
{
HostNameOrAddress = host,
IPAddress = ip,
Status = status,
RoundTripTime = roundTripTime,
IsSuccess = true,
IsResolved = resolve != null,
};
}).ToArray());
return pingResult;
}
}
$asm = "System", "System.Net", "System.Linq", "System.Threading.Tasks", "System.Net.NetworkInformation"
$source = @"
using System;
using System.Net;
using System.Threading.Tasks;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace PingEx
{
public class DnsResponse
{
public string HostName { get; private set; }
public IPAddress IPAddress { get; private set; }
public DnsResponse(string hostName, IPAddress ip)
{
this.HostName = hostName;
this.IPAddress = ip;
}
}
public class DnsResolver
{
public static DnsResponse ResolveIP(IPAddress ip, TimeSpan timeout)
{
Func<IPAddress, IPHostEntry> callback = s => Dns.GetHostEntry(s);
var result = callback.BeginInvoke(ip, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout, false))
{
return new DnsResponse(ip.ToString(), ip);
}
var hostEntry = callback.EndInvoke(result);
return new DnsResponse(hostEntry.HostName, ip);
}
public static DnsResponse ResolveHostName(string hostNameOrAddress, TimeSpan timeout)
{
Func<string, IPHostEntry> callback = s => Dns.GetHostEntry(s);
var result = callback.BeginInvoke(hostNameOrAddress, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout, false))
{
return new DnsResponse(hostNameOrAddress, null);
}
var hostEntry = callback.EndInvoke(result);
var ip = hostEntry.AddressList.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork);
return new DnsResponse(hostNameOrAddress, ip);
}
}
public class PingResponse
{
public string HostNameOrAddress { get; set; }
public IPAddress IPAddress { get; set; }
public IPStatus Status { get; set; }
public bool IsSuccess { get; set; }
public long RoundTripTime { get; set; }
public bool IsResolved { get; set; }
}
public class NetworkInformationExtensions
{
private static readonly byte[] _buffer = new byte[16];
private static readonly PingOptions _options = new PingOptions(64, false);
private static readonly TimeSpan _pingTimeout = TimeSpan.FromMilliseconds(10);
private static readonly TimeSpan _dnsTimeout = TimeSpan.FromMilliseconds(20);
private static bool _resolveDns = true;
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress)
{
return await PingAsync(hostNameOrAddress, _pingTimeout, _resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout)
{
return await PingAsync(hostNameOrAddress, pingTimeout, _resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, bool resolveDns)
{
return await PingAsync(hostNameOrAddress, _pingTimeout, resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, bool resolveDns)
{
return await PingAsync(hostNameOrAddress, pingTimeout, resolveDns, _dnsTimeout);
}
public static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, TimeSpan dnsTimeout)
{
return await PingAsync(hostNameOrAddress, pingTimeout, _resolveDns, _dnsTimeout);
}
private static async Task<PingResponse[]> PingAsync(string[] hostNameOrAddress, TimeSpan pingTimeout, bool resolveDns, TimeSpan dnsTimeout)
{
var pingResult = await Task.WhenAll(hostNameOrAddress.Select(async x =>
{
// Resolve only when incoming is HostName.
IPAddress ip = null;
DnsResponse resolve = null;
var isIpAddress = IPAddress.TryParse(x, out ip);
if (!isIpAddress)
{
resolve = DnsResolver.ResolveHostName(x, dnsTimeout);
ip = resolve.IPAddress;
}
// Execute PingAsync
PingReply reply = null;
using (var ping = new Ping())
{
try
{
reply = await ping.SendPingAsync(ip, (int)pingTimeout.TotalMilliseconds, _buffer, _options);
}
catch
{
// ping throw should never stop operation. just return null.
}
}
// set RoundtripTime
long roundTripTime = 0;
if (reply != null) roundTripTime = reply.RoundtripTime;
// set Status
var status = IPStatus.DestinationHostUnreachable;
if (reply != null) status = reply.Status;
// set IsSuccess
var isSuccess = status == IPStatus.Success;
// return when PingFailed || HostName || OmitResolveDns
if (!isSuccess || !isIpAddress || !resolveDns)
return new PingResponse
{
HostNameOrAddress = x,
IPAddress = ip,
Status = status,
RoundTripTime = roundTripTime,
IsSuccess = isSuccess,
IsResolved = resolve != null,
};
// Resolve Dns only for success host entry.
var host = x;
resolve = DnsResolver.ResolveIP(ip, dnsTimeout);
if (resolve != null) host = resolve.HostName;
return new PingResponse
{
HostNameOrAddress = host,
IPAddress = ip,
Status = status,
RoundTripTime = roundTripTime,
IsSuccess = true,
IsResolved = resolve != null,
};
}).ToArray());
return pingResult;
}
}
}
"@
Add-Type -TypeDefinition $source -ReferencedAssemblies $asm;
$computerName = 1..254 | %{"192.168.10.$_"};
$computerName = 1..254 | %{"192.168.10.$_", "192.168.20.$_", "172.16.30.$_", "172.30.40.$_"};
# [PingEx.NetworkInformationExtensions]::PingAsync($computerName).Result;
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromMilliseconds(1), $false).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, $false).Result;}
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromMilliseconds(100), $false).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromMilliseconds(10), [TimeSpan]::FromMilliseconds(10)).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromMilliseconds(20), [TimeSpan]::FromMilliseconds(20)).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromMilliseconds(20), [TimeSpan]::FromSeconds(1)).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromSeconds(1), [TimeSpan]::FromSeconds(1)).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromSeconds(2), [TimeSpan]::FromSeconds(1)).Result;} | select TotalMilliseconds
measure-command {[PingEx.NetworkInformationExtensions]::PingAsync($computerName, [TimeSpan]::FromSeconds(1), [TimeSpan]::FromSeconds(2)).Result;} | select TotalMilliseconds
@guitarrapc
Copy link
Author

Benchmark

C#

Target Parameters ElapsedTime ms (HostCount = 250) ElapsedTime ms (HostCount = 1016)
HostName Resolve = false, PingTimeout = 1 239.6038
HostName Resolve = false, PingTimeout = 10 498.3157
HostName Resolve = false, PingTimeout = 100 503.4938
HostName Resolve = true, PingTimeout = 10, DnsTimeOut = 10 499.4027
HostName Resolve = true, PingTimeout = 10, DnsTimeOut = 20 500.4586
HostName Resolve = true, PingTimeout = 20, DnsTimeOut = 20 499.5751
HostName Resolve = true, PingTimeout = 20, DnsTimeOut = 1000 500.4327
HostName Resolve = true, PingTimeout = 1000, DnsTimeOut = 1000 998.8722
HostName Resolve = true, PingTimeout = 2000, DnsTimeOut = 1000 2000.348
HostName Resolve = true, PingTimeout = 1000, DnsTimeOut = 2000 999.4717
IP Resolve = false, PingTimeout = 1 446.3438 521.1539
IP Resolve = false, PingTimeout = 10 499.2011 507.6459
IP Resolve = false, PingTimeout = 100 499.0686 493.4757
IP Resolve = true, PingTimeout = 10, DnsTimeOut = 10 500.3029 502.5852
IP Resolve = true, PingTimeout = 10, DnsTimeOut = 20 498.7924 499.3611
IP Resolve = true, PingTimeout = 20, DnsTimeOut = 20 501.8176 500.9099
IP Resolve = true, PingTimeout = 20, DnsTimeOut = 1000 501.5701 499.6192
IP Resolve = true, PingTimeout = 1000, DnsTimeOut = 1000 997.7653 994.8125
IP Resolve = true, PingTimeout = 2000, DnsTimeOut = 1000 1998.9452 2006.3466
IP Resolve = true, PingTimeout = 1000, DnsTimeOut = 2000 1000.3012 997.7852

PowerShell

Target Parameters ElapsedTime ms (HostCount = 250) ElapsedTime ms (HostCount = 1016)
IP Resolve = false, PingTimeout = 1 436.3851 514.5688
IP Resolve = false, PingTimeout = 10 496.5833 494.8772
IP Resolve = false, PingTimeout = 100 498.2532 501.1701
IP Resolve = true, PingTimeout = 10, DnsTimeOut = 10 498.0926 496.2422
IP Resolve = true, PingTimeout = 10, DnsTimeOut = 20 498.9931 497.0781
IP Resolve = true, PingTimeout = 20, DnsTimeOut = 20 498.2816 499.442
IP Resolve = true, PingTimeout = 20, DnsTimeOut = 1000 499.0049 502.2526
IP Resolve = true, PingTimeout = 1000, DnsTimeOut = 1000 997.3409 994.9815
IP Resolve = true, PingTimeout = 2000, DnsTimeOut = 1000 1997.5999 1999.6754
IP Resolve = true, PingTimeout = 1000, DnsTimeOut = 2000 998.8348 1006.5796

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment