.NET Ping() giving false timeouts? - c#

I have a script that is looping through and sending a ping to a host every second. The purpose of which is to test wireless performance on my home network. The issue is, I seem to be getting timeouts from my script, while a standard "ping -t" on either client (pingER and pingEE) have no such timeouts. In fact, not once in the last 30 minutes has either timed out, while my script has received timeout replies every few minutes.
My essential script is below. In all cases, when it does happen, my reply is TimedOut with a response time of 0ms. Is my script causing false timeouts? Thanks.
EDIT: updated code to reflect newest changes, still having same behavior.
while (true)
{
System.Threading.Thread.Sleep(delay);
var builder = new StringBuilder();
hosts.ForEach(h =>
{
var buffer = new byte[32];
var reply = ping.Send(h, 4000, buffer, new PingOptions(600, true));
var error = reply.Status != IPStatus.Success || reply.RoundtripTime > maxRoundTrip;
if (verbose || error)
{
builder.AppendFormat("{0}: {1} ({2}ms)", h, reply.Status, reply.RoundtripTime);
builder.Append(Environment.NewLine);
if (error)
{
WriteLog(h, reply);
SendEmail(h, reply);
}
}
});
Console.WriteLine(DateTime.Now);
Console.WriteLine(builder.ToString());
}

If your ping -t timeout value is less than 1000 or the value of maxRoundTrip is less than 1000 then you are checking against different timeouts in your two different pings (.NET Ping.Send vs. ping -t). The default Windows ping timeout is 4000 ms. http://technet.microsoft.com/en-us/library/bb490968.aspx
Also in your .NET code, you are sending the max buffer size of 65500 bytes. The default Windows ping command sends only 32 bytes of data.
So the difference between either the timeout or buffer size is likely what is causing the variance.
More info about the .NET Ping.Send method: http://msdn.microsoft.com/en-us/library/ms144956(v=vs.110).aspx

I might have figured it out. I was using the netbios name for the machine in question. I'm guessing that each time it did a ping, it was factoring in the IP lookup in the round trip and was timing out. When I used just the IP, it worked just fine and hasn't timed out.
Thanks for the input.

In my case, the byte array size is the main reason (I programmed a similar ping application)
When
var buffer = new byte[65500];
2014/3/30 11:29:26
8.8.8.8: TimedOut (0ms)
changed to
var buffer = new byte[32];
2014/3/30 11:32:50
8.8.8.8: Success (8ms)
I deduced that the ping buffer if too high then the ICMP echo message might be fragmented become more than one packets then only send out,therefore it cannot completed the ping within the timeout period. (But seem like OP mentioned in answer it is hostname resolve problem, then may be my deduction is wrong)
my sample code
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
int delay = 1000;
Ping ping = new Ping();
//OP using something else?
System.Net.IPAddress h = IPAddress.Parse("8.8.8.8");
while (true)
{
System.Threading.Thread.Sleep(delay);
var builder = new StringBuilder();
//change buffer size change the result...
//var buffer = new byte[65500];
var buffer = new byte[32];
var reply = ping.Send(h, 1000, buffer, new PingOptions(600, false));
var error = reply.Status != IPStatus.Success || reply.RoundtripTime > 3000;
if (error)
{
builder.AppendFormat("{0}: {1} ({2}ms)", h, reply.Status, reply.RoundtripTime);
builder.Append(Environment.NewLine);
}
else
{
builder.AppendFormat("{0}: {1} ({2}ms)", h, reply.Status, reply.RoundtripTime);
}
Console.WriteLine(DateTime.Now);
Console.WriteLine(builder.ToString());
}
}
}
}
var reply = ping.Send(h, 1000, buffer, new PingOptions(600, false));
1000 is timeout (ms) for the ping reply...

