NodeJS - Cosa significa effettivamente "socket hang up"?


277

Sto costruendo un raschietto web con Node e Cheerio, e per un certo sito Web sto ottenendo il seguente errore (succede solo su questo sito Web, nessun altro che provo a raschiare.

Succede in una posizione diversa ogni volta, quindi a volte è url xche genera l'errore, altre volte url xva bene ed è un URL completamente diverso:

    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)

È molto difficile eseguire il debug, non so davvero da dove cominciare. Per cominciare, che cosa È una presa di riagganciare errore? È un errore 404 o simile? O significa semplicemente che il server ha rifiutato una connessione?

Non riesco a trovare una spiegazione di questo da nessuna parte!

EDIT: ecco un esempio di codice che a volte restituisce errori:

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

Non c'è una chiamata diretta per chiudere la connessione, ma sto usando Node Requestquale (per quanto posso dire) usa http.getquindi non è necessario, correggimi se sbaglio!

EDIT 2: Ecco un vero e proprio bit di codice in uso che causa errori. prodURLe altre variabili sono per lo più selettori jquery definiti in precedenza. Questo utilizza la asynclibreria per 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);
                }
            ]);
    });
}

26
Significa che il socket non invia l' endevento di connessione entro il periodo di timeout. Se ricevi la richiesta di cheerio tramite http.request(non http.get). Devi chiamare request.end()per terminare l'invio della richiesta.
user568109

1
@ user568109 Devo notare che sto usando il requestservizio nodo , non una http.requestrichiesta specifica (penso, sono molto nuovo nel nodo!). Questo è quello: github.com/mikeal/request Sembra che termini automaticamente la richiesta, no? EDIT: secondo i documenti, http method, defaults to GETquindi non è questo il problema.
JVG,

2
Quindi non dovrebbe essere il problema. Cosa succede se si commenta la parte di scraping incluso cheerio.load e si restituisce lo stesso contenuto. Il trucco qui è, cheerio.loadè asincrono. Quindi potrebbe non finire prima di iniziare a fare cose con $.
user568109

4
A volte ho anche scoperto che se eseguo la scansione di un sito in modo troppo aggressivo (come più di 10 connessioni simultanee), inizieranno a rispondere con le interruzioni dei socket, quindi potrebbe essere anche quello.
tobek,

1
Proprio FYI, in inglese, hang upsignifica terminare una conversazione elettronica tagliando la connessione ; nasce dal riagganciare il telefono vecchio stile.
Константин Ван,

Risposte:


161

Ci sono due casi quando socket hang upviene lanciato:

Quando sei un cliente

Quando, come client, invii una richiesta a un server remoto e non ricevi una risposta tempestiva. Il tuo socket è terminato e genera questo errore. Dovresti rilevare questo errore e decidere come gestirlo: ritentare la richiesta, metterla in coda per dopo, ecc.

Quando sei un server / proxy

Quando tu, come server, forse un server proxy, ricevi una richiesta da un client, quindi inizi ad agire su di esso (o inoltra la richiesta al server upstream), e prima di aver preparato la risposta, il client decide di annullare / interrompere la richiesta.

Questa traccia dello stack mostra cosa succede quando un client annulla la richiesta.

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 linea http.js:1526:9punta allo stesso socketCloseListenermenzionato sopra da @Blender, in particolare:

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

Questo è un caso tipico se il client è un utente nel browser. La richiesta di caricamento di alcune risorse / pagine richiede molto tempo e gli utenti semplicemente aggiornano la pagina. Tale azione provoca l'interruzione della richiesta precedente che sul lato server genera questo errore.

Poiché questo errore è causato dal desiderio di un client, non si aspettano di ricevere alcun messaggio di errore. Pertanto, non è necessario considerare questo errore come critico. Ignoralo. Ciò è incoraggiato dal fatto che su tale errore il ressocket ascoltato dal client viene, sebbene sia ancora scrivibile, distrutto.

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

Quindi, nessun punto per inviare nulla, tranne la chiusura esplicita dell'oggetto risposta:

res.end();

Tuttavia, ciò che si dovrebbe fare per certo se si è un server proxy che ha già inoltrato la richiesta al monte, è per interrompere la richiesta interna al monte, indicando la vostra mancanza di interesse per la risposta, che a sua volta dire il monte server per, forse, interrompere un'operazione costosa.


2
Come posso, come cliente, far aspettare la richiesta più a lungo? Si sta verificando un errore a 35 secondi e ho bisogno di aspettare circa un minuto.
Big Money,

Sto affrontando lo stesso problema. È possibile attendere la risposta e iniziare a inviare la richiesta successiva come un'esecuzione una alla volta. Posso sapere come gestire questo socket riagganciato ?.
Deepak,

@BigMoney che potresti usare setTimeout(). vedi questa domanda: stackoverflow.com/questions/6214902/…
the holla

I tuoi dettagli mi sono sopravvissuti dall'inferno, stavo usando node.js come server proxy tra server upstream e client, il timeout su richiesta ha generato questo errore solo perché mi sono dimenticato di usare res.send, grazie
Farzad YZ

Puoi ricevere "socket hang up" come client quando stai provando a fare una seconda richiesta a un server web di sviluppo di Django sulla stessa connessione. Non supporta keep-alive. E nel caso in cui il tuo cliente lo preveda, ricevi l'errore. Guarda come segue .
x-yuri,

53

Dai un'occhiata alla fonte :

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

Il messaggio viene emesso quando il server non invia mai una risposta.


2
Da un punto di vista funzionale puoi spiegare cosa significa? Sto cercando di creare protezioni qui aggiungendo gli URL offensivi a un array e raschiandoli in seguito. Ho letto in alcuni punti che gli errori potrebbero essere un problema di accodamento con Node, non conosco il modo migliore per rimediare ed evitarlo.
JVG

5
Ma quanto tempo aspetta?
CommaToast,

2
Dovrebbe usare en.wikipedia.org/wiki/Exponential_backoff per l'implementazione di "quanto tempo".
Norman H,

questo "socket hang up" non ha senso. Questa è solo una sorpresa dal team di nodejs.
puchu,

45

Un caso degno di nota: quando mi connetto da Node.js a Node.js usando Express, ottengo "socket hang up" se non antepongo il percorso URL richiesto con "/".


1
quello era il mio problema, sia client che server in puro http node.js
ashley willis,

1
@silentorb: puoi per favore mostrare l'URL di esempio? Sto riscontrando lo stesso errore in questo caso .. Grazie.
Pritam,

4
Errore: "user / login", Success: "/ user / login"
silentorb

4
Amico, ho passato quasi un'ora a debug! Ho visto la tua risposta e ho pensato SH **, aggiunto il / e funziona benissimo :) grazie!
Daniel Gruszczyk,

4
Mi hai risparmiato ore con questa risposta!
imhotep

32

ero solito require('http') a consumare https servizio e lo ha dimostrato " socket hang up".

Poi invece sono passato require('http')a require('https'), e funziona.


Anche se questo potrebbe essere una soluzione al problema, è non è una risposta alla domanda. Il poster voleva una risposta sul significato del messaggio di errore. Inoltre, ci sono già molte risposte di alta qualità. Il tuo non fornisce valore aggiunto.
Johannes Dorn,

19
Grazie per il tuo commento. Perdo questo tempo per questo errore. Alla fine, provo solo questa soluzione e funziona. Voglio solo condividere. Spero che sia utile per gli altri non perdere tempo, non per lodare come risposta di alta qualità.
Aekkawit Chanpen

12
@JohannesDorn Questa è una risposta implicita alla domanda sul significato dell'errore. E utile a quello.
Ulad Kasach,

30

di seguito è riportato un semplice esempio in cui ho riscontrato lo stesso errore quando ho perso l'aggiunta del codice commentato nell'esempio di seguito. Uncommenting il codice req.end()risolverà questo 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();

2
Questo mi ha salvato la sanità mentale ... Grazie!
PGallagher,

