NodeJS - ¿Qué significa realmente "zócalo colgado"?

277

Estoy construyendo un raspador web con Node y Cheerio, y para un determinado sitio web obtengo el siguiente error (solo ocurre en este sitio web, no en otros que intento raspar).

Ocurre en una ubicación diferente cada vez, por lo que a veces es url xel error, otras veces url xestá bien y es una URL completamente diferente:

    Error!: Error: socket hang up using [insert random URL, it's different every time]

Error: socket hang up
    at createHangUpError (http.js:1445:15)
    at Socket.socketOnEnd [as onend] (http.js:1541:23)
    at Socket.g (events.js:175:14)
    at Socket.EventEmitter.emit (events.js:117:20)
    at _stream_readable.js:910:16
    at process._tickCallback (node.js:415:13)

Esto es muy difícil de depurar, realmente no sé por dónde empezar. Para comenzar, ¿qué ES un error de colgar el socket? ¿Es un error 404 o similar? ¿O solo significa que el servidor rechazó una conexión?

¡No puedo encontrar una explicación de esto en ningún lado!

EDITAR: Aquí hay una muestra de código que (a veces) devuelve errores:

function scrapeNexts(url, oncomplete) {
    request(url, function(err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        $ = cheerio.load(body);
        // do stuff with the '$' cheerio content here
    });
}

No hay una llamada directa para cerrar la conexión, pero estoy usando lo Node Requestque (por lo que puedo decir) usa, http.getasí que esto no es obligatorio, ¡corríjame si me equivoco!

EDITAR 2: Aquí hay un bit de código real en uso que está causando errores. prodURLy otras variables son en su mayoría selectores jquery que se definieron anteriormente. Esto usa la asyncbiblioteca para Node.

function scrapeNexts(url, oncomplete) {
    request(url, function (err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        async.series([
                function (callback) {
                    $ = cheerio.load(body);
                    callback();
                },
                function (callback) {
                    $(prodURL).each(function () {
                        var theHref = $(this).attr('href');
                        urls.push(baseURL + theHref);
                    });
                    var next = $(next_select).first().attr('href');
                    oncomplete(next);
                }
            ]);
    });
}
JVG
fuente
26
Significa que el socket no envía un endevento de conexión dentro del período de tiempo de espera. Si recibe la solicitud de cheerio a través de http.request(no http.get). Debe llamar request.end()para terminar de enviar la solicitud.
user568109
1
@ user568109 Debo señalar que estoy usando el requestservicio de nodo , no una http.requestsolicitud específica (¡creo que soy muy nuevo en el nodo!). Este es el siguiente: github.com/mikeal/request Parece que finaliza la solicitud automáticamente, ¿no? EDITAR: Según los documentos, http method, defaults to GETese no es el problema.
JVG
2
Entonces no debería ser el problema. ¿Qué sucede si comenta la parte de raspado que incluye cheerio.load y devuelve el mismo contenido? El problema aquí es, cheerio.loades asíncrono. Por lo tanto, puede que no termine antes de comenzar a hacer cosas con $.
user568109
44
A veces también he descubierto que si rastreo un sitio de manera demasiado agresiva (como más de 10 conexiones simultáneas) comenzarán a responder con problemas de conexión, por lo que podría ser eso también.
tobek
1
Solo FYI, en inglés, hang upsignifica terminar una conversación electrónica cortando la conexión ; se originó al colgar el teléfono antiguo.
Константин Ван

Respuestas:

162

Hay dos casos cuando socket hang upse lanza:

Cuando eres cliente

Cuando usted, como cliente, envía una solicitud a un servidor remoto y no recibe una respuesta oportuna. Su zócalo está terminado, lo que arroja este error. Debe detectar este error y decidir cómo manejarlo: si volver a intentar la solicitud, ponerla en cola para más adelante, etc.

Cuando eres un servidor / proxy

Cuando usted, como servidor, tal vez un servidor proxy, recibe una solicitud de un cliente, luego comienza a actuar sobre ella (o retransmite la solicitud al servidor ascendente), y antes de que haya preparado la respuesta, el cliente decide cancelar / cancelar la solicitud.

Este seguimiento de la pila muestra lo que sucede cuando un cliente cancela la solicitud.

Trace: { [Error: socket hang up] code: 'ECONNRESET' }
    at ClientRequest.proxyError (your_server_code_error_handler.js:137:15)
    at ClientRequest.emit (events.js:117:20)
    at Socket.socketCloseListener (http.js:1526:9)
    at Socket.emit (events.js:95:17)
    at TCP.close (net.js:465:12)

La línea http.js:1526:9apunta a lo mismo socketCloseListenermencionado anteriormente por @Blender, particularmente:

// This socket error fired before we started to
// receive a response. The error needs to
// fire on the request.
req.emit('error', createHangUpError());

...

function createHangUpError() {
  var error = new Error('socket hang up');
  error.code = 'ECONNRESET';
  return error;
}

Este es un caso típico si el cliente es un usuario en el navegador. La solicitud de cargar algún recurso / página lleva mucho tiempo, y los usuarios simplemente actualizan la página. Dicha acción hace que la solicitud anterior se anule, lo que en el lado del servidor arroja este error.

Dado que este error es causado por el deseo de un cliente, no esperan recibir ningún mensaje de error. Por lo tanto, no es necesario considerar este error como crítico. Simplemente ignóralo. Esto se ve alentado por el hecho de que, en tal error, el ressocket que escuchó su cliente es, aunque aún se puede escribir, destruido.

console.log(res.socket.destroyed); //true

Entonces, no tiene sentido enviar nada, excepto cerrar explícitamente el objeto de respuesta:

res.end();

Sin embargo, lo que debe hacer con certeza si es un servidor proxy que ya ha transmitido la solicitud a la cadena ascendente, es cancelar su solicitud interna a la cadena ascendente, lo que indica su falta de interés en la respuesta, que a su vez informará a la cadena ascendente servidor para, quizás, detener una operación costosa.

Ojo
fuente
2
¿Cómo puedo, como cliente, simplemente hacer que la solicitud espere más? Se está produciendo un error a los 35 segundos y necesito que espere aproximadamente un minuto.
Big Money
Estoy enfrentando el mismo problema. ¿Es posible esperar la respuesta y comenzar a enviar la siguiente solicitud como una ejecución una por una. ¿Puedo saber cómo manejar este socket colgado?
Deepak
@BigMoney que podrías usar setTimeout(). vea esta pregunta: stackoverflow.com/questions/6214902/…
the holla
Sus datos me sobrevivieron del infierno, estaba usando node.js como un servidor proxy entre el servidor ascendente y el cliente, el tiempo de espera por solicitud arrojó este error solo porque olvidé usarlo res.send, gracias
Farzad YZ
Puede recibir "cuelgue de socket" como cliente cuando intenta hacer una segunda solicitud al servidor web de desarrollo de Django a través de la misma conexión. No es compatible keep-alive. Y en caso de que su cliente lo espere, obtendrá el error. Se ve a lo largo de las siguientes líneas .
x-yuri
53

Echa un vistazo a la fuente :

function socketCloseListener() {
  var socket = this;
  var parser = socket.parser;
  var req = socket._httpMessage;
  debug('HTTP socket close');
  req.emit('close');
  if (req.res && req.res.readable) {
    // Socket closed before we emitted 'end' below.
    req.res.emit('aborted');
    var res = req.res;
    res.on('end', function() {
      res.emit('close');
    });
    res.push(null);
  } else if (!req.res && !req._hadError) {
    // This socket error fired before we started to
    // receive a response. The error needs to
    // fire on the request.
    req.emit('error', createHangUpError());
    req._hadError = true;
  }
}

El mensaje se emite cuando el servidor nunca envía una respuesta.

Licuadora
fuente
2
Desde una perspectiva funcional, ¿puedes explicar qué significa esto? Estoy tratando de construir salvaguardas aquí agregando las URL ofensivas a una matriz y luego eliminándolas más tarde. Leí en algunos lugares que los errores podrían ser un problema de cola con Node, no sé la mejor manera de remediarlo y evitarlo.
JVG
55
¿Pero cuánto tiempo espera?
CommaToast
2
Debería usar en.wikipedia.org/wiki/Exponential_backoff para la implementación de "cuánto tiempo".
Norman H
Este "enchufe" no tiene sentido. Esto es solo una sorpresa del equipo de nodejs.
puchu
45

Un caso que vale la pena mencionar: cuando me conecto desde Node.js a Node.js usando Express, obtengo un "bloqueo del socket" si no prefijo la ruta URL solicitada con "/".

silentorb
fuente
1
ese era mi problema, tanto el cliente como el servidor en http node.js puro
ashley willis
1
@silentorb: ¿Puedes mostrar una URL de ejemplo? Estoy enfrentando el mismo error en este caso ... Gracias.
Pritam
44
Error: "usuario / inicio de sesión", Éxito: "/ usuario / inicio de sesión"
silentorb
44
¡Hombre, he pasado casi una hora depurándolo! Vi su respuesta y pensó SH **, agregó el / y funciona bien :) ¡gracias!
Daniel Gruszczyk
44
¡Me salvaste horas con esta respuesta!
imhotep
32

Solía require('http')consumir el servicio https y mostraba " socket hang up".

Luego cambié require('http')a en su require('https')lugar, y está funcionando.

Aekkawit Chanpen
fuente
Si bien esto puede ser una solución al problema, es no una respuesta a la pregunta. El afiche quería una respuesta sobre el significado del mensaje de error. Además, ya hay muchas respuestas de alta calidad. La suya no proporciona valor adicional.
Johannes Dorn
19
Gracias por tu comentario. Pierdo mi tiempo por este error. Finalmente, solo intento esta solución y funciona. Solo quiero compartir. Espero que sea útil para que otros no pierdan su tiempo, no para alabar como respuesta de alta calidad.
Aekkawit Chanpen
12
@JohannesDorn Esta es una respuesta implícita a la pregunta de qué significa el error. Y uno útil en eso.
Ulad Kasach
30

A continuación se muestra un ejemplo simple donde obtuve el mismo error cuando no pude agregar el código comentado en el ejemplo a continuación. Descomentar el código req.end()resolverá este problema.

var fs = require("fs");
var https = require("https");

var options = {
    host: "en.wikipedia.org",
    path: "/wiki/George_Washington",
    port: 443,
    method: "GET"
};

var req = https.request(options, function (res) {
    console.log(res.statusCode);
});


// req.end();
Shiyas Cholamukhath
fuente
2
Esto salvó mi cordura ... ¡Gracias!
PGallagher
Eres un heroe! Gracias.
Xenhat, el
17

Ampliando la respuesta de Blender, esto sucede en una serie de situaciones. Los más comunes con los que me encuentro son:

  1. El servidor se bloqueó.
  2. El servidor rechazó su conexión, probablemente bloqueado por User-Agent.

socketCloseListener, como se describe en la respuesta de Blender, no es el único lugar donde se crean los errores de colgar.

Por ejemplo, encontrado aquí :

function socketOnEnd() {
  var socket = this;
  var req = this._httpMessage;
  var parser = this.parser;

  if (!req.res) {
    // If we don't have a response then we know that the socket
    // ended prematurely and we need to emit an error on the request.
    req.emit('error', createHangUpError());
    req._hadError = true;
  }
  if (parser) {
    parser.finish();
    freeParser(parser, req);
  }
  socket.destroy();
}

Puede intentar curlcon los encabezados y los que se envían desde Node y ver si obtiene una respuesta allí. Si no obtiene una respuesta con curl, pero sí obtiene una respuesta en su navegador, User-Agentes probable que su encabezado esté bloqueado.

Astuto
fuente
3
Otra razón por la que el servidor podría rechazar su conexión (acabo de presionar esto cuando me muevo a prod en lugar de QA), es si su servidor espera una solicitud https en lugar de http.
mcole
7

Otro caso que vale la pena mencionar (para Linux y OS X) es que si usa una biblioteca como httpspara realizar las solicitudes, o si pasa https://...como una URL de la instancia servida localmente, usará un puerto 443que es un puerto privado reservado y usted podría estar terminando en Socket hang upo ECONNREFUSEDerrores.

En su lugar, use port 3000, fe y haga una httpsolicitud.

Milkncookiez
fuente
6

Tuve el mismo problema al usar la biblioteca Nano para conectarme a Couch DB . Traté de ajustar la agrupación de conexiones con el uso de la biblioteca keepaliveagent y seguía fallando con el mensaje de colgar el socket .