I'm seeing the same behavior as the OP. My ping.Send() is only sending a buffer of byte[30] and always uses the explicit IP so no DNS/NetBIOS involved. I've tried varying timeout values but consistently see regular timeout returns from Send() vs zero timeouts when using ping from the command-line.
public PingCheck(string address, int timeout = 250, int pingNbr = 1)
{
var rtn = IPAddress.TryParse(address, out IPAddress addr);
if (rtn)
{
IpAddress = addr;
var dataSend = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
var pinger = new Ping();
var pingerOptions = new PingOptions();
pingerOptions.DontFragment = true;
var buffer = Encoding.ASCII.GetBytes(dataSend);
var statusList = new List<IPStatus>();
var lossList = new List<bool>();
var tripList = new List<long>();
for (int i=0; i<pingNbr; i++)
{
var pingerReply = pinger.Send(addr, timeout, buffer, pingerOptions);
var bufback = pingerReply.Buffer;
var dataBack = Encoding.ASCII.GetString(bufback);
var status = pingerReply.Status;
var loss = (dataSend != dataBack);
var msecs = pingerReply.RoundtripTime;
statusList.Add(status);
lossList.Add(loss);
tripList.Add(msecs);
}
// TODO: Add statistics check
}
else
throw new PingException("No IP Address");
}

Related

C# UdpClient.Connect() raises exception on 2nd call

I tried to send form a single UdpClient to several different open UDP sockets from localhost to localhost. However in the first version only the first message of the loop was sent, the rest did not even make it out.
When trying to isolate the error the second call to udpClient.Connect() raises a SocketException pointing to the Error WSAEISCONN 10056
The official documentation of UdpClient.Connect() here states in the remark section if you want to send to different endpoints, call Connect again. However this is the opposite of what the error tells me.
So is this just an error in the docs of the Connect methode, or do I miss something here?
Too fast requests should not be an issue with only 3 requests every 3 Seconds and as I use the normal Send(buffer) call, there should be no packets waiting to be sent to the previous endpoint.
Simple example to reproduce (I used .net 6, Win10):
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace UdpClientIssue;
internal class Program
{
static void Main(string[] args)
{
var sender = CreateSender(3_000, 40001, 40002, 40003);
var reciverA = CreateReciver("ReciverA", 40001);
var reciverB = CreateReciver("ReciverB", 40002);
var reciverC = CreateReciver("ReciverC", 40003);
reciverA.Start();
reciverB.Start();
reciverC.Start();
sender.Start();
sender.Join();
}
static void Reciver(int port)
{
var localEP = new IPEndPoint(IPAddress.Loopback, port);
using var listener = new UdpClient(localEP);
var buff = new byte[1024];
while (true)
{
var senderEP = new IPEndPoint(IPAddress.None, 0);
var data = listener.Receive(ref senderEP);
var message = Encoding.UTF8.GetString(data);
Console.WriteLine($"Recived at {port}: {message}");
}
}
static void Sender(int interval_ms, params int[] ports)
{
int messageNumber = 0;
while (true)
{
Thread.Sleep(interval_ms);
using (var udpClient = new UdpClient())
{
foreach (var remotePort in ports)
{
var message = $"Message {++messageNumber} to {remotePort}";
var sendBuffer = Encoding.UTF8.GetBytes(message);
Console.WriteLine($"Sending to {remotePort}: {message}");
var remoteEP = new IPEndPoint(IPAddress.Loopback, remotePort);
//This errors on second iteration
//udpClient.Connect(remoteEP);
//udpClient.Send(sendBuffer);
//This works
udpClient.Send(sendBuffer, remoteEP);
}
}
messageNumber += 100 - ports.Length;
}
}
static Thread CreateReciver(string name, int port)
{
var ts = new ThreadStart(() => Reciver(port));
var t = new Thread(ts)
{
Name = name
};
return t;
}
static Thread CreateSender(int interval_ms, params int[] ports)
{
var ts = new ThreadStart(() => Sender(interval_ms, ports));
var t = new Thread(ts)
{
Name = "Sender"
};
return t;
}
}
I believe it's error in the docs. Description of this error message says:
Socket is already connected.
A connect request was made on an already-connected socket
And source code explicitly throws this exception with this error code if socket was already connected.
Here you see it just forwards connect to Socket:
public void Connect(IPEndPoint endPoint)
{
ThrowIfDisposed();
ArgumentNullException.ThrowIfNull(endPoint);
CheckForBroadcast(endPoint.Address);
Client.Connect(endPoint);
_active = true;
}
And in Socket.Connect you see it throws this errors if socket is already connected:
if (_isConnected)
{
throw new SocketException((int)SocketError.IsConnected);
}
Fortunately you already know the better way of doing this - just pass remote endpoint to Send call. UDP is connectionless protocol anyway so calling Connect only sets default remote endpoint and doesn't perform actual connection.

How to implement send and receive hl7 data in .NET in ssh connection

I'm implementing an application in .Net. I have to create a connection by SSH which is works, but the HL7 data receiving fails. The destination is a raspberry pi. So when I'm debugging the ssh client is connected, the port is forwarded, the tcp client also connected, but there is no answer for my queries. Plese suggest me some examples!
In this project I have already implemented it on Android - it works fine.
So in .Net I tried the NHapiTools library and I also tried the direct TcpClient way too. localPort = remotePort. I used localIP = "localhost"
static void Main(string[] args)
{
try
{
PrivateKeyFile file = new PrivateKeyFile(#"./key/private.key");
using (var client = new SshClient(remoteIP, sshPort, username, file))
{
client.Connect();
var ci = client.ConnectionInfo;
var port = new ForwardedPortLocal(localIP, localPort, client.ConnectionInfo.Host, remotePort);
client.AddForwardedPort(port);
port.Start();
var req = "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5";
////TCP
var tcpClient = new TcpClient();
tcpClient.Connect(localIP, (int)localPort);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
using (var stream = tcpClient.GetStream())
{
stream.Write(data, 0, data.Length);
using (var buffer = new MemoryStream())
{
byte[] chunk = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(chunk, 0, chunk.Length)) > 0)
{
buffer.Write(chunk, 0, bytesRead);
}
data = buffer.ToArray();
}
}
//I used this also with same result -> no respond
//SimpleMLLP
/*
var connection = new SimpleMLLPClient(localIP, localPort,
Encoding.UTF8);
var response = connection.SendHL7Message(req);
*/
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
}
So I experinced that the buffer size is 0 in TCP (due to time out). In the SimpleMLLP test SendHK7Message method never returns
You are not implementing MLLP (also called LLP) protocol while sending message.
Description HEX ASCII Symbol
Message starting character 0B 11 <VT>
Message ending characters 1C,0D 28,13 <FS>,<CR>
This way, when you send a message to Listener (TCP/MLLP server), it looks for Start Block in your incoming data. It never finds it. It just discards your entire message considering garbage. Hence, you get nothing back from Listener.
With MLLP implemented, your message (the stuff you are writing on socket) should look something like below:
<VT>MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5<FS><CR>
Note the <VT>, <CR> and <FS> are place holders in above message.
You may refer to this article for detailed information (Read step 4 and onward):
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace SimpleMllpHl7ClientAdvanced
{
public class Program
{
private static char END_OF_BLOCK = '\u001c';
private static char START_OF_BLOCK = '\u000b';
private static char CARRIAGE_RETURN = (char)13;
static void Main(string[] args)
{
TcpClient ourTcpClient = null;
NetworkStream networkStream = null;
var testHl7MessageToTransmit = new StringBuilder();
//a HL7 test message that is enveloped with MLLP as described in my article
testHl7MessageToTransmit.Append(START_OF_BLOCK)
.Append("MSH|^~\\&|AcmeHIS|StJohn|CATH|StJohn|20061019172719||ORM^O01|MSGID12349876|P|2.3")
.Append(CARRIAGE_RETURN)
.Append("PID|||20301||Durden^Tyler^^^Mr.||19700312|M|||88 Punchward Dr.^^Los Angeles^CA^11221^USA|||||||")
.Append(CARRIAGE_RETURN)
.Append("PV1||O|OP^^||||4652^Paulson^Robert|||OP|||||||||9|||||||||||||||||||||||||20061019172717|20061019172718")
.Append(CARRIAGE_RETURN)
.Append("ORC|NW|20061019172719")
.Append(CARRIAGE_RETURN)
.Append("OBR|1|20061019172719||76770^Ultrasound: retroperitoneal^C4|||12349876")
.Append(CARRIAGE_RETURN)
.Append(END_OF_BLOCK)
.Append(CARRIAGE_RETURN);
try
{
//initiate a TCP client connection to local loopback address at port 1080
ourTcpClient = new TcpClient();
ourTcpClient.Connect(new IPEndPoint(IPAddress.Loopback, 1080));
Console.WriteLine("Connected to server....");
//get the IO stream on this connection to write to
networkStream = ourTcpClient.GetStream();
//use UTF-8 and either 8-bit encoding due to MLLP-related recommendations
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
//send a message through this connection using the IO stream
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent data to server successfully....");
var receiveMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
// Our server for this example has been designed to echo back the message
// keep reading from this stream until the message is echoed back
while (bytesReceivedFromServer > 0)
{
if (networkStream.CanRead)
{
bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer == 0)
{
break;
}
}
}
var receivedMessage = Encoding.UTF8.GetString(receiveMessageByteBuffer);
Console.WriteLine("Received message from server: {0}", receivedMessage);
}
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
catch (Exception ex)
{
//display any exceptions that occur to console
Console.WriteLine(ex.Message);
}
finally
{
//close the IO strem and the TCP connection
networkStream?.Close();
ourTcpClient?.Close();
}
}
}
}
You should modify your following line of code as below:
var req = START_OF_BLOCK + "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5" + END_OF_BLOCK + CARRIAGE_RETURN;
For more open source code, you may refer to this github project.
After days of struggling I have solved the problem. The main error was with the port forwarding.
I would reccomend to use SSH.Net by Renci (There was algorithm error with Tamir ssh).
After ssh connection created I used this to port forward:
var port = new ForwardedPortLocal(localIP, localPort, "localhost", remotePort);
Check your localIP with ipconfig /all in cmd. Or use 127.0.0.1 as a loopback IP.
SimpleMLLPClient did not worked for me so I used the direct tcp client query way. Like this:
TcpClient ourTcpClient = new TcpClient();
ourTcpClient.Connect(localIP, (int)localPort);
NetworkStream networkStream = ourTcpClient.GetStream();
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent to server successfully....");
byte[] receiveMessageByteBuffer = new byte[ourTcpClient.ReceiveBufferSize];
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer > 0 && networkStream.CanRead)
{
receivedMessage.Append(Encoding.UTF8.GetString(receiveMessageByteBuffer));
}
var message = receivedMessage.Replace("\0", string.Empty);
Console.WriteLine("Received message from server: {0}", message);
}
So it gave me instant answer with 0 bytes (not due timeout). And here comes Amit Joshi help. I used a query what he suggested with START_OF_BLOCK, CARRIAGE_RETURN and END_OF_BLOCK and finally started to work. Thank you Amit Joshi!
Additional info:
In Android (java/Kotlin) jsch session setPortForwardingL works fine with three params:
val session = jsch.getSession("user", sshIP, sshPort)
session.setPassword("")
jsch.addIdentity(privatekey.getAbsolutePath())
// Avoid asking for key confirmation
val prop = Properties()
prop.setProperty("StrictHostKeyChecking", "no")
session.setConfig(prop)
session.connect(5000)
session.setPortForwardingL(localForwardPort, "localhost", remotePort)
val useTls = false
val context = DefaultHapiContext()
connection = context.newClient("localhost", localForwardPort, useTls)

