É possível executar ping em um servidor a partir de Javascript?

152

Estou criando um aplicativo Web que exige que eu verifique se os servidores remotos estão online ou não. Quando o executo a partir da linha de comando, o carregamento da minha página chega aos 60 anos (para 8 entradas, ele será dimensionado linearmente com mais).

Eu decidi seguir a rota do ping no final do usuário. Dessa forma, eu posso carregar a página e esperar que os dados do "servidor estejam online" enquanto navego no meu conteúdo.

Se alguém tiver a resposta para a pergunta acima ou se souber uma solução para manter minha página carregada rapidamente, eu certamente apreciaria.

Chuck Callebs
fonte

Respostas:

134

Eu encontrei alguém que realiza isso com um uso muito inteligente do Imageobjeto nativo .

Da fonte deles, essa é a função principal (ela depende de outras partes da fonte, mas você entendeu).

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

  }
}

Isso funciona em todos os tipos de servidores que eu testei (servidores web, servidores ftp e servidores de jogos). Também funciona com portas. Se alguém encontrar um caso de uso que falhar, poste nos comentários e eu atualizarei minha resposta.

Atualização : o link anterior foi removido. Se alguém encontrar ou implementar o acima, comente e eu o adicionarei na resposta.

Atualização 2 : @trante foi bom o suficiente para fornecer um jsFiddle.

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

Atualização 3 : o @Jonathon criou um repositório do GitHub com a implementação.

https://github.com/jdfreder/pingjs

Atualização 4 : Parece que esta implementação não é mais confiável. As pessoas também estão relatando que o Chrome não suporta mais tudo, gerando um net::ERR_NAME_NOT_RESOLVEDerro. Se alguém puder verificar uma solução alternativa, colocarei isso como a resposta aceita.

Chuck Callebs
fonte
48
É isso que eu tenho usado. Porém, ele tem uma falha e é que a "imagem" é armazenada em cache. Quando sigo um determinado IP inicialmente, recebo 304 ms - mas se o fizer uma segunda vez sem recarregar a página, recebo 2 ms. Isso pode ser evitado anexando "/?cachebreaker="+new Date().getTime();a ao final do img src, se necessário.
Meshaal
2
isso não está funcionando para mim, um nome de host inválido conhecido resulta na solicitação de ping e não foi possível encontrar o host .... mas desde onerror é 'bom' essa coisa diz que respondeu
Maslow
5
Mais testes revelaram que isso é totalmente não confiável.
Leo
2
A API Ping criada pela @Jonathon irá executar ping em tudo com êxito. Sites que não existem e caracteres aleatórios.
IE5Master
2
Na verdade, a API Ping sempre falha com o onerror. NO ENTANTO, se o URL de destino denota uma imagem, ele dispara uma sobrecarga, o que é incrível! Ignora verificações do CORS.
Martin Vysny 02/02
20

O ping é ICMP, mas se houver alguma porta TCP aberta no servidor remoto, isso pode ser alcançado da seguinte maneira:

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
fonte
Eu gosto desta solução complicada. Ter que fornecer uma função para pong em vez de retornar um número é um pouco estranho para mim, mas viável.
24413 Nick
@armen: você precisa fornecer uma função como terceiro argumento para ping (), o valor desse argumento é chamado de pong dentro da função.
finitud
2
ping("example.com", "77", function(m){ console.log("It took "+m+" miliseconds."); })..... chamada de exemplo
jave.web 18/10/2015
@ Nick: Isso é coisa assíncrona, então, no momento em que o método retorna, o item onreadystatechangeainda não foi acionado. Isso significa que você precisa de um retorno de chamada para atender quando o estado mudar.
temor
Tudo o que eu escolher para host, pong () é chamado. ping ( test.zzzzzzzzz, "77", function (m) {console.log ("Demorou" + m + "milissegundos.");}) imprime "Demorou 67 milissegundos". ping ( stackoverflow.com, "80", function (m) {console.log ("Demorou" + m + "milissegundos.");}) fornece um erro de CORS: developer.mozilla.org/en-US/docs/Web/ HTTP / CORS / Erros /… Não vejo como posso verificar com este código se um computador remoto está online.
Tux
18

você pode tentar isso:

coloque ping.html no servidor com ou sem conteúdo, no javascript faça o mesmo que abaixo:

<script>
    function ping(){
       $.ajax({
          url: 'ping.html',
          success: function(result){
             alert('reply');
          },     
          error: function(result){
              alert('timeout/error');
          }
       });
    }
</script>
jerjer
fonte
1
Os servidores que estou executando o ping não são meus, então não tenho essa opção. Obrigado embora.
Chuck Callebs
9
@dlchambers Não é devido ao CORS, mas DEVIDO A um domínio diferente. O CORS permite que um servidor especifique as origens e ele precisa ser suportado pelo navegador. portanto, é mais um problema entre domínios.
Royi Namir
Os pedidos do tipo HEAD também não funcionarão por causa do CORS. Testado com o Chromium 55. Recebendo o mesmo erro com o código http 0, como no caso de net :: CONNECTION_REFUSED, por exemplo.
Martin Vysny 02/02
Este é mais um navegador cruzado de forma amigável.
Gabriel
14

