Czy można pingować serwer z JavaScript?

152

Tworzę aplikację internetową, która wymaga sprawdzenia, czy zdalne serwery są online, czy nie. Kiedy uruchamiam go z wiersza poleceń, ładowanie mojej strony wzrasta do pełnych 60 sekund (dla 8 wpisów będzie skalować liniowo z większą liczbą).

Postanowiłem pójść drogą pingowania po stronie użytkownika. W ten sposób mogę załadować stronę i po prostu pozwolić im czekać na dane „serwer jest online” podczas przeglądania moich treści.

Jeśli ktoś ma odpowiedź na powyższe pytanie lub zna sposób na szybkie ładowanie mojej strony, na pewno bym to docenił.

Chuck Callebs
źródło

Odpowiedzi:

134

Znalazłem kogoś, kto osiąga to dzięki bardzo sprytnemu wykorzystaniu natywnego Imageobiektu.

Z ich źródła jest to główna funkcja (ma zależności od innych części źródła, ale masz pomysł).

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

  }
}

Działa to na wszystkich typach serwerów, które testowałem (serwery WWW, serwery ftp i serwery gier). Działa również z portami. Jeśli ktoś napotka przypadek użycia, który się nie powiedzie, napisz w komentarzach, a zaktualizuję moją odpowiedź.

Aktualizacja : poprzedni link został usunięty. Jeśli ktoś znajdzie lub zastosuje powyższe, proszę o komentarz, a ja dodam to do odpowiedzi.

Aktualizacja 2 : @trante było na tyle miłe, aby zapewnić jsFiddle.

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

Aktualizacja 3 : @Jonathon utworzył repozytorium GitHub z implementacją.

https://github.com/jdfreder/pingjs

Aktualizacja 4 : Wygląda na to, że ta implementacja nie jest już niezawodna. Ludzie zgłaszają również, że Chrome nie obsługuje już tego wszystkiego, wyrzucając net::ERR_NAME_NOT_RESOLVEDbłąd. Jeśli ktoś może zweryfikować alternatywne rozwiązanie, umieszczę je jako zaakceptowaną odpowiedź.

Chuck Callebs
źródło
48
To jest to, czego używałem. Ma jednak jedną wadę, a mianowicie to, że „obraz” jest buforowany. Kiedy początkowo pinguję dany adres IP, otrzymuję 304 ms - ale jeśli pinguję go po raz drugi bez ponownego ładowania strony, zamiast tego otrzymuję 2 ms. Można tego uniknąć dodając a "/?cachebreaker="+new Date().getTime();na końcu img src, jeśli to konieczne.
Meshaal
2
to nie działa dla mnie, znana zła nazwa hosta powoduje, że żądanie Ping nie może znaleźć hosta .... ale ponieważ onerror jest „dobry”, ta rzecz mówi, że odpowiedziała
Maslow
5
Dalsze testy wykazały, że jest to całkowicie niewiarygodne.
Leo,
2
Interfejs Ping API utworzony przez @Jonathon z powodzeniem pinguje wszystko. Witryny, które nie istnieją i losowe znaki.
IE5Master
2
Ping API faktycznie zawsze kończy się niepowodzeniem z onerror. JEDNAK jeśli docelowy adres URL oznacza obraz, uruchamia się on w trybie onload, co jest niesamowite! Pomija kontrole CORS.
Martin Vysny
20

Ping to ICMP, ale jeśli na zdalnym serwerze jest otwarty port TCP, można to osiągnąć w następujący sposób:

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
  }

}

Nils Holgersson
źródło
Podoba mi się to trudne rozwiązanie. Konieczność zapewnienia funkcji dla ponga zamiast zwracania liczby jest dla mnie trochę dziwna, ale wykonalna.
Nick
@armen: musisz podać funkcję jako trzeci argument funkcji ping (), wartość tego argumentu w funkcji nazywa się pong.
finitud
2
ping("example.com", "77", function(m){ console.log("It took "+m+" miliseconds."); })..... przykładowa rozmowa
jave.web
@Nick: To jest asynchroniczna rzecz, więc w momencie powrotu metody onreadystatechangenie została jeszcze uruchomiona . Oznacza to, że potrzebujesz oddzwonienia, aby odebrać, gdy zmieni się stan.
awe
Cokolwiek wybiorę jako hosta, nazywane jest pong (). ping ( test.zzzzzzzzz, "77", function (m) {console.log ("Zajęło to" + m + "milisekund.");}) wyświetla "Zajęło to 67 milisekund." ping ( stackoverflow.com, "80", function (m) {console.log ("To zajęło" + m + "milisekund.");}) daje błąd CORS: developer.mozilla.org/en-US/docs/Web/ HTTP / CORS / Errors /… Nie widzę, jak mogę sprawdzić tym kodem, czy zdalny komputer jest online.
Tux
18

