Click here to Skip to main content
Click here to Skip to main content

Continuous asynchronous Ping using TAP and IProgress in C#5

, 15 Jan 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
This article shows how to use C# 5 async functions to create a continuous asynchronous ping and report progress to the UI.

Introduction

The new support for asynchronous functions in C# and Vb.Net 5.0 allow an efficient way to deal with concurrent operations represented as Task<TResult>. The big advantages in daily use are composability and automatic marshaling to the synchronization context (i.e. main thread). The latter saves a lot of calls to Dispatcher.BeginInvoke and their likes and makes the application virtually single threaded and by that easy to debug and maintain.

We can not only utilize these tools for short asynchronous tasks, but also for potentially endless ones. In this article I will use the TAP infrastructure to create an asynchronous Ping tool.

The TAP and IProgress

First of all we will have a look at our tools. If you are already familiar with Async Functions and the TAP you might want to skip this paragraph.

The new Async Functions in .Net consist of two parts. One is the extension of the compiler. This is surfaced by the introduction of two new keywords: async and await (resp. Async and Await in VB.Net). The usage of those is beyond the scope of this article, but Joseph Albahari’s talk gives a good introduction to those.

The other part is the Taskbased Asynchronous Pattern or TAP. The TAP defines the interface that asynchronous functions should implement. This paper of Stephen Toub describes the pattern in detail.

It states, that an asynchronous function should supply the following signature

Task<TResult> DoWorkAsync(&hellip; parameters, CancellationToken cancellationToken, IProgress<T> progress) 

The following points should be noted:

  • The method returns a Task
  • 'Async' is appended to the methods name
  • The method accepts a CancellationToken
  • The method accepts an IProgress<T>

For our task to create a continuous ping operation we are interested in the last two items.

We might need the ability to cancel the task at some time, so we will want to pass a CancellationToken. Furthermore we need a way to continuously report ping times. We achieve this by using IProgress<T>.

IProgress<T> and Progress<T>

As we saw, the .Net Framework 4.5, which is shipped with C# 5, provides an interface IProgress<T>. This interface contains one single method.

public interface IProgress<in T>
{
    void Report(T value);
}

As the name suggests, the asynchronous method can use that method to report progress.

The Framework also includes a simple implementation of that interface named Progress<T>. This class offers a constructor that accepts an Action<T> and an event ProgressChanged. Both can be used to register a callback for process updates. The Progress class invokes both the action and the event on the synchronization context, if one is present. Such a context is created automatically for every WPF, Silverlight and Forms application. That means that we do not have to care about form.Invoke resp. Dispatcher.BeginInvoke ourselves to update UI elements.

Asynchronous Ping using Progress<T>

Using all that, we can finally start to write our async ping.

PingResult

First we need a class to report the progress of our Ping. That class is a container for miscellaneous information of the pings state.

public class PingResult
{
    public IPAddress Address { get; private set; }
    public int PingsTotal { get; private set; }
    public int PingsSuccessfull { get; private set; }
    public TimeSpan AverageTime { get; private set; }
    public TimeSpan LastTime { get; private set; }
    public IPStatus LastStatus { get; private set; }
 
    public PingResult(IPAddress address)
    {
        Address = address;
        LastStatus = IPStatus.Unknown;
    }
      
    internal void AddResult(PingReply res){ ... }
}

The AddResult method will be called by the ping method to accumulate the information into AverageTime, PingsTotal and so on. It accepts an instance of System.Net.NetworkInformation.PingReply.

The Ping methods

With that in place we can at last create the method I keep talking about since the beginning. The method is a thin wrapper around the functionality provided by System.Net.NetworkInformation.Ping.

public static async Task ContinuousPingAsync(
    IPAddress address, 
    int timeout, 
    int delayBetweenPings, 
    IProgress<PingResult> progress, 
    CancellationToken cancellationToken)
{
    var ping = new System.Net.NetworkInformation.Ping();
    var result = new PingResult(address);
    while (!cancellationToken.IsCancellationRequested)
    {
        var res = await ping.SendPingAsync(address, timeout).ConfigureAwait(false);
        result.AddResult(res);
        progress.Report(result);
        await Task.Delay(delayBetweenPings).ConfigureAwait(false);
    }
}

That's all. If you think about the old days of APM with its BeginPing(...) and EndPing(IAsyncResult result) you might be surprised how simple it is to compose asynchronous methods. But let's get through that step by step.

I decided to define that method as static, since all states (result and ping) are hidden inside. As explained above, the method returns a Task to give the caller a handle to the running computation. That makes exception handling easy for the caller. An IProgress<PingResult> object is used to report the ping status. The method can be aborted using the CancellationToken.

Two async functions are invoked and awaited for within the while loop. Since no code is invoked from this loop that can potentially access UI elements, we use .ConfigureAwait(false) to signal the compiler that it does not have to continue on the captured synchronization context (i.e. the UI thread). All reporting us done using the Progress class, which takes care of the marshaling to the UI thread.

WPF Sample application

The source code also includes a simple WPF MVVM app demo application.

screenshot of WPF demo application

You can enter addresses of computers you want to ping. DNS resolution and ping are done asynchronously using this article's methods.

Using the code

The main library contains one static class with three methods to bring asynchronous ping functionality.

namespace Network
{
    public static class Ping
    {
        public static IPAddress ResolveAddress(string hostNameOrAddress)
        {...}
 
        public static async Task<IPAddress> ResolveAddressAsync(string hostNameOrAddress) 
        {...}
 
        public static async Task ContinuousPingAsync(
            IPAddress address, 
            int timeout, 
            int delayBetweenPings, 
            IProgress<PingResult> progress, 
            CancellationToken cancellationToken) 
        {...}
    }
}

ResolveAddress and ResolveAddressAsync mimic the behaviour of System.Net.NetworkInformation.Ping for address resolution. In short you can provide an IP address as string or a hostname that will be resolved using DNS.

ContinuousPingAsync is used to start the continuous ping I keep talking about.

The interface is fairly simple. Please see the included WPF demo application for details. If you have any problems, feel free to contact me. 

History

  • 2012-10-21: Initial version 
  • 2012-10-25: Added source code 
  • 2014-01-15: Fixed code listing 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Butzhammer
Software Developer evopro systems engineering AG
Germany Germany
Peter Butzhammer studied physics but decided to work as a software developer 10 years ago. He has a strong interest in statistics, simulation and functional programming.

Comments and Discussions

 
Questionrepeated code: why? PinmemberAdel Khalil22-Aug-14 8:20 
QuestionContinuous asynchronous ping in c# winform PinmemberFX Andy Widjaja30-May-14 22:01 
QuestionTask.Delay [modified] PinmemberEmile van Gerwen15-Jan-14 3:42 
AnswerRe: Task.Delay PinmemberPeter Butzhammer15-Jan-14 10:40 
GeneralMy vote of 5 PinmemberFlorian Rappl19-Dec-12 10:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 15 Jan 2014
Article Copyright 2012 by Peter Butzhammer
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid