¿Es posible hacer ping a un servidor desde Javascript?

152

Estoy creando una aplicación web que requiere que verifique si los servidores remotos están en línea o no. Cuando lo ejecuto desde la línea de comandos, la carga de mi página sube a 60 segundos (para 8 entradas, se escalará linealmente con más).

Decidí seguir la ruta del ping en el extremo del usuario. De esta manera, puedo cargar la página y hacer que esperen a que los datos del "servidor esté en línea" mientras exploran mi contenido.

Si alguien tiene la respuesta a la pregunta anterior, o si conocen una solución para mantener mi página cargada rápidamente, definitivamente lo agradecería.

Chuck Callebs
fuente

Respuestas:

134

He encontrado a alguien que logra esto con un uso muy inteligente del Imageobjeto nativo .

Desde su fuente, esta es la función principal (tiene dependencias en otras partes de la fuente, pero se entiende la idea).

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

  }
}

Esto funciona en todos los tipos de servidores que he probado (servidores web, servidores ftp y servidores de juegos). También funciona con puertos. Si alguien encuentra un caso de uso que falla, publique en los comentarios y actualizaré mi respuesta.

Actualización : se ha eliminado el enlace anterior. Si alguien encuentra o implementa lo anterior, comente y lo agregaré a la respuesta.

Actualización 2 : @trante fue lo suficientemente amable como para proporcionar un jsFiddle.

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

Actualización 3 : @Jonathon creó un repositorio de GitHub con la implementación.

https://github.com/jdfreder/pingjs

Actualización 4 : Parece que esta implementación ya no es confiable. La gente también informa que Chrome ya no lo admite todo, arrojando un net::ERR_NAME_NOT_RESOLVEDerror. Si alguien puede verificar una solución alternativa, la pondré como la respuesta aceptada.

Chuck Callebs
fuente
48
Esto es lo que he estado usando. Sin embargo, tiene un defecto, y es que la "imagen" está en caché. Cuando hago ping a una IP dada inicialmente, obtengo 304 ms, pero si la hago ping por segunda vez sin volver a cargar la página, obtengo 2 ms en su lugar. Esto podría evitarse agregando "/?cachebreaker="+new Date().getTime();a al final del img src si es necesario.
Meshaal
2
esto no funciona para mí, un nombre de host incorrecto conocido da como resultado que la solicitud de Ping no pueda encontrar el host .... pero como onerror es 'bueno', esto dice que respondió
Maslow
55
Más pruebas revelaron que esto es totalmente poco confiable.
Leo
2
La API de ping que creó @Jonathon hará ping exitosamente a todo. Sitios que no existen y caracteres aleatorios.
IE5Master
2
La API Ping en realidad siempre falla con onerror. SIN EMBARGO si la URL de destino denota una imagen, dispara una carga que es increíble. Omite cheques CORS.
Martin Vysny
20

Ping es ICMP, pero si hay algún puerto TCP abierto en el servidor remoto, se podría lograr así:

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
fuente
Me gusta esta solución complicada. Tener que proporcionar una función para pong en lugar de devolver un número es un poco extraño para mí, pero viable.
Nick
@armen: debe proporcionar una función como tercer argumento para ping (), el valor de este argumento se llama pong dentro de la función.
finitud
2
ping("example.com", "77", function(m){ console.log("It took "+m+" miliseconds."); })..... llamada de ejemplo
jave.web
@Nick: Esto es algo asíncrono, por lo que en el momento en que el método regresa, onreadystatechangeaún no se ha activado . Esto significa que necesita una devolución de llamada para responder cuando cambia el estado.
asombro
Lo que elijo para el host, se llama pong (). ping ( test.zzzzzzzzz, "77", function (m) {console.log ("Tomó" + m + "milisegundos");}) imprime "Tomó 67 milisegundos". ping ( stackoverflow.com, "80", function (m) {console.log ("Tomó" + m + "milisegundos");}) da un error CORS: developer.mozilla.org/en-US/docs/Web/ HTTP / CORS / Errores / ... No veo cómo puedo verificar con este código si una computadora remota está en línea.
Tux
18

puedes probar esto:

coloque ping.html en el servidor con o sin contenido, en javascript haga lo siguiente:

<script>
    function ping(){
       $.ajax({
          url: 'ping.html',
          success: function(result){
             alert('reply');
          },     
          error: function(result){
              alert('timeout/error');
          }
       });
    }
</script>
jerjer
fuente
1
Los servidores que estoy haciendo ping no son míos, así que no tengo esa opción. Gracias sin embargo.
Chuck Callebs
9
@dlchambers No se debe a CORS sino DEBIDO a la política de dominio cruzado. CORS permite que un servidor especifique los orígenes y además debe ser compatible con el navegador. entonces es más como un problema de dominio cruzado.
Royi Namir
Las solicitudes de tipo HEAD tampoco funcionarán debido a CORS. Probado con Chromium 55. Recibiendo el mismo error con el código http 0 que en el caso de net :: CONNECTION_REFUSED, por ejemplo.
Martin Vysny
Esto es más cruzado firendly.
Gabriel
14