Você não pode "pingar" diretamente em javascript. Pode haver algumas outras maneiras:

  • Ajax
  • Usando um applet java com isReachable
  • Escrevendo um script do lado do servidor que faz ping e usando o AJAX para se comunicar com o lado do servidor
  • Você também pode executar ping no flash (actionscript)
Eugene
fonte
1
O isReachable do Java não é confiável ... Mas, bem, é 2018 e os Java Applets estão desatualizados.
Dreua
8

Você não pode executar ping regularmente no Javascript do navegador, mas pode descobrir se o servidor remoto está ativo, carregando, por exemplo, uma imagem do servidor remoto. Se o carregamento falhar -> servidor inoperante.

Você pode até calcular o tempo de carregamento usando onload-event. Aqui está um exemplo de como usar o evento onload.

Epeli
fonte
4
O lamentável é que os servidores que estou executando o ping não são servidores da web. Eles são servidores de jogos.
Chuck Callebs
1
Então você deve executar o ping no servidor - ou pode considerar um servidor http leve.
Epeli
Você conhece uma maneira de fazer pings agregados? Essa é a minha principal desaceleração, aguardando a conclusão de uma solicitação antes que a outra comece.
Chuck Callebs
Você precisa executar ping nos servidores simultaneamente. Você pode conseguir isso com seleção, threads ou processos. Para uma solução realmente hardcore, você pode usar o Eventmachine.
Epeli
1
Você pode aproveitar o comando da linha de pingcomandos. É força industrial. Ou, há todos os tipos de aplicativos de código aberto / gratuitos para monitorar se um host está sendo executado, usando vários tipos de verificações de pulsação.
the Tin Man
7

Apresentando uma solução 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
fonte
A isUp();chamada não deveria estar no onopenmanipulador de eventos? :)
jave.web
1
Ele usa o protocolo websocket, mas assume que não há realmente um servidor websocket aguardando uma conexão. Isso é apenas para executar ping em 'qualquer IP antigo'.
Antony Woods
Mas como você pode detectar se o WebSocket simplesmente não é suportado ou se realmente ocorreu algum erro? :)
jave.web
Se você acha que terá azar o suficiente para executar ping em um IP que possa aceitar uma conexão de websocket na porta 80, sim, você também deve adicionar isUp();esse retorno de chamada. Ou reduza isso adicionando uma porta distintamente não-websocket-y.
Antony Woods
Votei isso porque vim aqui à procura de uma solução para o que acontece quando meu servidor Websocket é reinicializado, e essa resposta me mostrou que eu poderia definir Timeout no manipulador de erros do soquete e tentar reconectar (espera ocupada). Provavelmente não é uma boa prática, mas resolve meu problema. :)
mynameisnafe
6

Para manter suas solicitações rápidas, armazene em cache os resultados do ping no servidor e atualize o arquivo ou o banco de dados ping a cada dois minutos (ou por mais preciso que você queira). Você pode usar o cron para executar um comando shell com seus 8 pings e gravar a saída em um arquivo, o servidor da Web incluirá esse arquivo em sua visualização.

user456733
fonte
Eu acho que essa é a maneira mais confiável com a melhor possível para obter resultados precisos. No entanto, uma vez que ainda requer alguns trabalhos no servidor, eu diria que não é uma maneira inteligente.
Chetabahana 24/03
5

Se o que você está tentando ver é se o servidor "existe", você pode usar o seguinte:

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

Isso retornará uma indicação verdadeira / falsa se o servidor existe.

Se você deseja tempo de resposta, uma pequena modificação fará:

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

O uso é então trivial:

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

Ou:

var responseInMillis = ping("example.com");
alert(responseInMillis);
Ohad
fonte
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
vonbrand
1
Esta é uma ótima solução, mas, infelizmente, o serviço de consulta de domínio do Yahoo parece fornecer resultados variáveis. por exemplo, se você tentar os seguintes URLs, begin-download .com beginfreedownload .com eles retornam como não registrados, mas se você acessar esses URLs, eles claramente serão registrados. alguma idéia do que está acontecendo aqui? (Eu adicionei um espaço para os nomes de domínio nestes exemplos, apenas para evitar dando suco de google no meu teste eu não ter um espaço neles.)
user280109
Você está fazendo uma solicitação ao Yahoo! API, o ping não será o mesmo se você fizer uma solicitação da sua máquina. Não faz sentido #
Andre Figueiredo
Este causas No 'Access-Control-Allow-Origin' header is present on the requested resource. Origine sucesso callback nunca é chamado
vladkras
4

