Реализация функции получения среднего времени ответа сервера с помощью команды ping

Вам приходилось когда-либо писать клиент-серверные приложения? Если да, то, вероятно, вы сталкивались с необходимостью получения времени ответа сервера. Такая необходимость может быть вызвана, например, при работе клиента с сетью серверов, имеющих разное географическое положение, для выбора оптимального сервера. В этом небольшом рецепте показан пример реализации функции получения среднего времени ответа сервера с помощью системной утилиты 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, что позволит избавиться от использования сторонней утилиты.

Добавить комментарий