Вам приходилось когда-либо писать клиент-серверные приложения? Если да, то, вероятно, вы сталкивались с необходимостью получения времени ответа сервера. Такая необходимость может быть вызвана, например, при работе клиента с сетью серверов, имеющих разное географическое положение, для выбора оптимального сервера. В этом небольшом рецепте показан пример реализации функции получения среднего времени ответа сервера с помощью системной утилиты ping.
Прежде, чем писать код, следует уточнить, что аргументы утилиты ping различаются для разных ОС. Описание утилиты можно прочитать по ссылкам: Windows, Linux. Данная реализация поддерживает Windows, Linux. Macos вероятно, тоже будет работать, но проверить это не удалось ввиду отсутствия данной ОС в пределах досягаемости.
Реализация: файл ping.d
#!/usr/bin/env dub
/+ dub.sdl:
+/
static int pingTime(string host, int number = 4, int timeout = 400)
{
import std.process;
import std.regex;
import std.conv;
import std.stdio;
import std.range;
import std.algorithm;
//todo: or `/usr/sbin/`, find its with `ls /usr/bin | grep ping`
auto command = ["/usr/bin/ping", "-c", to!string(number), "-q", host, "-W", to!string(timeout)];
auto re = regex(`.*rtt.*=.*\/(?P<time>\d+).\d+\/.*`, "gm");
auto output = pipe();
version (Windows)
{
import std.path;
command = [
buildPath(environment.get("SystemRoot", "C:\\Windows"), "System32", "ping"),
"-w", to!string(timeout), "-n", to!string(number), host
];
re = regex(`.*[A|a]verage\s=\s(?P<time>\d+)ms`, "gm");
}
auto ping = spawnProcess(command, stdin, output.writeEnd, stderr);
auto os = appender!string;
output.readEnd.byChunk(4096).copy(os);
if (wait(ping) == 0)
{
auto res = matchFirst(os[], re);
if (res["time"] != null)
{
return to!int(res["time"]);
}
}
return -1;
}
void main(string[] args)
{
import std.stdio;
pingTime("google.com").writeln;
pingTime("ya.ru").writeln;
}Запуск: dub run --single ping.d
В этом коде функция pingTime принимает IP-адрес или домен сервера, число попыток пинга, таймаут запроса в мс. и возвращает среднее время ответа в мс, если ответ не получен, возвращает -1.
Внутри тела функции происходит запуск процесса утилиты ping с заданными параметрами, ожидание и получение результатов запроса, получение из них среднего времени ответа сервера с помощью регулярного выражения.
Обратите внимание, что утилита ping может иметь разное расположение в системах Posix: /usr/bin/ping, /usr/sbin/ping. По этой причине, правильным решением может быть поиск расположения ping с использованием, к примеру, утилиты grep (что в данном коде не реализовано). Также, в большинстве случаев, утилиту можно вызвать просто командой ping без указания полного пути, но в таком случае, если бинарный файл вашего приложения также называется ping (ping.exe), то ваше приложение будет рекурсивно запускать само себя вместо нужной утилиты, учитывайте это.
Кроме того, для работы утилиты ping в системах Posix требуются права системного администратора, поэтому не забывайте использовать sudo.
Данный способ не универсален, не рекомендуем его использование в критически важных сервисах. Правильным решением будет написание своей реализации ping, с использованием отправки сообщений эхо-запросов протокола ICMP, что позволит избавиться от использования сторонней утилиты.