No puede "hacer ping" directamente en javascript. Puede haber algunas otras formas:

  • Ajax
  • Usar un applet java con isReachable
  • Escribir un script del lado del servidor que suena y usar AJAX para comunicarse con el script del servidor
  • También puede hacer ping en flash (actionscript)
Eugene
fuente
1
El isReachable de Java es poco confiable ... Pero bueno, es 2018 y los Applets de Java están desactualizados de todos modos.
Dreua
8

No puede hacer ping regular en Javascript del navegador, pero puede averiguar si el servidor remoto está vivo, por ejemplo, cargando una imagen desde el servidor remoto. Si la carga falla -> servidor inactivo.

Incluso puede calcular el tiempo de carga utilizando onload-event. Aquí hay un ejemplo de cómo usar el evento onload.

Epeli
fuente
44
Lo desafortunado de eso es que los servidores que estoy haciendo ping no son servidores web. Son servidores de juegos.
Chuck Callebs el
1
Luego tiene que hacer ping en el lado del servidor, o podría considerar un servidor http ligero.
Epeli
¿Conoces una forma de hacer pings agregados? Esa es mi desaceleración principal, esperando que una solicitud termine antes de que comience la otra.
Chuck Callebs el
Necesita hacer ping a los servidores al mismo tiempo. Puede lograr eso con select, hilos o procesos. Para una solución realmente hardcore, puede usar Eventmachine.
Epeli
1
Puede considerar aprovechar el comando de línea de pingcomandos. Es la fuerza industrial. O bien, hay todo tipo de aplicaciones gratuitas / de código abierto para monitorear si se está ejecutando un host, utilizando varios tipos de controles de latidos.
El Hombre de hojalata
7

Presentando una solución 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
fuente
¿No debería ser la isUp();llamada en el onopencontrolador de eventos? :)
jave.web
1
Utiliza el protocolo websocket pero supone que en realidad no hay un servidor websocket esperando una conexión. Esto es solo para hacer ping a 'cualquier IP vieja'.
Antony Woods
Pero, ¿cómo puede detectar si WebSocket simplemente no es compatible o si realmente hubo algún error? :)
jave.web
Si cree que va a ser mala suerte de ping a una IP que podría aceptar una conexión WebSocket en el puerto 80, entonces sí, usted debe también añadir isUp();a la devolución de llamada. O mitigue eso agregando un puerto claramente no websocket-y.
Antony Woods
Lo voté porque vine aquí buscando una solución a lo que sucede cuando mi servidor websocket se reinicia, y esta respuesta me mostró que podía configurar Timeout en el controlador de errores del socket e intentar reconectarme (espera ocupada). Probablemente no sea la mejor práctica, pero resuelve mi problema. :)
mynameisnafe
6

Para mantener sus solicitudes rápidas, guarde en caché los resultados del ping del lado del servidor y actualice el archivo o la base de datos de ping cada dos minutos (o la precisión que desee). Puede usar cron para ejecutar un comando de shell con sus 8 pings y escribir la salida en un archivo, el servidor web incluirá este archivo en su vista.

usuario456733
fuente
Creo que esta es la forma más confiable con el mejor resultado posible. Sin embargo, dado que todavía requiere algunos trabajos en el servidor, diría que no es una forma inteligente.
Chetabahana
5

Si lo que intenta ver es si el servidor "existe", puede usar lo siguiente:

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

Esto devolverá una indicación de verdadero / falso si el servidor existe.

Si desea tiempo de respuesta, una ligera modificación hará:

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

El uso es entonces trivial:

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

O:

var responseInMillis = ping("example.com");
alert(responseInMillis);
Ohad
fuente
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
vonbrand
1
Esta es una gran solución, pero desafortunadamente, el servicio de consulta de dominio de Yahoo parece dar resultados variables. por ejemplo, si prueba las siguientes URL, comience a descargar .com beginfreedownload .com, volverán como no registradas, pero si va a esas URL, es evidente que están registradas. alguna idea de lo que está pasando aquí? (He añadido un espacio para los nombres de dominio en estos ejemplos, sólo para evitar dar jugo de Google en mi prueba no tuve un espacio en ellos.)
user280109
Estás haciendo una solicitud a Yahoo! API, el ping no será el mismo si hace una solicitud desde su máquina. Esto no tiene sentido
Andre Figueiredo
Esto provoca que la No 'Access-Control-Allow-Origin' header is present on the requested resource. Origindevolución de llamada de éxito nunca se llame
vladkras
4

