Можно ли пинговать сервер из Javascript?

152

Я делаю веб-приложение, которое требует, чтобы я проверил, подключены ли удаленные серверы к сети или нет. Когда я запускаю его из командной строки, моя страница загружается на целых 60 секунд (для 8 записей она будет линейно масштабироваться с увеличением).

Я решил пойти по пути пинга на стороне пользователя. Таким образом, я могу загрузить страницу и просто ждать, пока данные «сервер подключен», просматривая мой контент.

Если у кого-то есть ответ на поставленный выше вопрос или если он знает решение, позволяющее быстро загружать мою страницу, я определенно буду признателен за это.

Чак Каллебс
источник

Ответы:

134

Я нашел кого-то, кто выполняет это с очень умным использованием нативного Imageобъекта.

Исходя из их источника, это основная функция (она зависит от других частей источника, но вы поняли идею).

function Pinger_ping(ip, callback) {

  if(!this.inUse) {

    this.inUse = true;
    this.callback = callback
    this.ip = ip;

    var _that = this;

    this.img = new Image();

    this.img.onload = function() {_that.good();};
    this.img.onerror = function() {_that.good();};

    this.start = new Date().getTime();
    this.img.src = "http://" + ip;
    this.timer = setTimeout(function() { _that.bad();}, 1500);

  }
}

Это работает на всех типах серверов, которые я тестировал (веб-серверы, FTP-серверы и игровые серверы). Это также работает с портами. Если кто-то сталкивается с случаем использования, который терпит неудачу, пожалуйста, отправьте в комментариях, и я обновлю свой ответ.

Обновление : Предыдущая ссылка была удалена. Если кто-то найдет или реализует вышеизложенное, прокомментируйте, и я добавлю его в ответ.

Обновление 2 : @trante был достаточно хорош, чтобы предоставить jsFiddle.

http://jsfiddle.net/GSSCD/203/

Обновление 3 : @Jonathon создал репозиторий GitHub с реализацией.

https://github.com/jdfreder/pingjs

Обновление 4 : похоже, что эта реализация больше не является надежной. Люди также сообщают, что Chrome больше не поддерживает все это, выдавая net::ERR_NAME_NOT_RESOLVEDошибку. Если кто-то может проверить альтернативное решение, я поставлю это как принятый ответ.

Чак Каллебс
источник
48
Это то, что я использовал. Однако у него есть один недостаток, и это то, что «изображение» кэшируется. Когда я пингую данный IP изначально, я получаю 304 мс - но если я пингую его второй раз без перезагрузки страницы, я получаю 2 мс. Этого можно избежать, добавив a "/?cachebreaker="+new Date().getTime();в конец img src, если это необходимо.
Мешааль
2
это не работает для меня, известное неверное имя хоста приводит к тому, что запрос Ping не может найти хост .... но так как onerror «хороший», эта вещь говорит, что она ответила
Маслоу
5
Дальнейшее тестирование показало, что это абсолютно ненадежно.
Лев
2
API Ping, созданный @Jonathon, успешно пропингует все. Сайты, которые не существуют и случайные символы.
IE5Master
2
Ping API фактически всегда завершается с ошибкой. ОДНАКО, если целевой URL обозначает изображение, он запускает загрузку, которая является удивительной! Обходит проверки CORS.
Мартин Высный
20

Ping - это ICMP, но если на удаленном сервере есть открытый порт TCP, это можно сделать так:

function ping(host, port, pong) {

  var started = new Date().getTime();

  var http = new XMLHttpRequest();

  http.open("GET", "http://" + host + ":" + port, /*async*/true);
  http.onreadystatechange = function() {
    if (http.readyState == 4) {
      var ended = new Date().getTime();

      var milliseconds = ended - started;

      if (pong != null) {
        pong(milliseconds);
      }
    }
  };
  try {
    http.send(null);
  } catch(exception) {
    // this is expected
  }

}