C# Ping is not fast enough

I am trying to make a scan wether the pc is online or offline.
But my current code is way to slow to scan with a good performance as if an computer is offline there is a delay of 3 to 5 seconds.
I even added the timeout parameter set as 500 but it still takes more than 3 seconds if a computer is offline.
public bool PingComputer(string computername)
{
bool check = false;
Ping ping = new Ping();
try
{
PingReply reply = ping.Send(computername, 500);
check = reply.Status == IPStatus.Success;
}
catch (PingException)
{
}
return check;
}
I also already read about asynchron pings but i couldĀ“nt find a suitable solution yet that simply returns true if computer is online or false if it is offline.
Thanks in advance.
If you pretended to Ping a list of Computers you can use Parallel or use async Task.
I tested both methods bellow with the same 77 IPs. Used variable sec = 3.
Tasks toke 00:00:02.7146249
Parallel toke 00:00:05.9941404
To use the methods
Dictionary<string, bool> pingsReturn = await Network.PingListAsync(dictionary,3);
I can give you the 2 examples:
Task
public static async Task<Dictionary<string, bool>> PingListAsync(Dictionary<string, bool> HostList, int sec = 3)
{
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
// set a quick TTL
PingOptions options = new PingOptions(20, true);
// internal support Task to handle Ping Exceptions like "host not found"
async Task<KeyValuePair<string, bool>> PingHost(string host)
{
try
{
var pingresult = await Task.Run(() => new Ping().SendPingAsync(host, sec * 1000, buffer, options));
//t.Wait();
if (pingresult.Status == IPStatus.Success)
return new KeyValuePair<string, bool>(host, true);
else
return new KeyValuePair<string, bool>(host, false);
}
catch
{
return new KeyValuePair<string, bool>(host, false);
}
}
//Using Tasks >>
var watch = new Stopwatch();
watch.Start();
var tasksb = HostList.Select(HostName => PingHost(HostName.Key.ToString()));
var pinglist = await Task.WhenAll(tasksb);
foreach (var pingreply in pinglist)
{
HostList[pingreply.Key] = pingreply.Value;
}
watch.Stop();
Log.Debug("PingList (Tasks) Time elapsed: " + watch.Elapsed);
//Using Tasks <<
return HostList;
}
Parallel
public static async Task<Dictionary<string, bool>> PingListAsync(Dictionary<string, bool> HostList, int sec = 3)
{
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
// set a quick TTL
PingOptions options = new PingOptions(20, true);
//Using Parallel >>
watch = new Stopwatch();
watch.Start();
// avoid exception "Collection was modified; enumeration operation may not execute."
// we need new dictionary and add values
Dictionary<string, bool> dictionary = new Dictionary<string, bool>();
Parallel.ForEach(HostList.Keys, (currHost) =>
{
try
{
var pingReply = new Ping().Send(currHost, sec * 1000, buffer, options);
if (pingReply.Status == IPStatus.Success)
dictionary.Add(currHost, true);
else
dictionary.Add(currHost, false);
}
catch
{
// handle Ping Exceptions like "host not found"
dictionary.Add(currHost, false);
}
});
watch.Stop();
Log.Debug("PingList (Parallel) Time elapsed: " + watch.Elapsed);
//Using Parallel <<
return dictionary;
}
PS - I know this is an old question, but is still valid.