var KeepAliveAgent = require('agentkeepalive');

var myagent = new KeepAliveAgent({
    maxSockets: 10,
    maxKeepAliveRequests: 0,
    maxKeepAliveTime: 240000
});

nano = new Nano({
    url : uri,
    requestDefaults : {
        agent : myagent
    }
});

Después de algunas dificultades pude resolver el problema, ya que salió como un error muy, muy simple. Me estaba conectando a la base de datos a través del protocolo HTTPS, pero seguía pasando a mi nanoobjeto un agente keepalive creado como muestran los ejemplos para el uso de esta biblioteca (se basan en algunos valores predeterminados que usan http).

Un simple cambio para usar HttpsAgent hizo el truco:

var KeepAliveAgent = require('agentkeepalive').HttpsAgent;
Marcin TP Łuczyński
fuente
1
Para un poco más de detalle, si la solicitud está configurada para el puerto 443 y la solicitud se emite a través del módulo http en lugar del módulo https, entonces se cuelga un socket. Sería bueno si hubiera más detalles sobre por qué ocurrió la desconexión (¿negociación SSL / TLS?). He visto ese nivel de detalle en ASP.NET, por ejemplo.
Richard Collette
6

Esto me causó problemas, ya que estaba haciendo todo lo que se enumera aquí, pero todavía recibía errores. Resulta que llamar a req.abort () en realidad arroja un error, con un código de ECONNRESET, por lo que en realidad debe detectarlo en su controlador de errores.

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        return;
    }
    //handle normal errors
});
Jeffrey Harmon
fuente
5

Para requestusuarios de módulos

Tiempos de espera

Hay dos tipos principales de tiempos de espera: tiempos de espera de conexión y tiempos de espera de lectura . Se produce un tiempo de espera de conexión si se alcanza el tiempo de espera mientras el cliente intenta establecer una conexión con una máquina remota (correspondiente a la connect()llamada en el zócalo). Se produce un tiempo de espera de lectura cada vez que el servidor es demasiado lento para enviar una parte de la respuesta.

Tenga en cuenta que los tiempos de espera de conexión emiten un ETIMEDOUTerror y los tiempos de espera de lectura emiten un ECONNRESETerror.

Константин Ван
fuente
3

Tuve el mismo problema durante la solicitud a algún servidor. En mi caso, configurar cualquier valor para User-Agent en los encabezados en las opciones de solicitud me ayudó.

const httpRequestOptions = {
    hostname: 'site.address.com',
    headers: {
       'User-Agent': 'Chrome/59.0.3071.115'
    }
};

No es un caso general y depende de la configuración del servidor.

AndreyU
fuente
2

También la razón puede deberse al uso de appinstancia de en expresslugar de serverdesde const server = http.createServer(app)mientras se crea el socket del servidor.

Incorrecto

const express = require('express');
const http = require('http');
const WebSocket = require('ws');


const app = express();

app.use(function (req, res) {
  res.send({ msg: "hello" });
});

const wss = new WebSocket.Server({ server: app }); // will throw error while connecting from client socket

app.listen(8080, function listening() {
  console.log('Listening on %d', server.address().port);
});

Correcto

const express = require('express');
const http = require('http');
const WebSocket = require('ws');


const app = express();

app.use(function (req, res) {
  res.send({ msg: "hello" });
});

const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

server.listen(8080, function listening() {
  console.log('Listening on %d', server.address().port);
});
Nikolay Podolnyy
fuente
1

Realizo desarrollo web (nodo) y Android, y abro el simulador de dispositivo Android Studio y el acoplador juntos, ambos usan el puerto 8601, se quejó de socket hang uperror, después de cerrar el simulador de dispositivo Android Studio y funciona bien en el lado del nodo. No use el simulador de dispositivo Android Studio y la ventana acoplable juntos.

Yao Li
fuente
1

Recibí un error similar al usar CouchDB en el clúster OCP.