O problema dos pings padrão é que eles são ICMP, que muitos lugares não deixam passar por razões de segurança e tráfego . Isso pode explicar o fracasso.

O Ruby anterior ao 1.9 tinha um baseado em TCP ping.rb, que será executado com o Ruby 1.9+. Tudo o que você precisa fazer é copiá-lo da instalação 1.8.7 para outro lugar. Acabei de confirmar que seria executado executando ping no meu roteador doméstico.

o homem de lata
fonte
4

Há muitas respostas loucas aqui e especialmente sobre o CORS -

Você pode fazer uma solicitação http HEAD (como GET, mas sem carga útil). Consulte https://ochronus.com/http-head-request-good-uses/

NÃO precisa de uma verificação prévia, a confusão ocorre devido a uma versão antiga da especificação, consulte Por que uma solicitação HEAD de origem cruzada precisa de uma verificação prévia?

Então você pode usar a resposta acima, que está usando a biblioteca jQuery (não disse), mas com

type: 'HEAD'

--->

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

Claro que você também pode usar vanilla js ou dojo ou qualquer outra coisa ...

sebilasse
fonte
1
O envio da solicitação HEAD ainda falha com "XMLHttpRequest não pode carregar o IP. Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Portanto, a origem 'ip2' não tem acesso permitido." Tão bloqueado pelo CORS. Você receberá sucesso com o status http 0 e, portanto, não poderá diferenciá-lo de net :: ERR_CONNECTION_REFUSED por exemplo
Martin Vysny
0

Não sei qual versão do Ruby você está executando, mas você tentou implementar o ping para ruby ​​em vez de javascript? http://raa.ruby-lang.org/project/net-ping/

wajiw
fonte
Sim, eu tentei isso, mas os pings sempre voltavam falsos, independentemente do servidor da web. Eu mudei para usar apenas ping server.comsintaxe.
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)
});;

se você executar isso com o nó, o console 200 será registrado, desde que o google não esteja inativo.

LifterCoder
fonte
não, não é ... 1) está faltando, 2) está retornando 301 Movido permanentemente: 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);
    }
  });
};

use assim:

ping('https://stackoverflow.com/')
  .then(res=>console.log(res))
  .catch(e=>console.log(e))
王玉 略
fonte
-4

Você pode executar o comando DOS ping.exe no javaScript usando o seguinte:

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

Foi isso que foi solicitado ou estou faltando alguma coisa?

garryg
fonte
4
Infelizmente, isso funcionará apenas no IE e apenas no Windows Server. Uma opção melhor seria usar o AJAX para executar um script do lado do servidor.
Andrew Grothe
Que diabos? Objetos ativos do X podem executar no sistema de arquivos do usuário (no IE no servidor win)? Eu não tinha trabalhado com ActiveX, mas não imaginava que ...
Julix
-6

apenas substitua

file_get_contents

com

$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
fonte
1
Você deve editar sua resposta anterior em vez de adicionar uma nova 'parcial'.
Artemix
-8

Pode ser muito mais fácil do que tudo isso. Se você deseja que sua página carregue, verifique a disponibilidade ou o conteúdo de alguma página estrangeira para acionar outras atividades da página da web, você pode fazê-lo usando apenas javascript e php como este.

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>

Isso deve resolver.

O javascript acionado deve incluir clearInterval (stopmelater)

Deixe-me saber se isso funciona para você

Jerry

Jerry Wickey
fonte
2
Como eu disse antes, esses não são servidores da Web que estou executando ping. Estes são servidores de jogos. Eles não responderão às solicitações HTTP. :(
Chuck Callebs
cara, basta substituir file_get_contents com
Jerry Wickey
$ ip = $ _SERVER ['127.0.0.1']; exec ("ping -n 4 $ ip 2> & 1", $ output, $ retval); if ($ retval! = 0) {echo "não!"; } else {echo "yes!"; }
Jerry Wickey 23/03
4
Esta não é uma pergunta sobre PHP. Esta é uma pergunta sobre JavaScript. Eu não uso PHP.
Chuck Callebs
1
Não use assinaturas ou slogans nas suas postagens, ou elas serão removidas. Consulte as Perguntas frequentes para obter detalhes.
Artemix
-13

Você pode tentar usar PHP na sua página da Web ... algo como isto:

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

?>

Isso não foi testado, portanto pode haver erros de digitação, etc ... mas estou confiante de que funcionaria. Poderia ser melhorado também ...

Chris
fonte
5
De acordo com as tags do OP, essa é realmente uma pergunta sobre Ruby on Rails, portanto, usar PHP não é uma boa solução.
o homem de lata
1
Não é para iniciar uma discussão aqui, mas não é o objetivo deste fórum ajudar?
26410 Chris
5
@ Chris Sim, mas que tipo de ajuda você deu ao OP que usa Ruby e JS em seu aplicativo fornecendo código PHP?
biphobe