El problema con los pings estándar es que son ICMP, que muchos lugares no dejan pasar por razones de seguridad y tráfico . Eso podría explicar el fracaso.

Ruby anterior a 1.9 tenía un TCP basado ping.rb, que se ejecutará con Ruby 1.9+. Todo lo que tiene que hacer es copiarlo de la instalación 1.8.7 a otro lugar. Acabo de confirmar que se ejecutará haciendo ping a mi enrutador doméstico.

el hombre de hojalata
fuente
4

Aquí hay muchas respuestas locas y especialmente sobre CORS:

Podrías hacer una solicitud http HEAD (como GET pero sin carga útil). Ver https://ochronus.com/http-head-request-good-uses/

NO necesita una verificación previa, la confusión se debe a una versión anterior de la especificación, consulte ¿Por qué una solicitud HEAD de origen cruzado necesita una verificación previa?

Entonces, podría usar la respuesta anterior que usa la biblioteca jQuery (no lo dijo) pero con

type: 'HEAD'

--->

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

Por supuesto, también puedes usar vanilla js o dojo o lo que sea ...

sebilasse
fuente
1
El envío de la solicitud HEAD todavía falla con "XMLHttpRequest no puede cargar IP. No hay encabezado 'Access-Control-Allow-Origin' en el recurso solicitado. Por lo tanto, Origin 'ip2' no tiene acceso permitido". Así bloqueado por CORS. Recibirá el éxito con el estado http 0 y, por lo tanto, no podrá diferenciarlo de net :: ERR_CONNECTION_REFUSED, por ejemplo
Martin Vysny,
0

No sé qué versión de Ruby está ejecutando, pero ¿ha intentado implementar ping para ruby ​​en lugar de JavaScript? http://raa.ruby-lang.org/project/net-ping/

wajiw
fuente
Sí, lo he intentado pero los pings siempre son falsos, independientemente del servidor web. Lo cambié para usar solo la ping server.comsintaxis.
Chuck Callebs el
-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)
});;

si ejecuta esto con el nodo, la consola registraría 200 siempre que Google no esté inactivo.

LifterCoder
fuente
no, no es ... 1) faltan requieren, 2) está regresando 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);
    }
  });
};

usar así:

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

Puede ejecutar el comando DOS ping.exe desde javaScript utilizando lo siguiente:

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

¿Es esto lo que se pidió o me falta algo?

Garryg
fuente
44
Desafortunadamente, esto solo funcionará en IE y solo en un servidor de Windows. Una mejor opción sería usar AJAX para ejecutar un script del lado del servidor.
Andrew Grothe
¿Que demonios? ¿Los objetos Active X pueden ejecutarse en el sistema de archivos del usuario (en IE en el servidor win)? No había trabajado con ActiveX pero no me lo imaginaba ...
Julix
-6

solo reemplace

file_get_contents

con

$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
fuente
1
Debería editar su respuesta anterior en lugar de agregar una nueva 'parcial'.
Artemix
-8

Puede ser mucho más fácil que todo eso. Si desea que su página se cargue, verifique la disponibilidad o el contenido de alguna página extranjera para activar otra actividad de la página web, puede hacerlo usando solo javascript y 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>

Deberias hacer eso.

El JavaScript activado debe incluir clearInterval (stopmelater)

Avísame si eso funciona para ti

alemán

Jerry Wickey
fuente
2
Como dije antes, estos no son servidores web que estoy haciendo ping. Estos son servidores de juegos. No responderán a las solicitudes HTTP. :(
Chuck Callebs
amigo, solo reemplaza file_get_contents con
Jerry Wickey
$ ip = $ _SERVER ['127.0.0.1']; exec ("ping -n 4 $ ip 2> & 1", $ salida, $ retval); if ($ retval! = 0) {echo "no!"; } else {echo "¡sí!"; }
Jerry Wickey
44
Esta no es una pregunta PHP. Esta es una pregunta de JavaScript. Yo no uso PHP.
Chuck Callebs
1
No utilices firmas o lemas en tus publicaciones, o se eliminarán. Vea las preguntas frecuentes para más detalles.
Artemix
-13

Podría intentar usar PHP en su página web ... algo como esto:

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

?>

Esto no está probado, por lo que puede tener errores tipográficos, etc. pero estoy seguro de que funcionaría. También podría mejorarse ...

Chris
fuente
55
De acuerdo con las etiquetas del OP, esta es realmente una pregunta de Ruby on Rails, por lo que usar PHP no es una buena solución.
The Tin Man
1
No para comenzar una discusión aquí, pero ¿no es el objetivo de este foro ayudar?
Chris
55
@ Chris Lo es, pero ¿qué tipo de ayuda le diste a OP que usa Ruby y JS en su aplicación al proporcionar código PHP?
biphobe