Нильс Хольгерссон
источник
Мне нравится это хитрое решение. Необходимость предоставить функцию для понга вместо того, чтобы возвращать число, немного странно для меня, но выполнимо.
Ник
@armen: вы должны предоставить функцию в качестве третьего аргумента ping (), значение этого аргумента называется pong внутри функции.
13
2
ping("example.com", "77", function(m){ console.log("It took "+m+" miliseconds."); })..... пример звонка
jave.web
@Nick: Это асинхронный материал, поэтому, когда метод возвращается, onreadystatechangeеще не сработал. Это означает, что вам нужно перезвонить, чтобы забрать, когда состояние меняется.
благоговение
Что бы я ни выбрал для хоста, вызывается pong (). ping ( test.zzzzzzzzz, "77", function (m) {console.log ("Это заняло" + m + "милисекунды.);}) печатает" Это заняло 67 миллисекунд ". ping ( stackoverflow.com, "80", function (m) {console.log ("Это заняло" + m + "милисекунды.");}) выдает ошибку CORS: developer.mozilla.org/en-US/docs/Web/ HTTP / CORS / Errors /… Я не вижу, как проверить этот код, если удаленный компьютер подключен к сети.
Tux
18

Вы можете попробовать это:

поместите ping.html на сервер с содержимым или без него, на javascript сделайте то же самое, что и ниже:

<script>
    function ping(){
       $.ajax({
          url: 'ping.html',
          success: function(result){
             alert('reply');
          },     
          error: function(result){
              alert('timeout/error');
          }
       });
    }
</script>
jerjer
источник
1
Серверы, на которых я пингуюсь, не являются моими собственными, поэтому у меня нет такой возможности. Спасибо хоть.
Чак Каллебс
9
@dlchambers Не обязана CORS, но обязана междоменной политике. CORS позволяет серверу указывать источник, плюс он должен поддерживаться браузером. так что это больше похоже на междоменную проблему.
Ройи Намир
Запросы типа HEAD также не будут работать из-за CORS. Протестировано с Chromium 55. Получение такой же ошибки с http кодом 0, как, например, в случае net :: CONNECTION_REFUSED.
Мартин Высны
Это более кроссбраузерно.
Габриэль
14

Вы не можете напрямую "пинговать" в JavaScript. Там может быть несколько других способов:

  • Ajax
  • Использование Java-апплета с isReachable
  • Написание серверного скрипта, который пингует и использует AJAX для связи с вашим серверным скриптом
  • Вы также можете пинговать во флэш-памяти (actionscript)
Евгений
источник
1
Java isReachable довольно ненадежен .... Но, ну, это 2018 год, и апплеты Java в любом случае устарели.
Дреуа
8

Вы не можете выполнять регулярный пинг в браузере Javascript, но вы можете узнать, жив ли удаленный сервер, например, загрузив изображение с удаленного сервера. Если загрузка не удалась -> сервер не работает.

Вы даже можете рассчитать время загрузки с помощью события onload. Вот пример того, как использовать событие onload.

Эпели
источник
4
К сожалению, серверы, на которых я пингуюсь, не являются веб-серверами. Они игровые серверы.
Чак Каллебс
1
Затем вы должны выполнить пинг на стороне сервера - или вы могли бы рассмотреть какой-нибудь легкий http-сервер.
Эпели
Знаете ли вы, как сделать совокупный пинг? Это мое основное замедление: ожидание завершения одного запроса до начала другого.
Чак Каллебс
Вам нужно пинговать серверы одновременно. Вы можете достичь этого с помощью select, потоков или процессов. Для действительно жесткого решения вы можете использовать Eventmachine.
Эпели
1
Вы можете воспользоваться преимуществами командной строки ping. Это промышленная сила. Или есть все виды бесплатных приложений с открытым исходным кодом, чтобы отслеживать, работает ли хост, используя различные типы проверок сердцебиения.
Жестянщик
7

С помощью решения веб-сокета ...

function ping(ip, isUp, isDown) {
  var ws = new WebSocket("ws://" + ip);
  ws.onerror = function(e){
    isUp();
    ws = null;
  };
  setTimeout(function() { 
    if(ws != null) {
      ws.close();
      ws = null;
      isDown();
    }
  },2000);
}
Энтони Вудс
источник
Разве isUp();вызов не должен быть в onopenобработчике событий? :)
jave.web
1
Он использует протокол websocket, но предполагает, что на самом деле сервер websocket не ожидает подключения. Это просто для пинга «любой старый IP».
Энтони Вудс
Но как определить, что WebSocket просто не поддерживается или действительно была какая-то ошибка? :)
jave.web
Если вы считаете, что вам не повезло пропинговать IP-адрес, который может принять подключение к веб-сокету через порт 80, то да, вы должны также добавить isUp();этот обратный вызов. Или смягчите это, добавив явно не-websocket-y порт.
Энтони Вудс
Я проголосовал за это, потому что я пришел сюда в поисках решения того, что происходит, когда мой сервер веб-сокетов перезагружается, и этот ответ показал мне, что я могу установить setTimeout в обработчике ошибок сокета и попытаться восстановить соединение (занятое ожидание). Вероятно, не лучшая практика, но решает мою проблему. :)
mynameisnafe
6