const cloudantSessionStore = sessionStore.createSessionStore(
  {
    type: 'couchdb',
    host: 'https://' + credentials['host'],
    port: credentials['port'],
    dbName: 'sessions',
    options: {
      auth: {
        username: credentials['username'],
        password: credentials['password']
      },
      cache: false
    }
  }

Que debe ser "http", no "https", para conectarse con mi instancia CouchDB. Espero que pueda ser útil para cualquiera que se enfrente a un problema similar.

Jing DU
fuente
0

En mi caso, fue porque una respuesta de aplicación / json estaba mal formateada (contiene un seguimiento de pila). La respuesta nunca fue enviada al servidor. Fue muy difícil de depurar porque no había registro. Este hilo me ayuda mucho a entender lo que sucede.

jmcollin92
fuente
0

En caso de que esté utilizando node-http-proxy, tenga en cuenta este problema, que provocará un error de bloqueo de socket: https://github.com/nodejitsu/node-http-proxy/issues/180 .

Para la resolución, también en este enlace, simplemente mueva declarando la ruta API (para proxy) dentro de las rutas express antes de express.bodyParser ().

Thinhbk
fuente
0

Encontré este problema ayer ejecutando mi aplicación web y el servidor node.js a través de IntelliJ IDEA 2016.3.6. Todo lo que tenía que hacer era borrar mis cookies y caché en mi navegador Chrome.

Jordan.JD
fuente
0

Si está experimentando este error en una conexión https y está sucediendo instantáneamente, podría ser un problema configurar la conexión SSL.

Para mí fue este problema https://github.com/nodejs/node/issues/9845 pero para ti podría ser otra cosa. Si es un problema con el ssl, entonces debería poder reproducirlo con el paquete nodejs tls / ssl solo tratando de conectarse al dominio

BrightEyed
fuente
0

Creo que vale la pena señalar ...

Estaba creando pruebas para las API de Google. Estaba interceptando la solicitud con un servidor improvisado y luego los reenviaba a la API real. Intenté pasar los encabezados en la solicitud, pero algunos encabezados estaban causando un problema con express en el otro extremo.

Es decir, tuve que eliminar connection, accepty content-lengthencabezados antes de usar el módulo de solicitud para reenviar.

let headers = Object.assign({}, req.headers);
delete headers['connection']
delete headers['accept']
delete headers['content-length']
res.end() // We don't need the incoming connection anymore
request({
  method: 'post',
  body: req.body,
  headers: headers,
  json: true,
  url: `http://myapi/${req.url}`
}, (err, _res, body)=>{
  if(err) return done(err);
  // Test my api response here as if Google sent it.
})
Senica Gonzalez
fuente
0

En mi caso, no fue un error, sino un comportamiento esperado para el navegador Chrome. Chrome mantiene viva la conexión de tls (creo que para la velocidad), pero el servidor node.js la detiene después de 2 minutos y obtiene un error.

Si intenta solicitar GET utilizando el navegador de borde, no habrá ningún error en absoluto. Si cierra la ventana de Chrome, recibirá un error de inmediato.

¿Entonces lo que hay que hacer? 1) Puede filtrar estos errores, porque en realidad no son errores. 2) Tal vez hay una mejor solución :)

RTW
fuente
0

Parece que hay un caso adicional aquí, que es que Electron no es fanático del nombre de dominio "localhost". En mi caso, necesitaba cambiar esto:

const backendApiHostUrl = "http://localhost:3000";

a esto:

const backendApiHostUrl = "http://127.0.0.1:3000";

Después de eso, el problema simplemente desapareció.

Esto significa que la resolución DNS (local o remota) también podría estar causando algunos problemas.

Pawel Barcik
fuente
0

Después de una larga depuración en el código del nodo js, ​​cadena de conexión mongodb, comprobación de CORS, etc., para mí simplemente cambiar a un número de puerto diferente lo server.listen(port);hizo funcionar, en postman, intente eso también. No hay cambios en la proxyconfiguración solo los valores predeterminados.

Sunil Kumar
fuente
-1

Este error también puede ocurrir al trabajar con http.request, probablemente su solicitud aún no haya finalizado.

Ejemplo:

const req = https.request(options, res => {})

Y siempre necesita agregar esta línea: req.end() con esta función ordenaremos finalizar el envío de la solicitud.

Como en la documentación se dice:

Con http.request () siempre se debe llamar a req.end () para indicar el final de la solicitud, incluso si no se escriben datos en el cuerpo de la solicitud.

Predrag Davidovic
fuente