Home ProgrammingC# Visual C#: TCP/ICMP monitoring program – kmon

Visual C#: TCP/ICMP monitoring program – kmon

by Kliment Andreev
5.3K views

I needed some small program that will work in the background and do basic PING checks. Then I’ve decided to expand the scope and include TCP port checks, so I made this small program in Visual C#. You can get the program from GitHub.

git clone https://www.github.com/klimenta/kmon


As you can see from the screenshot, the top left buttons are used to add/remove a server. Each server can have maximum of 6 ports that can be monitored plus ping (ICMP). When you click on the red/green button, it enables or disables the monitoring. The time interval of 5 seconds (default) means that the program will do its checks for 5 seconds and then stop for 5 seconds. The more servers you have, the more your time interval should be. Do about 1-2 seconds per server for internal servers and 3-4 seconds for external (internet facing) servers. The timeout value of 100 ms means that when checking TCP ports, the program will wait for 100 ms before giving up. If your services are slow in response, increase this value.

There are also three buttons (SA, CLR and CPY). When clicked, they Select All the text from the text box, Clear the text and/or Copy it to the clipboard.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.ComponentModel;
using System.Drawing;
using System.Text;

namespace kmon
{

    public partial class frmMain : Form
    {
        //All checks are running as background processes, otherwise the GUI would be very unresponsive
        private BackgroundWorker bw = new BackgroundWorker();
        //Ping average in ms
        double dPingTimeAverage;
        //Holds the status of each TCP port (open/closed)
        bool bCheckOpenTCPPort;
        //kmon can monitor up to 6 ports. You can probably go with more
        const int iNumberOfPorts = 6;
        //A list of Text Boxes for the ports
        List<TextBox> tbPort = new List<TextBox>();
        //Temp variable/counter
        public int intCounter = 0;
        //Default timeout for ping or port checks before kmon gives up
        int intTimeout = 100; //Default = 100 ms
        //Time interval for all checks. kmon will do it's job for 5 secs then pause for 5 secs
        int intTimeInterval = 5000; //Default 5 sec

        //Checks if the TCP port is open or closed
        public static bool CheckOpenTCPPort(string host, int port, int intTimeout)
        {
            bool bOpenPort = false;
            try
            {            
                TcpClient tcpClient = new TcpClient();
                tcpClient.ReceiveTimeout = intTimeout;
                tcpClient.SendTimeout = intTimeout;
                var AsyncResult = tcpClient.BeginConnect(host, port, null, null);
                var waitHandle = AsyncResult.AsyncWaitHandle;
                try
                {
                    if (!AsyncResult.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(intTimeout), false))
                {
                        // wait handle didn't came back in time
                        tcpClient.Close();
                    }
                    else
                    {
                        // The result was positiv
                        bOpenPort = tcpClient.Connected;
                    }
                    // ensure the ending-call
                    tcpClient.EndConnect(AsyncResult);
                }
                finally
                {
                    // Ensure to close the wait handle.
                    waitHandle.Close();
                }
            }
            catch
            {
            }
            return bOpenPort;
        }

        //Returns the ping time average. If the return value is negative 1 (-1), it means the host is not reachable.
        //It doesn't necessarily means it's down. It means, ICMP protocolo can not be established between kmon and the end host.
        public static double PingTimeAverage(string host, int timeout)
        {
            Ping pingSender = new Ping();
            PingOptions options = new PingOptions();
            options.DontFragment = true;
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes(data);
            //int timeout = 
            PingReply reply = pingSender.Send(host, timeout, buffer, options);
            if (reply.Status == IPStatus.Success)
            {
                return reply.RoundtripTime;
            }
            else
            { return -1.0; }
        }

        //A class for each monitored server
        public class Server
        {
            //Server name
            public string strServerName { get; set; }
            //Ping monitoring (y/n)
            public bool bPing { get; set; }
            //TCP Ports
            public List<int> lstPorts = new List<int>();
        }

        //Initialize a list of server objects
        List<Server> srvServer = new List<Server>();
        //Toggle for monitoring/not monitoring button
        private bool bMonitor = false;
        
        public frmMain()
        {
            InitializeComponent();
            btnMonitor.BackColor = System.Drawing.Color.Red;
            btnMonitor.Text = "Not Monitoring";
            
            for (int i = 0; i < iNumberOfPorts; i++)
            {
                TextBox newTextBox = new TextBox();
                tbPort.Add(newTextBox);
                tbPort[i].Width = 45;
                tbPort[i].Height = 20;
                                
                tbPort[i].TabIndex = i + 10;
                tbPort[i].Text = "0";
                tbPort[i].Name = "tbPort" + i.ToString();
                tbPort[i].Tag= i;
                tbPort[i].TextChanged += new EventHandler(tbPort_TextChanged);
                this.Controls.Add(newTextBox);
            }
            tbPort[0].Top = 45; tbPort[1].Top = 45; tbPort[2].Top = 45;
            tbPort[3].Top = 75; tbPort[4].Top = 75; tbPort[5].Top = 75;
            tbPort[0].Left = 221; tbPort[3].Left = 221;
            tbPort[1].Left = 335; tbPort[4].Left = 335;
            tbPort[2].Left = 449; tbPort[5].Left = 449;


            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);            
        }

        //Event when you change the port to be monitored
        protected void tbPort_TextChanged(object sender, EventArgs e)
        {
            int intPort;
            int intCheck = lbServers.SelectedIndices.Count;
            
            if (lbServers.SelectedIndex == -1)
            {
                MessageBox.Show("You have to select a server on the left side", "Error");
                return;
            }
            try
            {                
                intPort = Convert.ToInt32((sender as TextBox).Text.Trim());
                if (intPort > 65535) intPort = 65535;           
            }
            catch (Exception)
            {
                (sender as TextBox).Text = "0";
                return;
            }
            srvServer[lbServers.SelectedIndex].lstPorts[Convert.ToInt32((sender as TextBox).Tag)] = intPort;            
        }

        //Add server to be monitored
        private void btnAdd_Click(object sender, EventArgs e)
        {
            frmAddServer frm2 = new frmAddServer();
            frm2.ShowDialog();
            //Prevent duplicates
            if (frmAddServer.strServerName == null || frmAddServer.strServerName == "") return;
            if (!lbServers.Items.Contains(frmAddServer.strServerName))
            {
                lbServers.Items.Add(frmAddServer.strServerName);
                srvServer.Add(new Server { strServerName = frmAddServer.strServerName, bPing = false, lstPorts = new List<int>(iNumberOfPorts) { 0, 0, 0, 0, 0, 0 } });                
            }
            else
            {
                MessageBox.Show("Duplicate entry. Please re-enter.", "kmon");
            }            
        }

        //Remove server from being monitored
        private void btnRemove_Click(object sender, EventArgs e)
        {
            for (int intCounter = lbServers.SelectedIndices.Count - 1; intCounter >= 0; intCounter--)
            {
                int intIndex = lbServers.SelectedIndices[intCounter];
                lbServers.Items.RemoveAt(intIndex);
                srvServer.RemoveAt(intIndex);
            }
        }

        //Toggle button monitor/not monitor
        private void btnMonitor_Click(object sender, EventArgs e)
        {            
            if (bMonitor)
            {
                bMonitor = false;
                btnMonitor.BackColor = Color.Red;
                btnMonitor.Text = "Not Monitoring";               
                if (bw.WorkerSupportsCancellation == true)
                {
                    bw.CancelAsync();
                }
            }
            else
            {
                bMonitor = true;
                btnMonitor.BackColor = Color.Green;
                btnMonitor.Text = "Monitoring";               
                if (bw.IsBusy != true)
                {
                    bw.RunWorkerAsync();
                }
            }
        }

        //Event when you change the time interval
        private void tbInterval_TextChanged(object sender, EventArgs e)
        {
            try
            {
                intTimeInterval = Convert.ToInt32(tbInterval.Text.Trim()) * 1000;                
            }
            catch (Exception)
            {
                tbInterval.Text = "5";
            }
        }

        //Event when you select the server from the list of servers
        private void lbServers_SelectedIndexChanged(object sender, EventArgs e)
        {            
            try
            {               
                for (int i = 0; i < iNumberOfPorts; i++)
                {
                    tbPort[i].Text = srvServer[lbServers.SelectedIndex].lstPorts[i].ToString();
                }                    
                cbPing.Checked = srvServer[lbServers.SelectedIndex].bPing;
            }
            catch (Exception)
            { }            
        }

        //Check mark event ping/not ping
        private void cbPing_CheckedChanged(object sender, EventArgs e)
        {
            int intCheck = lbServers.SelectedIndices.Count;
            if (lbServers.SelectedIndex == -1)
            {
                MessageBox.Show("You have to select a server on the left side", "Error");
                return;
            }
            srvServer[lbServers.SelectedIndex].bPing = cbPing.Checked;       
        }

        //Background process init/cancel
        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            while ( true )
            {
                if ((worker.CancellationPending == true))
                {
                    e.Cancel = true;
                    break;
                }
                else
                {                    
                    System.Threading.Thread.Sleep(intTimeInterval); //Time interval between each thread
                    worker.ReportProgress(0);
                }
            }
        }

        //Append log
        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Cancelled == true))
            {                
                rtbLog.AppendText(DateTime.Now + " ", Color.Blue);                
                rtbLog.AppendText("NOT MONITORING!" + Environment.NewLine, Color.Red);
            }

            else if (!(e.Error == null))
            {
                rtbLog.AppendText(DateTime.Now + " ", Color.Blue);
                rtbLog.AppendText("Error: " + e.Error.Message, Color.Red);
            }

            else
            {
                rtbLog.AppendText(DateTime.Now + " ", Color.Blue);
                rtbLog.AppendText("Done!", Color.Green);
            }
        }

        //Main process for ping/check TCP ports
        private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Color cColor;
            for (int j = 0; j < srvServer.Count; j++)
            {
                if (srvServer[j].bPing) {
                    dPingTimeAverage = PingTimeAverage(srvServer[j].strServerName, intTimeout);
                    rtbLog.AppendText(DateTime.Now + " ", Color.Blue);
                    if (dPingTimeAverage == -1)
                    {
                        cColor = Color.Red;
                    }
                    else
                    {
                        cColor = Color.Green;
                    }
                    string strPingTimeAverage = dPingTimeAverage.ToString();
                    if (strPingTimeAverage == "-1")
                    {
                        strPingTimeAverage = "NOT REACHABLE ";
                    }
                    rtbLog.AppendText(srvServer[j].strServerName + " " + strPingTimeAverage + "ms reply time " + Environment.NewLine, cColor);
                }
                int i = 0;
                while (srvServer[j].lstPorts[i] != 0) {
                    bCheckOpenTCPPort = CheckOpenTCPPort(srvServer[j].strServerName, srvServer[j].lstPorts[i], intTimeout);
                    rtbLog.AppendText(DateTime.Now + " ", Color.Blue);
                    if (bCheckOpenTCPPort)
                    {
                        cColor = Color.Green;
                    }
                    else
                    {
                        cColor = Color.Red;
                    }
                    string strPort;
                    if (bCheckOpenTCPPort)
                    {
                        strPort = " opened";
                    }
                    else {
                        strPort = " CLOSED";
                    }
                    rtbLog.AppendText(srvServer[j].strServerName + " has port " + srvServer[j].lstPorts[i].ToString() + strPort + Environment.NewLine, cColor);
                    i++;
                }
            }
        }

        //Select all in the log
        private void btnSelectAll_Click(object sender, EventArgs e)
        {
            rtbLog.SelectAll();
        }

        //Clear the log
        private void btnClearLog_Click(object sender, EventArgs e)
        {
            rtbLog.Clear();
        }

        //Copy the log to clipboard
        private void btnCopy_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(rtbLog.SelectedRtf, TextDataFormat.Rtf);
        }

        //Event when you change the timeout setting
        private void tbTimeout_TextChanged(object sender, EventArgs e)
        {
            try
            {
                intTimeout = Convert.ToInt32(tbTimeout.Text.Trim());
            }
            catch (Exception)
            {
                tbTimeout.Text = "100";
            }            
        }

        //Hide the window when minimized
        private void frmMain_Resize(object sender, EventArgs e)
        {
            if (FormWindowState.Minimized == WindowState)
                Hide();
        }

        //Retore the view when the icon is clicked in the tray
        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        //Restore the icon when "restore" option is chosen from the menu
        private void restoreToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        //Try to close the program when "exit" option is chosen from the menu
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        //Ask for confirmation before the program closes
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("Are you sure you want to close?", "kmon", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
            {
                e.Cancel = true;
            }
        }
    }
}

//Class to Extend AppendText for RichTextBox control to include the color value
public static class RichTextBoxExtensions
{
    public static void AppendText(this RichTextBox box, string text, Color color)
    {
        box.SelectionStart = box.TextLength;
        box.SelectionLength = 0;
        box.SelectionColor = color;
        box.AppendText(text);
        box.SelectionColor = box.ForeColor;
    }
}

Related Articles

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More