Одно время работал в МФЦ и понадобилось местному админу следить за работой интернета в отделениях. Появилась идея написать небольшую программку которая в доступном виде выводила бы на экран состояние работы, а также писала бы логи, чтоб в случае чего можно было определить, когда связи не было.
Программа - консольное приложение, параметры задаются при запуске в следующем виде:
PingLogger.exe путь_к-папке_с_логами хост1 хост2 ... хостN
Папка в случае отсутствия будет создана, хосты могут быть либо в виде имени (например smfd.ru), либо в виде ip-адреса.
Сама программа состоит из двух классов: стандартного Program, который проверяет входные параметры, создаёт в случае необходимости папку для хранения логов, получает IP-адреса из имён заданных хостов и параллельно вызывает метод Ping() класса Pinger для каждого хоста, который уже собственно и отправляет эхо-запросы и пишет логи. Ниже код программы:
namespace PingLogger
{
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
#endregion
public class Program
{
public static object sync = new object();
static void Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Надо указать как минимум 2 параметра:");
Console.WriteLine("1. Путь к папке где хранить логи");
Console.WriteLine("2. IP-адрес 1");
Console.WriteLine(" ... ");
Console.WriteLine("n. IP-адрес n");
Console.WriteLine("Например: PingLogger.exe d:\\PingLogs\\ localhost ya.ru 8.8.8.8 smfd.ru");
Console.ReadKey();
return;
}
string logPath = args[0];
if (!Directory.Exists(logPath))
{
try { Directory.CreateDirectory(logPath); }
catch
{
Console.WriteLine("Путь к папке указан неверно! " + logPath);
Console.ReadKey();
return;
}
}
Dictionary<IPAddress, string> ipAddrs = new Dictionary<IPAddress, string>();
List<string> errors = new List<string>();
for (int i = 1; i < args.Length; i++)
{
IPAddress ipAddr;
bool isHostName = false;
if (!IPAddress.TryParse(args[i], out ipAddr))
{
IPHostEntry ipHost = null;
try
{
ipHost = Dns.Resolve(args[i]);
ipAddr = ipHost.AddressList.First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
isHostName = true;
}
catch (System.Net.Sockets.SocketException ex)
{
errors.Add(args[i] + " не является ни IP-адресом, ни правильным именем хоста, или не удалось получить IP-адрес по имени хоста");
}
}
if (ipAddr != null)
{
if (!ipAddrs.ContainsKey(ipAddr))
{
if (isHostName)
ipAddrs.Add(ipAddr, args[i]);
else
ipAddrs.Add(ipAddr, string.Empty);
}
else
{
if (isHostName && string.IsNullOrEmpty(ipAddrs[ipAddr])) // если ип уже есть но без имени хоста, добавим имя
ipAddrs[ipAddr] = args[i];
else if (isHostName && !ipAddrs[ipAddr].Contains(args[i])) // если ип уже есть но имя хоста/ов не совпадает, добавим имя
ipAddrs[ipAddr] = string.Format("{0}, {1}", ipAddrs[ipAddr], args[i]);
}
}
}
if (errors.Count > 0)
{
Console.WriteLine();
Console.WriteLine("Во входных параметрах есть ошибки:");
foreach (string error in errors)
Console.WriteLine(error);
if (ipAddrs.Count == 0)
{
Console.ReadKey();
return;
}
Console.WriteLine();
Console.WriteLine("Продолжить выполнение программы? (Y = да)");
if (Console.ReadKey().Key != ConsoleKey.Y)
return;
}
Console.Title = "PingLogger <Shift>+<S> => остановить пинг";
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
foreach (KeyValuePair<IPAddress, string> ipAddr in ipAddrs)
{
string ip = ipAddr.Key.ToString();
string hostName = ipAddr.Value;
Task.Factory.StartNew(() =>
{
new Pinger(ip, hostName, logPath).Ping(cancelTokenSource.Token);
}, cancelTokenSource.Token);
}
while (true)
{
ConsoleKeyInfo ci = Console.ReadKey();
if (ci.Key == ConsoleKey.S && ci.Modifiers == ConsoleModifiers.Shift)
cancelTokenSource.Cancel();
}
}
}
class Pinger
{
string ipAddr;
string hostName;
string logPath;
int pingAttempts;
int pingCount;
long pingTime;
public Pinger(string ipAddr, string hostName, string logPath)
{
this.ipAddr = ipAddr;
this.hostName = hostName;
this.logPath = logPath;
this.pingAttempts = 0;
this.pingCount = 0;
this.pingTime = 0;
}
public void Ping(CancellationToken cancelToken)
{
Ping ping = new Ping();
PingReply pingR = null;
bool cancelled = false;
while (true)
{
string fileName = string.Empty;
if(string.IsNullOrEmpty(hostName))
fileName = Path.Combine(this.logPath, string.Format("{0} {1:dd.MM.yyyy}.txt", this.ipAddr, DateTime.Now));
else
fileName = Path.Combine(this.logPath, string.Format("{0} ({1}) {2:dd.MM.yyyy}.txt", this.hostName, this.ipAddr, DateTime.Now));
StringBuilder logMessage = new StringBuilder();
try
{
cancelToken.ThrowIfCancellationRequested();
this.pingAttempts++;
pingR = null;
logMessage.Append(string.Format("{0:hh:mm:ss} > Ответ от {1}: ", DateTime.Now, this.ipAddr));
pingR = ping.Send(this.ipAddr);
if (pingR.Status == IPStatus.Success)
{
logMessage.Append(string.Format("{0} мс", (pingR.RoundtripTime == 0 ? "<1" : pingR.RoundtripTime.ToString())));
this.pingCount++;
this.pingTime += pingR.RoundtripTime;
}
else
logMessage.Append("нет ответа");
}
catch (OperationCanceledException) { cancelled = true; }
catch (PingException) { logMessage.Append("нет ответа"); }
if (cancelled)
{
lock (Program.sync)
{
string host = this.ipAddr;
if(!string.IsNullOrEmpty(this.hostName))
host = string.Format("{0} ({1})", this.hostName, host);
string resultMessage1 = string.Format("Результаты эхо-запроса для {0}:", host);
string resultMessage2 = string.Format("Отправлено: {0}, получено: {1}, % ответа: {2:F2}%, среднее время отклика: {3} мс",
this.pingAttempts,
this.pingCount,
this.pingCount != 0 ? ((double)this.pingCount * 100) / (double)this.pingAttempts : 0,
this.pingCount != 0 ? Math.Round(((double)this.pingTime / (double)this.pingCount)) : 0);
logMessage.AppendLine();
logMessage.AppendLine(string.Format("{0:hh:mm:ss} {1}", DateTime.Now, resultMessage1));
logMessage.AppendLine(resultMessage2);
using (StreamWriter w = File.AppendText(fileName))
w.WriteLine(logMessage.ToString());
Console.WriteLine();
Console.WriteLine(resultMessage1);
Console.WriteLine(resultMessage2);
}
break;
}
lock (Program.sync)
{
if (!File.Exists(fileName))
File.Create(fileName).Close();
if (pingR != null && pingR.Status == IPStatus.Success) // Все норм
{
Console.ForegroundColor = ConsoleColor.White;
Console.Write("#");
}
else if (pingR != null && (pingR.Status == IPStatus.TimedOut || pingR.Status == IPStatus.DestinationHostUnreachable)) // Нет ответа или хост недоступен
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("#");
}
else // Нет ответа по неясным причинам или ошибка
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("!");
}
using (StreamWriter w = File.AppendText(fileName))
w.WriteLine(logMessage.ToString());
Console.ResetColor();
}
Thread.Sleep(1000);
}
}
}
}
При нажатии сочетания клавиш <Shift>+<S> отправка эхо-запросов останавливается, на экран и в лог-файл выводятся общие результаты пинга.
В файл логи пишутся в следующем виде:
discogs.com (216.151.17.130) 07.07.2016.txt
11:43:20 > Ответ от 216.151.17.130: 205 мс
11:43:21 > Ответ от 216.151.17.130: 205 мс
...
11:47:02 > Ответ от 216.151.17.130: 205 мс
11:47:03 > Ответ от 216.151.17.130: нет ответа
11:47:09 > Ответ от 216.151.17.130: 205 мс
...
11:47:36 > Ответ от 216.151.17.130: 206 мс
11:47:38 > Ответ от 216.151.17.130: 205 мс
11:47:39 Результаты эхо-запроса для discogs.com (216.151.17.130):
Отправлено: 91, получено: 90, % ответа: 98,90%, среднее время отклика: 205 мс
Во вложении находится сама программа и проект.