Sei un eroe! Grazie.
Xenhat

17

Espandendo la risposta di Blender, ciò accade in diverse situazioni. I più comuni in cui mi imbatto sono:

  1. Il server si è bloccato.
  2. Il server ha rifiutato la connessione, molto probabilmente bloccato da User-Agent.

socketCloseListener, come indicato nella risposta di Blender, non è l'unico luogo in cui vengono creati gli errori di riaggancio.

Ad esempio, trovato qui :

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

Potresti provare curlcon le intestazioni e quelle che vengono inviate dal Nodo e vedere se ottieni una risposta lì. Se non ricevi una risposta con curl, ma ricevi una risposta nel tuo browser, User-Agentè molto probabile che l' intestazione venga bloccata.


3
Un altro motivo per cui il server potrebbe rifiutare la tua connessione (ho appena toccato questo quando si passa a prod anziché QA), è se il tuo server si aspetta una richiesta https invece di http.
Mc il

7

Un altro caso degno di nota (per Linux e OS X) è che se si utilizza una libreria come httpsper eseguire le richieste o se si passa https://...come URL dell'istanza servita localmente, si utilizzerà la porta 443che è una porta privata riservata e l'utente potrebbe finire in Socket hang upoECONNREFUSED errori.

Utilizzare invece port 3000, fe ed eseguire una httprichiesta.


6

Ho avuto lo stesso problema durante l'utilizzo della libreria Nano per connettermi a Couch DB . Ho provato a mettere a punto il pool di connessioni con l'uso della libreria keepaliveagent e ha continuato a fallire con il messaggio di riaggancio del socket .

var KeepAliveAgent = require('agentkeepalive');

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

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

Dopo un po 'di fatica sono stato in grado di risolvere il problema - come è emerso è stato un errore molto, molto semplice. Mi stavo connettendo al database tramite protocollo HTTPS, ma continuavo a passare al mio oggetto nano un agente keepalive creato come esempio per l'uso di questo programma librario (si basano su alcune impostazioni predefinite che usano http).

Una semplice modifica per usare HttpsAgent ha funzionato:

var KeepAliveAgent = require('agentkeepalive').HttpsAgent;

1
Per un po 'più di dettaglio, se la richiesta è configurata per la porta 443 e la richiesta viene emessa tramite il modulo http anziché il modulo https, si ottiene un socket riagganciato. Sarebbe bello se ci fossero maggiori dettagli sul perché si è verificata la disconnessione (negoziazione SSL / TLS?). Ho visto quel livello di dettaglio in ASP.NET per esempio.
Richard Collette,

6

Questo mi ha causato problemi, poiché stavo facendo tutto ciò che è elencato qui, ma continuavo a ricevere errori. Si scopre che la chiamata a req.abort () genera effettivamente un errore, con un codice ECONNRESET, quindi è necessario rilevarlo nel gestore degli errori.

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

5

Per gli requestutenti dei moduli

timeout

Esistono due tipi principali di timeout: timeout di connessione e timeout di lettura . Si verifica un timeout di connessione se il timeout viene raggiunto mentre il client sta tentando di stabilire una connessione a una macchina remota (corrispondente alla connect()chiamata sul socket). Si verifica un timeout di lettura ogni volta che il server è troppo lento per rispedire una parte della risposta.

Notare che i timeout di connessione generano un ETIMEDOUTerrore e che i timeout di lettura generano un ECONNRESETerrore.


3

Ho avuto lo stesso problema durante la richiesta ad alcuni server. Nel mio caso, l'impostazione di qualsiasi valore su User-Agent nelle intestazioni nelle opzioni di richiesta mi ha aiutato.

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

Non è un caso generale e dipende dalle impostazioni del server.


2

Anche la ragione può essere dovuta all'uso appdell'istanza di expressanziché di serverdaconst server = http.createServer(app) durante la creazione socket server.

Sbagliato

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

Corretta

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

1

