How to execute codes periodically in C#

While some applications passively wait for files to be available for processing, there are many others that need to periodically execute codes to fulfill business requirements. For instance, some may constantly trigger other applications via the command line to monitor the network while some others may constantly access databases to generate graphical reports for business analysts.

C#.Net is one programming language that you can use to create applications that need to do work periodically, especially in a windows environment. In this post, I document the usage of the System.Timers.Timer class to execute codes periodically.

A sample scenario

Suppose we have servers that are configured to response to ICMP requests from fellow computers in the network. Somewhere in our office, we have an unused laptop that has a windows operating system that supports the .Net framework. Let's get this laptop to survey the availability of one of our servers.

We will write a C# application that will run on the laptop. This application will send an ICMP packet to the server every five minutes. The application will then log the responses in a text file so that we can inspect the availability of our server.

The PingLogger class

Let's put our business logic into the PingLogger class:

using System;
using System.IO;
using System.Net.NetworkInformation;
using System.Timers;
using System.Text;

public class PingLogger
{
    private string _remoteHostAddress;
    private Timer _pingTimer;
    private Ping _ping;
    private StreamWriter _logFileWriter;

    public PingLogger(string remoteHostAddress)
    {
        this._remoteHostAddress = remoteHostAddress;

        // Configure a Timer for use
        this._pingTimer = new Timer();
        this._pingTimer.Interval = 300000;
        this._pingTimer.Elapsed += new ElapsedEventHandler(this.TimeElapsed);
        this._pingTimer.Enabled = true;

        this._ping = new Ping();
        
        // Gain write access to serverPingStatus.txt
        FileStream fileStream = File.Open("serverPingStatus.txt",
                FileMode.Append, FileAccess.Write);
        this._logFileWriter =  new StreamWriter(fileStream); 
            
    } // end public PingLogger()

    private void PingRemoteHost()
    {
        // Print the time that we try to ping the remote address
        this._logFileWriter.Write("[");
        this._logFileWriter.Write(System.DateTime.Now.ToString());
        this._logFileWriter.Write("] ");
        try
        {
            PingReply reply = this._ping.Send(this._remoteHostAddress, 3000);

            if (reply.Status == IPStatus.Success)
            {
                this._logFileWriter.Write("Successful ICMP response from ");
                this._logFileWriter.Write(this._remoteHostAddress);
                this._logFileWriter.Write(". Round Trip Time: ");
                this._logFileWriter.Write(reply.RoundtripTime);
                this._logFileWriter.Write(" milliseconds.");
            }
            else
            {
                this._logFileWriter.Write("Unsuccessful ICMP response from ");
                this._logFileWriter.Write(this._remoteHostAddress);
                this._logFileWriter.Write("Status of ICMP response: ");
                this._logFileWriter.Write(reply.Status);
                
            } // end if
        } 
        catch (Exception ex) {
            this._logFileWriter.Write("Encountered problem while pinging ");
            this._logFileWriter.Write(this._remoteHostAddress);
            this._logFileWriter.Write(". Error message: ");
            this._logFileWriter.Write(ex.Message);
        } // end try-catch

        this._logFileWriter.WriteLine();
        this._logFileWriter.Flush();

    } // end private void PingRemoteHost()

    private void TimeElapsed(Object sender, ElapsedEventArgs eventArgs)
    {
        PingRemoteHost();
    } // end private void TimeElapsed()

} // end public class PingLogger

Configuring System.Timers.Timer to do your bidding

In the constructor of PingLogger, we first initialize an instance of System.Timers.Timer and set it to _pingTimer. We then perform three configuration settings on _pingTimer:

  • The time interval in which our code will execute. We set a value of 300000 to the Interval property of _pingTimer to indicate that we want it to call our code once every 300000 milliseconds, which is once every five minutes.
  • The code to call once every five minutes. We wrapped the TimeElapsed method with a System.Timers.ElapsedEventHandler delegate and add it to the Elapsed event of _pingTimer. Note that the TimeElapsed method has to accept an System.Object instance followed by a System.Timers.ElapsedEventArgs instance as its parameters.
  • A flag to get _pingTimer to start counting. We set the Enabled property of _pingTimer to true in order to get it to start counting. The first signalling of the Elapsed event will happen five minutes after we set the Enabled property of _pingTimer to true.

After _pingTimer is configured, PingRemoteHost will be executed once every five minutes.

Using System.Net.NetworkInformation.Ping to send ICMP packets

During the initialization of an PingLogger instance, we first prepare an instance of System.Net.NetworkInformation.Ping and set it to _ping.

Subsequently, when PingRemoteHost is called, we call the Send method of _ping to initiate an ICMP request to the remote host which is provided when an instance of PingLogger is created. This call returns an instance of System.Net.NetworkInformation.PingReply, which contains information about the outcome of the ICMP request.

Using System.IO.StreamWriter to log the ICMP responses

In order to write to file, we create an instance of System.IO.StreamWriter and set it to _logFileWriter in the constructor of PingLogger. This gave our code write access to serverPingStatus.txt.

When PingRemoteHost is executed, we first write the current date time via _logFileWriter. Subsequent writes are different for different situations:

  • The call to _ping.Send throws an Exception. We write the address of the server, followed by the exception message.
  • The ping is successful. We write that we are able to get an ICMP response from the server successfully. We then write the round trip time taken to send the ICMP request to the server and receive an ICMP response from the server.
  • The ping is unsuccessful. We write that we are not able to receive an ICMP response from the server successfully. We then write the status of the ICMP response.

The periodic execution was concluded by writing an end of line character and flushing the written data out to serverPingStatus.txt.

Making a console application out of PingLogger

With the bulk of the business logic being encapsulated in the PingLogger class, it is easy to create a console application that will help us survey the availability of our server.

public class Program
{
    public static void Main(string[] args)
    {
        PingLogger pingLogger = new PingLogger("192.168.0.1");
        Console.Read();
    } // end public static void Main(string[] args)
} // end class Program

All we have to do is to create an instance of our PingLogger class and call Console.Read to prevent the application from terminating.

When we run the console application for the first time, a serverPingStatus.txt will appear in the same directory as our application's executable. And until we press a key to terminate our application, our application will ping the address supplied (for eg. 192.168.0.1) and write the outcome in serverPingStatus.txt, once every five minutes.

Some posts that may interest you

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.