In this C# tutorial we're going to learn how to ping a host over a network or the Internet using C#. Furthermore the ping is going to be executed asynchronously so that our application doesn't have to wait for the ping.
Pinging A good analogy for would be sending a package with a timer in it to a destination and then back to you. When you get the package back you look at the timer to see how long it took for it to make the trip. If you don’t get it back in a decent amount of time, you mark it as lost, and take it with the courier.
We’re all used to the ping command in the MS-DOS command prompt, and what we’re going to build today it’s going to look much like it:
Designing the application Start Visual Studio 2005 or whatever you prefer to use, and put up a form similar to the one below. The important controls are the TextBoxes txtIP, txtResponse (multiline) and a Button btnPing.
Preparing the class Before I go any further with showing off the SendPing() method and the related events, we need to add the two using statements, since we’ll be using an async call, we need System.Threading, and since we’ll need access to several networking objects, we need System.Net.NetworkInformation:
using System.Threading;
using System.Net.NetworkInformation;
Inside the class declare these two objects that we’ll want accessible later from inside the methods:
// Counts the pings
private int pingsSent;
// Can be used to notify when the operation completes
AutoResetEvent resetEvent = new AutoResetEvent(false);
Now we can move onto coding.
Writing the code We’ll start with the Click event of btnPing where everything starts:
private void btnPing_Click(object sender, EventArgs e)
{
// Reset the number of pings
pingsSent = 0;
// Clear the textbox of any previous content
txtResponse.Clear();
txtResponse.Text += "Pinging " + txtIP.Text + " with 32 bytes of data:\r\n\r\n";
// Send the ping
SendPing();
}
There we prepare things up, and call the SendPing() method:
private void SendPing()
{
System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
// Create an event handler for ping complete
pingSender.PingCompleted += new PingCompletedEventHandler(pingSender_Complete);
// Create a buffer of 32 bytes of data to be transmitted.
byte[] packetData = Encoding.ASCII.GetBytes("................................");
// Jump though 50 routing nodes tops, and don't fragment the packet
PingOptions packetOptions = new PingOptions(50, true);
// Send the ping asynchronously
pingSender.SendAsync(txtIP.Text, 5000, packetData, packetOptions, resetEvent);
}
First of all you probably noticed that we still declare the Ping object using its full namespace. That’s because otherwise the compiler will confuse it with the Ping namespace. On the line following that, we set an event handler for PingCompleted to pingSender_Complete, and that’s where our next chunk of code will be located. The event will fire, as you might’ve guessed, when the packet comes back, or when it doesn’t and the request has timed out. packetData is the content of the package. We made it 32 bytes in size by having 32 characters in it. It doesn’t matter what the characters are, so you can put it your little easter egg in there if you wish to. We don’t want the packet to be defragmented, since the returned result would not be a true number of milliseconds that the packet took to go to the destination and back. Finally the last method sends the ping to the desired host (IP address or host name), with a timeout of 5000 milliseconds and the options specified earlier (should not exceed 50 routing nodes, and should not be fragmented.)
This moves us to our next piece of code, which is the pingSender_Complete event:
private void pingSender_Complete(object sender, PingCompletedEventArgs e)
{
// If the operation was canceled, display a message to the user.
if (e.Cancelled)
{
txtResponse.Text += "Ping was canceled...\r\n";
// The main thread can resume
((AutoResetEvent)e.UserState).Set();
}
else if (e.Error != null)
{
txtResponse.Text += "An error occured: " + e.Error + "\r\n";
// The main thread can resume
((AutoResetEvent)e.UserState).Set();
}
else
{
PingReply pingResponse = e.Reply;
// Call the method that displays the ping results, and pass the information with it
ShowPingResults(pingResponse);
}
}
This piece of code fires when the package got back or when the timeout arrived. Depending on the response, we either show the negative result and tell the main thread it can resume, or call the method that displays the results, which is ShowPingResults():
public void ShowPingResults(PingReply pingResponse)
{
if (pingResponse == null)
{
// We got no response
txtResponse.Text += "There was no response.\r\n\r\n";
return;
}
else if (pingResponse.Status == IPStatus.Success)
{
// We got a response, let's see the statistics
txtResponse.Text += "Reply from " + pingResponse.Address.ToString() + ": bytes=" + pingResponse.Buffer.Length + " time=" + pingResponse.RoundtripTime + " TTL=" + pingResponse.Options.Ttl + "\r\n";
}
else
{
// The packet didn't get back as expected, explain why
txtResponse.Text += "Ping was unsuccessful: " + pingResponse.Status + "\r\n\r\n";
}
// Increase the counter so that we can keep track of the pings sent
pingsSent++;
// Send 4 pings
if (pingsSent < 4)
{
SendPing();
}
}
Here we do some additional checking. First we make sure that we got the parameter passed in correctly – pingResponse of type PingReply – since it holds all the information about how the package got back. We display that information much like MS-DOS displays it when using the ping command but without the final statistics. However, just like the MS-DOS ping command works by default, we do run 4 pings per each request, so at the end of ShowPingResults() we call the SendPing() method all over again, unless we already did that 4 times.
You’re now ready to compile, run and do some pinging! The example below pinged the yahoo.com server IP. Just don’t do it too much or you might take the Yahoo server down. Yeah, right ;-).