Чтобы ваши запросы были быстрыми, кэшируйте результаты проверки на стороне сервера и обновляйте файл или базу данных проверки каждые пару минут (или, насколько вы хотите, точнее). Вы можете использовать cron для запуска команды оболочки с вашими 8 пингами и записи вывода в файл, веб-сервер включит этот файл в ваше представление.

user456733
источник
Я думаю, что это самый надежный способ с наилучшим возможным для точного результата. Однако, так как он все еще требует некоторых работ на сервере, я бы сказал, что это не умный способ.
Четабахана,
5

Если вы пытаетесь увидеть, существует ли сервер, вы можете использовать следующее:

function isValidURL(url) {
    var encodedURL = encodeURIComponent(url);
    var isValid = false;

    $.ajax({
      url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodedURL + "%22&format=json",
      type: "get",
      async: false,
      dataType: "json",
      success: function(data) {
        isValid = data.query.results != null;
      },
      error: function(){
        isValid = false;
      }
    });

    return isValid;
}

Это вернет истину / ложь, указав, существует ли сервер.

Если вы хотите время отклика, небольшая модификация сделает:

function ping(url) {
    var encodedURL = encodeURIComponent(url);
    var startDate = new Date();
    var endDate = null;
    $.ajax({
      url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodedURL + "%22&format=json",
      type: "get",
      async: false,
      dataType: "json",
      success: function(data) {
        if (data.query.results != null) {
            endDate = new Date();
        } else {
            endDate = null;
        }
      },
      error: function(){
        endDate = null;
      }
    });

    if (endDate == null) {
        throw "Not responsive...";
    }

    return endDate.getTime() - startDate.getTime();
}

Использование тогда тривиально:

var isValid = isValidURL("http://example.com");
alert(isValid ? "Valid URL!!!" : "Damn...");

Или:

var responseInMillis = ping("example.com");
alert(responseInMillis);
Ohad
источник
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится.
vonbrand
1
Это отличное решение, но, к сожалению, служба запросов к домену Yahoo, похоже, дает переменные результаты. например, если вы попробуете следующие URL-адреса, begin-download .com beginfreedownload .com они вернутся как незарегистрированные, но если вы перейдете по этим URL-адресам, они явно зарегистрированы. Есть идеи, что здесь происходит? (Я добавил пробел к доменным именам в этих примерах, просто чтобы не выдавать гугл сок. В моем тесте у меня не было пробела)
user280109
Вы делаете запрос в Yahoo! API, ping не будет даже таким же, если вы сделаете запрос со своего компьютера. Это не имеет никакого смысла
Andre Figueiredo
Это вызывает No 'Access-Control-Allow-Origin' header is present on the requested resource. Originи успех обратного вызова никогда не называется
vladkras
4

Проблема со стандартными эхо-запросами заключается в том, что они являются ICMP, который многие места не пропускают по соображениям безопасности и трафика . Это может объяснить неудачу.

Ruby до 1.9 имел TCP-интерфейс ping.rb, который будет работать с Ruby 1.9+. Все, что вам нужно сделать, это скопировать его из установки 1.8.7 в другое место. Я только что подтвердил, что он будет работать, пропинговать мой домашний маршрутизатор.

жестяной человек
источник
4

Здесь есть много сумасшедших ответов, особенно о CORS -

Вы можете сделать запрос HTTP HEAD (как GET, но без полезной нагрузки). Смотрите https://ochronus.com/http-head-request-good-uses/

Он НЕ нуждается в предварительной проверке, путаница вызвана старой версией спецификации, см. Почему запрос HEAD для разных источников требует предварительной проверки?

Таким образом, вы можете использовать ответ выше, который использует библиотеку jQuery (не сказал это), но с

type: 'HEAD'

--->

<script>
    function ping(){
       $.ajax({
          url: 'ping.html',
          type: 'HEAD',
          success: function(result){
             alert('reply');
          },     
          error: function(result){
              alert('timeout/error');
          }
       });
    }
</script>

Конечно, вы также можете использовать Vanilla JS или Dojo или что-то еще ...

sebilasse
источник
1
При отправке HEAD-запроса все еще происходит сбой: «XMLHttpRequest не может загрузить IP-адрес. В запрошенном ресурсе отсутствует заголовок« Access-Control-Allow-Origin ». Поэтому для источника« ip2 »доступ запрещен». Так заблокировано CORS. Вы получите успех с http статусом 0, и, таким образом, вы не сможете отличить его, например, от net :: ERR_CONNECTION_REFUSED
Martin Vysny
0

Я не знаю, какую версию Ruby вы используете, но вы пытались реализовать ping для ruby ​​вместо javascript? http://raa.ruby-lang.org/project/net-ping/

wajiw
источник
Да, я пробовал это, но пинг всегда возвращался ложным, независимо от веб-сервера. Я изменил его, чтобы просто использовать ping server.comсинтаксис.
Чак Каллебс
-1
let webSite = 'https://google.com/' 
https.get(webSite, function (res) {
    // If you get here, you have a response.
    // If you want, you can check the status code here to verify that it's `200` or some other `2xx`.
    console.log(webSite + ' ' + res.statusCode)
}).on('error', function(e) {
    // Here, an error occurred.  Check `e` for the error.
    console.log(e.code)
});;

если вы запустите это с узлом, он будет поддерживать лог 200, пока Google не выключен.

LifterCoder
источник
нет, это не ... 1) вам не хватает, требуется, 2) он возвращается 301 Переехал
Михал Мики Янковский
-1
const ping = (url, timeout = 6000) => {
  return new Promise((reslove, reject) => {
    const urlRule = new RegExp('(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]');
    if (!urlRule.test(url)) reject('invalid url');
    try {
      fetch(url)
        .then(() => reslove(true))
        .catch(() => reslove(false));
      setTimeout(() => {
        reslove(false);
      }, timeout);
    } catch (e) {
      reject(e);
    }
  });
};

используйте как это:

ping('https://stackoverflow.com/')
  .then(res=>console.log(res))
  .catch(e=>console.log(e))
王玉 略
источник
-4

Вы можете запустить команду DOS ping.exe из javaScript, используя следующую строку:

function ping(ip)
{
    var input = "";
    var WshShell = new ActiveXObject("WScript.Shell");
    var oExec = WshShell.Exec("c:/windows/system32/ping.exe " + ip);

    while (!oExec.StdOut.AtEndOfStream)
    {
            input += oExec.StdOut.ReadLine() + "<br />";
    }
    return input;
}

Это то, о чем просили, или я что-то упустил?

garryg
источник
4
К сожалению, это будет работать только в IE и только на Windows Server. Лучшим вариантом было бы использовать AJAX для запуска сценария на стороне сервера.
Эндрю Грот
Какого черта? Активные X объекты могут исполняться в файловой системе пользователя (в IE на win server)? Я не работал с ActiveX, но не
думал,
-6

просто замени

file_get_contents

с участием

$ip = $_SERVER['xxx.xxx.xxx.xxx'];
exec("ping -n 4 $ip 2>&1", $output, $retval);
if ($retval != 0) { 
  echo "no!"; 
} 
else{ 
  echo "yes!"; 
}
Джерри Вики
источник
1
Вы должны отредактировать свой предыдущий ответ, а не добавлять новый «частичный».
Артемикс
-8

Это может быть намного проще, чем все это. Если вы хотите, чтобы ваша страница загружалась, а затем проверьте доступность или содержимое какой-либо внешней страницы, чтобы вызвать другие действия веб-страницы, вы можете сделать это, используя только javascript и php, как это.

yourpage.php

<?php
if (isset($_GET['urlget'])){
  if ($_GET['urlget']!=''){
    $foreignpage= file_get_contents('http://www.foreignpage.html');
    // you could also use curl for more fancy internet queries or if http wrappers aren't active in your php.ini
    // parse $foreignpage for data that indicates your page should proceed
    echo $foreignpage; // or a portion of it as you parsed
    exit();  // this is very important  otherwise you'll get the contents of your own page returned back to you on each call
  }
}
?>

<html>
  mypage html content
  ...

<script>
var stopmelater= setInterval("getforeignurl('?urlget=doesntmatter')", 2000);

function getforeignurl(url){
  var handle= browserspec();
  handle.open('GET', url, false);
  handle.send();
  var returnedPageContents= handle.responseText;
  // parse page contents for what your looking and trigger javascript events accordingly.
  // use handle.open('GET', url, true) to allow javascript to continue executing. must provide a callback function to accept the page contents with handle.onreadystatechange()
}
function browserspec(){
  if (window.XMLHttpRequest){
    return new XMLHttpRequest();
  }else{
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
}

</script>

Это должно сделать это.

Триггерный JavaScript должен включать clearInterval (stopmelater)

Дайте мне знать, если это работает для вас

Джерри

Джерри Вики
источник
2
Как я уже говорил, это не веб-серверы, на которых я пингуюсь. Это игровые серверы. Они не будут отвечать на запросы HTTP. :(
Чак Каллебс
чувак, просто замените file_get_contents на
Джерри Вики
$ ip = $ _SERVER ['127.0.0.1']; exec ("ping -n 4 $ ip 2> & 1", $ output, $ retval); if ($ retval! = 0) {echo "no!"; } else {echo "yes!"; }
Джерри Вики
4
Это не вопрос PHP. Это вопрос JavaScript. Я не использую PHP.
Чак Каллебс
1
Пожалуйста, не используйте подписи или слоганы в своих сообщениях, иначе они будут удалены. Смотрите FAQ для деталей.
Артемикс
-13

Вы можете попробовать использовать PHP на своей веб-странице ... примерно так:

<html><body>
<form method="post" name="pingform" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<h1>Host to ping:</h1>
<input type="text" name="tgt_host" value='<?php echo $_POST['tgt_host']; ?>'><br>
<input type="submit" name="submit" value="Submit" >
</form></body>
</html>
<?php

$tgt_host = $_POST['tgt_host'];
$output = shell_exec('ping -c 10 '. $tgt_host.');

echo "<html><body style=\"background-color:#0080c0\">
<script type=\"text/javascript\" language=\"javascript\">alert(\"Ping Results: " . $output . ".\");</script>
</body></html>";

?>

Это не проверено, поэтому может иметь опечатки и т.д ... но я уверен, что это будет работать. Может быть улучшено тоже ...

Крис
источник
5
Согласно тегам OP, это на самом деле вопрос Ruby on Rails, поэтому использование PHP не является хорошим решением.
Жестянщик
1
Не начинать спор здесь, но разве этот форум не поможет?
Крис
5
@Chris Да, но какую помощь вы оказали OP, который использует Ruby и JS в своем приложении, предоставляя код PHP?
Бифоб