Ping.Send(ip, timeout) in .Net returns too fast and complains on timeout

I'm using this method repeatedly in order to monitor connection to machine in the local network. When same test is done from cmd, results are steady and consistent:
C:\Windows\system32>ping -t 192.168.11.12
Pinging 192.168.11.12 with 32 bytes of data:
Reply from 192.168.11.12: bytes=32 time=1ms TTL=126
Reply from 192.168.11.12: bytes=32 time=1ms TTL=126
But when running from C# with 500 ms timeout it occasionally fails even before the timeout can expire:
public void TestIpAnswersPing()
{
var ip = "192.168.11.12";
var timeout = TimeSpan.FromMilliseconds(500);
var p = new Ping();
foreach (var i in Enumerable.Range(0, 1000))
{
var start = DateTime.Now;
PingReply reply = p.Send(ip, (int)timeout.TotalMilliseconds);
if(reply.Status != IPStatus.Success)
{
Debug.Assert(DateTime.Now - start >= timeout);
}
}
}
If I change timeout to 1 sec - all pass successfully with average time of pings 0.9 ms.
The only similar thing I have found is this - http://support.microsoft.com/kb/2533627, which doesn't help much.
Why this might happen and how to monitor high rate connection?
I had the same problem and fix it with multiple tries.
And don't know if Thread.Sleep(50); is necessary, but it seems to help.
Buffer, TimeOut, TTL are just snakeoil ;)
private PingReply Ping(string ip, int tryCount = 0)
{
var ping = new Ping();
byte[] buffer = new byte[65500];
var pingReply = ping.Send(ip, 500, buffer, new PingOptions(600, false));
if (pingReply.Status == IPStatus.Success)
{
return pingReply;
}
if (tryCount < 5)
{
Thread.Sleep(50);
return Ping(ip, tryCount + 1);
}
return pingReply;
}
Just in case someone still has this issue: because of some reason, Thread.Sleep(50) indeed solves the problem.

