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 aSystem.Timers.ElapsedEventHandler
delegate and add it to theElapsed
event of_pingTimer
. Note that theTimeElapsed
method has to accept anSystem.Object
instance followed by aSystem.Timers.ElapsedEventArgs
instance as its parameters. - A flag to get
_pingTimer
to start counting. We set theEnabled
property of _pingTimer totrue
in order to get it to start counting. The first signalling of theElapsed
event will happen five minutes after we set theEnabled
property of_pingTimer
totrue
.
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 anException
. 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.