Faccio sia lo sviluppo web (nodo) che Android e apro insieme il simulatore e la finestra mobile del dispositivo Android Studio, entrambi usano la porta 8601, si è lamentato socket hang updell'errore, dopo aver chiuso il simulatore del dispositivo Android Studio e funziona bene sul lato nodo. Non utilizzare il simulatore e la finestra mobile del dispositivo Android Studio insieme.


1

Ho riscontrato un errore simile quando si utilizza CouchDB sul cluster 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
    }
  }

Che dovrebbe essere "http", non "https", per connettersi con la mia istanza CouchDB. Spero che possa essere utile per chiunque abbia problemi simili.


0

Nel mio caso, è stato perché una risposta application / json è stata formattata male (contiene una traccia dello stack). La risposta non è mai stata inviata al server. È stato molto difficile eseguire il debug perché non c'erano log. Questo thread mi aiuta molto a capire cosa succede.


0

Nel caso in cui si utilizzi il proxy nodo-http, tenere presente questo problema, che comporterà un errore di riaggancio del socket: https://github.com/nodejitsu/node-http-proxy/issues/180 .

Per la risoluzione, anche in questo collegamento, è sufficiente spostare dichiarando la route API (per il proxy) all'interno di route express prima di express.bodyParser ().


0

Ho affrontato questo problema ieri eseguendo la mia applicazione web e il server node.js tramite IntelliJ IDEA 2016.3.6. Tutto quello che dovevo fare era cancellare i miei cookie e cache nel mio browser Chrome.


0

Se si verifica questo errore su una connessione https e si verifica immediatamente, potrebbe essere un problema durante l'impostazione della connessione SSL.

Per me è stato questo problema https://github.com/nodejs/node/issues/9845 ma per te potrebbe essere qualcos'altro. Se si tratta di un problema con ssl, dovresti essere in grado di riprodurlo con il pacchetto nodejs tls / ssl solo provando a connetterti al dominio


0

Penso che valga la pena notare ...

Stavo creando test per le API di Google. Stavo intercettando la richiesta con un server improvvisato, quindi inoltrandoli alla vera API. Stavo tentando di passare semplicemente le intestazioni nella richiesta, ma alcune intestazioni stavano causando un problema con express dall'altra parte.

Vale a dire, ho dovuto eliminare connection, accepte le content-lengthintestazioni prima di utilizzare il modulo di richiesta per inoltrare.

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.
})

0

Nel mio caso non è stato un errore, ma un comportamento previsto per il browser Chrome. Chrome mantiene attiva la connessione tls (per la velocità credo), ma il server node.js la interrompe dopo 2 minuti e viene visualizzato un errore.

Se si tenta di ottenere la richiesta GET utilizzando il browser Edge, non si verificherà alcun errore. Se chiuderai la finestra di Chrome, otterrai subito un errore.

Quindi che si fa? 1) Puoi filtrare questi errori, perché non sono realmente errori. 2) Forse c'è una soluzione migliore :)


0

Sembra esserci un caso in più qui, che è Electron che non è un fan del nome di dominio "localhost". Nel mio caso avevo bisogno di cambiare questo:

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

a questa:

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

Dopodiché il problema è appena scomparso.

Ciò significa che anche la risoluzione DNS (locale o remota) potrebbe causare alcuni problemi.


0

Dopo un lungo debug nel codice js del nodo, nella stringa di connessione mongodb, nel controllo CORS ecc., Per me il solo passaggio a un numero di porta diverso l'ha server.listen(port);fatto funzionare, in postman, prova anche quello. Nessuna modifica alle proxyimpostazioni solo le impostazioni predefinite.


-1

Questo errore può anche verificarsi quando si lavora con http.request , probabilmente la tua richiesta non è ancora terminata.

Esempio:

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

E devi sempre aggiungere questa riga: req.end() con questa funzione ordineremo di completare l'invio della richiesta.

Come nella documentazione si dice:

Con http.request () si deve sempre chiamare req.end () per indicare la fine della richiesta, anche se non ci sono dati scritti nel corpo della richiesta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.