TraceRoute and Ping in C#

Does anyone have C# code handy for doing a ping and traceroute to a target computer? I am looking for a pure code solution, not what I'm doing now, which is invoking the ping.exe and tracert.exe program and parsing the output. I would like something more robust.
Given that I had to write a TraceRoute class today I figured I might as well share the source code.
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Net;
namespace Answer
{
public class TraceRoute
{
private const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
public static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress)
{
return GetTraceRoute(hostNameOrAddress, 1);
}
private static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress, int ttl)
{
Ping pinger = new Ping();
PingOptions pingerOptions = new PingOptions(ttl, true);
int timeout = 10000;
byte[] buffer = Encoding.ASCII.GetBytes(Data);
PingReply reply = default(PingReply);
reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);
List<IPAddress> result = new List<IPAddress>();
if (reply.Status == IPStatus.Success)
{
result.Add(reply.Address);
}
else if (reply.Status == IPStatus.TtlExpired || reply.Status == IPStatus.TimedOut)
{
//add the currently returned address if an address was found with this TTL
if (reply.Status == IPStatus.TtlExpired) result.Add(reply.Address);
//recurse to get the next address...
IEnumerable<IPAddress> tempResult = default(IEnumerable<IPAddress>);
tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1);
result.AddRange(tempResult);
}
else
{
//failure
}
return result;
}
}
}
And a VB version for anyone that wants/needs it
Public Class TraceRoute
Private Const Data As String = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Public Shared Function GetTraceRoute(ByVal hostNameOrAddress As String) As IEnumerable(Of IPAddress)
Return GetTraceRoute(hostNameOrAddress, 1)
End Function
Private Shared Function GetTraceRoute(ByVal hostNameOrAddress As String, ByVal ttl As Integer) As IEnumerable(Of IPAddress)
Dim pinger As Ping = New Ping
Dim pingerOptions As PingOptions = New PingOptions(ttl, True)
Dim timeout As Integer = 10000
Dim buffer() As Byte = Encoding.ASCII.GetBytes(Data)
Dim reply As PingReply
reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions)
Dim result As List(Of IPAddress) = New List(Of IPAddress)
If reply.Status = IPStatus.Success Then
result.Add(reply.Address)
ElseIf reply.Status = IPStatus.TtlExpired Then
'add the currently returned address
result.Add(reply.Address)
'recurse to get the next address...
Dim tempResult As IEnumerable(Of IPAddress)
tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1)
result.AddRange(tempResult)
Else
'failure
End If
Return result
End Function
End Class
This implementation is simple, lazy (properly enumerable) and it will not go on searching forever (maxTTL) like some of the other answers.
public static IEnumerable<IPAddress> GetTraceRoute(string hostname)
{
// following are similar to the defaults in the "traceroute" unix command.
const int timeout = 10000;
const int maxTTL = 30;
const int bufferSize = 32;
byte[] buffer = new byte[bufferSize];
new Random().NextBytes(buffer);
using (var pinger = new Ping())
{
for (int ttl = 1; ttl <= maxTTL; ttl++)
{
PingOptions options = new PingOptions(ttl, true);
PingReply reply = pinger.Send(hostname, timeout, buffer, options);
// we've found a route at this ttl
if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
yield return reply.Address;
// if we reach a status other than expired or timed out, we're done searching or there has been an error
if (reply.Status != IPStatus.TtlExpired && reply.Status != IPStatus.TimedOut)
break;
}
}
}
Although the Base Class Library includes Ping, the BCL does not include any tracert functionality.
However, a quick search reveals two open-source attempts, the first in C# the second in C++:
http://www.codeproject.com/KB/IP/tracert.aspx
http://www.codeguru.com/Cpp/I-N/network/basicnetworkoperations/article.php/c5457/
For the ping part, take a look at the Ping class on MSDN.
This is the most efficient way I could think of.
Please vote it up if you like it so others can benefit.
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace NetRouteAnalysis
{
class Program
{
static void Main(string[] args)
{
var route = TraceRoute.GetTraceRoute("8.8.8.8")
foreach (var step in route)
{
Console.WriteLine($"{step.Address,-20} {step.Status,-20} \t{step.RoundtripTime} ms");
}
}
}
public static class TraceRoute
{
public static IEnumerable<PingReply> GetTraceRoute(string hostnameOrIp)
{
// Initial variables
var limit = 1000;
var buffer = new byte[32];
var pingOpts = new PingOptions(1, true);
var ping = new Ping();
// Result holder.
PingReply result = null;
do
{
result = ping.Send(hostnameOrIp, 4000, buffer, pingOpts);
pingOpts = new PingOptions(pingOpts.Ttl + 1, pingOpts.DontFragment);
if (result.Status != IPStatus.TimedOut)
{
yield return result;
}
}
while (result.Status != IPStatus.Success && pingOpts.Ttl < limit);
}
}
}
Ping: We can use the Ping class built into the .NET Framework.
Instantiate a Ping and subscribe to the PingCompleted event:
Ping pingSender = new Ping();
pingSender.PingCompleted += PingCompletedCallback;
Add code to configure and action the ping, e.g.:
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
string who = "www.google.com";
AutoResetEvent waiter = new AutoResetEvent(false);
int timeout = 12000;
PingOptions options = new PingOptions(64, true);
pingSender.SendAsync(who, timeout, buffer, options, waiter);
Add a PingCompletedEventHandler:
public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
... Do stuff here
}
Code-dump of a full working example, based on MSDN's example:
public static void Main(string[] args)
{
string who = "www.google.com";
AutoResetEvent waiter = new AutoResetEvent(false);
Ping pingSender = new Ping();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
pingSender.PingCompleted += PingCompletedCallback;
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
// Wait 12 seconds for a reply.
int timeout = 12000;
// Set options for transmission:
// The data can go through 64 gateways or routers
// before it is destroyed, and the data packet
// cannot be fragmented.
PingOptions options = new PingOptions(64, true);
Console.WriteLine("Time to live: {0}", options.Ttl);
Console.WriteLine("Don't fragment: {0}", options.DontFragment);
// Send the ping asynchronously.
// Use the waiter as the user token.
// When the callback completes, it can wake up this thread.
pingSender.SendAsync(who, timeout, buffer, options, waiter);
// Prevent this example application from ending.
// A real application should do something useful
// when possible.
waiter.WaitOne();
Console.WriteLine("Ping example completed.");
}
public 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();
}
Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
As am improvement to Scotts code answer above, I found that his solution doesn't work if the route tapers off into nothing before reaching the destination - it never returns. A better solution with at least a partial route could be this (which I've tested and it works well). You can change the '20' in the for loop to something bigger or smaller or try to detect if it's taking too long if you want to control the number of iterations some other way. Full credit to Scott for the original code - thanks.
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Net;
...
public static void TraceRoute(string hostNameOrAddress)
{
for (int i = 1; i < 20; i++)
{
IPAddress ip = GetTraceRoute(hostNameOrAddress, i);
if(ip == null)
{
break;
}
Console.WriteLine(ip.ToString());
}
}
private static IPAddress GetTraceRoute(string hostNameOrAddress, int ttl)
{
const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
Ping pinger = new Ping();
PingOptions pingerOptions = new PingOptions(ttl, true);
int timeout = 10000;
byte[] buffer = Encoding.ASCII.GetBytes(Data);
PingReply reply = default(PingReply);
reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);
List<IPAddress> result = new List<IPAddress>();
if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
{
return reply.Address;
}
else
{
return null;
}
}

Categories

Resources