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; } }