możesz spróbować tego:

umieść ping.html na serwerze z treścią lub bez, w javascript zrób to samo, co poniżej:

<script>
    function ping(){
       $.ajax({
          url: 'ping.html',
          success: function(result){
             alert('reply');
          },     
          error: function(result){
              alert('timeout/error');
          }
       });
    }
</script>
jerjer
źródło
1
Serwery, do których pinguję, nie są moimi własnymi, więc nie mam takiej opcji. W każdym razie dzięki.
Chuck Callebs
9
@dlchambers nie należy do CORS, ale z powodu poilicy międzydomenowej. CORS umożliwia serwerowi określenie pochodzenia, a ponadto musi być obsługiwany przez przeglądarkę. więc jest to bardziej kwestia międzydomenowa.
Royi Namir,
Żądania typu HEAD również nie będą działać z powodu CORS. Testowane z Chromium 55. Otrzymuje ten sam błąd z kodem http 0, jak na przykład w przypadku net :: CONNECTION_REFUSED.
Martin Vysny
Jest to bardziej przyjazne dla różnych przeglądarek.
Gabriel
14

Nie możesz bezpośrednio „pingować” w javascript. Może być kilka innych sposobów:

  • Ajax
  • Korzystanie z apletu Java z isReachable
  • Pisanie skryptu po stronie serwera, który pinguje i używa AJAX do komunikowania się ze skryptem serwera
  • Możesz także być w stanie pingować we flashu (skrypt działań)
Eugene
źródło
1
Java isReachable jest dość zawodna ... Ale cóż, mamy 2018 rok, a aplety Java i tak są przestarzałe.
dreua
8

Nie możesz wykonać zwykłego pingowania w Javascript przeglądarki, ale możesz sprawdzić, czy zdalny serwer działa, na przykład ładując obraz ze zdalnego serwera. Jeśli ładowanie się nie powiedzie -> serwer nie działa.

Możesz nawet obliczyć czas ładowania za pomocą zdarzenia onload. Oto przykład użycia zdarzenia onload.

Epeli
źródło
4
Niefortunne jest to, że serwery, do których pinguję, nie są serwerami WWW. To serwery gier.
Chuck Callebs
1
Następnie musisz wykonać pingowanie po stronie serwera - lub możesz rozważyć lekki serwer http.
Epeli
Czy znasz sposób wykonywania zbiorczych pingów? To moje główne spowolnienie, czekanie na zakończenie jednego żądania przed rozpoczęciem drugiego.
Chuck Callebs
Musisz jednocześnie pingować serwery. Możesz to osiągnąć za pomocą selekcji, wątków lub procesów. Dla naprawdę hardkorowego rozwiązania możesz użyć Eventmachine.
Epeli
1
Możesz chcieć skorzystać z polecenia wiersza pingpolecenia. To siła przemysłowa. Lub istnieją różnego rodzaju bezpłatne / otwarte aplikacje do monitorowania, czy host jest uruchomiony, przy użyciu różnych rodzajów kontroli pulsu.
Tin Man
7

Dołączanie do rozwiązania Websocket ...

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);
}
Antony Woods
źródło
Czy isUp();połączenie nie powinno być w onopenmodule obsługi zdarzeń? :)
jave.web
1
Używa protokołu WebSocket, ale zakłada, że w rzeczywistości nie ma serwera WebSocket czekającego na połączenie. Służy to tylko do pingowania „dowolnego starego adresu IP”.
Antony Woods
Ale jak można wykryć, czy protokół WebSocket po prostu nie jest obsługiwany lub czy rzeczywiście wystąpił jakiś błąd? :)
jave.web
Jeśli myślisz, że będziesz miał pecha, aby pingować adres IP, który może akceptować połączenie WebSocket na porcie 80, to tak, powinieneś również dodać isUp();do tego wywołania zwrotnego. Lub złagodzić to, dodając wyraźnie inny port sieciowy-y.
Antony Woods
Głosowałem za to, ponieważ przyszedłem tutaj, szukając rozwiązania tego, co się dzieje, gdy mój serwer WebSocket zostanie ponownie uruchomiony, a ta odpowiedź pokazała mi, że mogę ustawić limit czasu w module obsługi błędów gniazda i spróbować ponownie się połączyć (zajęte oczekiwanie). Prawdopodobnie nie jest to najlepsza praktyka, ale rozwiązuje mój problem. :)
mynameisnafe
6

Aby żądania były szybkie, buforuj wyniki polecenia ping po stronie serwera i aktualizuj plik ping lub bazę danych co kilka minut (lub jakkolwiek chcesz, aby była dokładna). Możesz użyć crona, aby uruchomić polecenie powłoki z 8 pingami i zapisać wynik do pliku, serwer sieciowy włączy ten plik do twojego widoku.

user456733
źródło
Myślę, że jest to najbardziej niezawodny sposób z najlepszym możliwym do dokładnego wyniku. Jednak ponieważ nadal wymaga to trochę pracy na serwerze, powiedziałbym, że nie jest to mądry sposób.
Chetabahana
5

Jeśli próbujesz sprawdzić, czy serwer „istnieje”, możesz użyć następującego:

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

To zwróci prawdę / fałsz wskazujące, czy serwer istnieje.

Jeśli chcesz uzyskać czas odpowiedzi, wystarczy niewielka modyfikacja:

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

Użycie jest wtedy trywialne:

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

Lub:

var responseInMillis = ping("example.com");
alert(responseInMillis);
Ohad
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
vonbrand
1
jest to świetne rozwiązanie, ale niestety usługa zapytań domeny yahoo wydaje się dawać zmienne wyniki. na przykład, jeśli spróbujesz następujących adresów URL, begin-download .com beginfreedownload .com wrócą jako niezarejestrowane, ale jeśli przejdziesz do tych adresów URL, są one wyraźnie zarejestrowane. jakiś pomysł, co się tutaj dzieje? (dodałem spację do nazw domen w tych przykładach, aby uniknąć wydawania soku Google. w moim teście nie miałem w nich spacji)
user280109
Wysyłasz żądanie do Yahoo! API, ping nie będzie nawet taki sam, jeśli wykonasz żądanie ze swojego komputera. To nie ma sensu
Andre Figueiredo
To powoduje No 'Access-Control-Allow-Origin' header is present on the requested resource. Origini sukces oddzwonienia nigdy nie jest wywoływany
vladkras
4

Problem ze standardowymi pingami polega na tym, że są to ICMP, przez które wiele miejsc nie przepuszcza ze względów bezpieczeństwa i ruchu . To może wyjaśniać niepowodzenie.

Ruby przed 1.9 miał oparty na TCP ping.rb, który będzie działał z Ruby 1.9+. Wszystko, co musisz zrobić, to skopiować go z instalacji 1.8.7 do innego miejsca. Właśnie potwierdziłem, że będzie działać, wysyłając polecenie ping do mojego domowego routera.

Blaszany Człowiek
źródło
4

Jest tu wiele szalonych odpowiedzi, a zwłaszcza dotyczących CORS -

Możesz wykonać żądanie HTTP HEAD (jak GET, ale bez ładunku). Zobacz https://ochronus.com/http-head-request-good-uses/

NIE wymaga kontroli wstępnej, zamieszanie jest spowodowane starą wersją specyfikacji, zobacz Dlaczego żądanie HEAD z innych źródeł wymaga kontroli wstępnej?

Więc możesz użyć powyższej odpowiedzi, która używa biblioteki jQuery (nie powiedział tego), ale z

type: 'HEAD'

--->

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

Oczywiście możesz także użyć vanilla js, dojo lub cokolwiek innego ...

sebilasse
źródło
1
Wysłanie żądania HEAD nadal kończy się niepowodzeniem z komunikatem „XMLHttpRequest nie może załadować adresu IP. Żądany zasób nie zawiera nagłówka„ Access-Control-Allow-Origin ”. W związku z tym źródło„ ip2 ”nie ma dostępu.” Więc zablokowany przez CORS. Otrzymasz sukces ze statusem http 0, a zatem nie będziesz w stanie odróżnić tego od na przykład net :: ERR_CONNECTION_REFUSED
Martin Vysny
0

Nie wiem, jakiej wersji Rubiego używasz, ale czy próbowałeś zaimplementować ping dla Rubiego zamiast javascript? http://raa.ruby-lang.org/project/net-ping/

wajiw
źródło
Tak, próbowałem, ale pingi zawsze były fałszywe, niezależnie od serwera WWW. Zmieniłem to, aby po prostu używać ping server.comskładni.
Chuck Callebs
-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)
});;

jeśli uruchomisz to z węzłem, konsola zarejestruje 200, o ile Google nie jest wyłączone.

LifterCoder
źródło
nie, to nie ... 1) brakuje ci wymagania, 2) powraca 301 Przeniesiono na stałe: D
Michal Miky Jankovský
-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);
    }
  });
};

użyj w ten sposób:

ping('https://stackoverflow.com/')
  .then(res=>console.log(res))
  .catch(e=>console.log(e))
王玉 略
źródło
-4

Polecenie ping.exe dla systemu DOS można uruchomić z poziomu javaScript w następujący sposób:

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

Czy o to proszono, czy czegoś mi brakuje?

garryg
źródło
4
Działa to niestety tylko w IE i tylko na Windows Server. Lepszym rozwiązaniem byłoby użycie AJAX do uruchomienia skryptu po stronie serwera.
Andrew Grothe,
Co za cholera? Obiekty Active X mogą być wykonywane w systemie plików użytkownika (w IE na serwerze Win)? Nie pracowałem z ActiveX, ale nie wyobrażałem sobie, że ...
Julix,
-6

po prostu wymień

file_get_contents

z

$ip = $_SERVER['xxx.xxx.xxx.xxx'];
exec("ping -n 4 $ip 2>&1", $output, $retval);
if ($retval != 0) { 
  echo "no!"; 
} 
else{ 
  echo "yes!"; 
}
Jerry Wickey
źródło
1
Zamiast dodawać nową „częściową” odpowiedź, należy edytować poprzednią odpowiedź .
Artemix
-8

To może być o wiele łatwiejsze niż to wszystko. Jeśli chcesz, aby Twoja strona się załadowała, sprawdź dostępność lub zawartość jakiejś obcej strony, aby wywołać inną aktywność na stronie internetowej, możesz to zrobić za pomocą tylko javascript i php, takiego jak ten.

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>

Że należy to zrobić.

Uruchomiony skrypt javascript powinien zawierać clearInterval (stopmelater)

Daj mi znać, jeśli to działa dla Ciebie

Nocnik

Jerry Wickey
źródło
2
Jak powiedziałem wcześniej, to nie są serwery WWW, do których pinguję. To są serwery gier. Nie będą odpowiadać na żądania HTTP. :(
Chuck Callebs
koleś, po prostu zamień file_get_contents na
Jerry Wickey
$ ip = $ _SERVER ['127.0.0.1']; exec ("ping -n 4 $ ip 2> & 1", $ wyjście, $ retval); if ($ retval! = 0) {echo "nie!"; } else {echo "tak!"; }
Jerry Wickey
4
To nie jest pytanie PHP. To jest pytanie JavaScript. Nie używam PHP.
Chuck Callebs,
1
Nie używaj podpisów ani sloganów w swoich postach, bo zostaną one usunięte. Zobacz FAQ, aby uzyskać szczegółowe informacje.
Artemix
-13

Możesz spróbować użyć PHP na swojej stronie internetowej ... coś takiego:

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

?>

To nie jest testowane, więc może zawierać literówki itp ... ale jestem pewien, że zadziała. Można by też poprawić ...

Chris
źródło
5
Według znaczników OP jest to w rzeczywistości kwestia Ruby on Rails, więc używanie PHP nie jest dobrym rozwiązaniem.
Tin Man
1
Nie zaczynać tutaj kłótni, ale czy celem tego forum nie jest pomoc?
Chris
5
@Chris To jest, ale jakiego rodzaju pomoc udzieliłaś OP, który używa Rubiego i JS w swojej aplikacji, dostarczając kod PHP?
biphobe