NodeJS - Que signifie réellement «socket raccrocher»?


277

Je construis un grattoir Web avec Node et Cheerio, et pour un certain site Web, j'obtiens l'erreur suivante (cela ne se produit que sur ce site Web, pas d'autres que j'essaie de gratter.

Cela se produit à un endroit différent à chaque fois, donc parfois c'est ce url xqui jette l'erreur, d'autres fois url xc'est bien et c'est une URL complètement différente:

    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)

C'est très difficile à déboguer, je ne sais pas vraiment par où commencer. Pour commencer, c'EST une prise raccrocher erreur? S'agit-il d'une erreur 404 ou similaire? Ou cela signifie-t-il simplement que le serveur a refusé une connexion?

Je ne trouve aucune explication à cela nulle part!

EDIT: Voici un exemple de code qui renvoie (parfois) des erreurs:

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

Il n'y a pas d'appel direct pour fermer la connexion, mais j'utilise Node Requestce qui (pour autant que je sache ) utilise http.getdonc ce n'est pas nécessaire, corrigez-moi si je me trompe!

EDIT 2: Voici un morceau de code en cours d'utilisation qui provoque des erreurs. prodURLet les autres variables sont principalement des sélecteurs jquery définis précédemment. Cela utilise la asyncbibliothèque de 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
Cela signifie que le socket n'envoie pas d' endévénement de connexion dans le délai imparti. Si vous recevez la demande de cheerio via http.request(pas http.get). Vous devez appeler request.end()pour terminer l'envoi de la demande.
— user568109

1
@ user568109 Je dois noter que j'utilise le requestservice de nœud , pas une http.requestdemande spécifique (je pense que je suis très nouveau sur le nœud!). C'est celui-ci: github.com/mikeal/request Il semble qu'il termine automatiquement la demande, non? EDIT: Selon les documents, http method, defaults to GETce n'est donc pas le problème.
— JVG

2
Alors ça ne devrait pas être le problème. Que se passe-t-il si vous commentez la partie de grattage, y compris cheerio.load, et renvoyez le même contenu. Le hic ici est, cheerio.loadest asynchrone. Il ne peut donc pas se terminer avant de commencer à faire des choses avec $.
— user568109

4
J'ai également constaté parfois que si j'explorais un site de manière trop agressive (comme plus de 10 connexions simultanées), il commencerait à répondre avec des raccords de socket, donc cela pourrait être aussi.
— tobek

1
Juste FYI, en anglais, hang upsignifie mettre fin à une conversation électronique en coupant la connexion ; originaire de raccrocher le téléphone à l'ancienne.
— Константин Ван

Réponses:


162

Il y a deux cas où socket hang upse jette:

Quand vous êtes client

Lorsque vous, en tant que client, envoyez une demande à un serveur distant et ne recevez aucune réponse en temps opportun. Votre socket est terminé, ce qui génère cette erreur. Vous devez intercepter cette erreur et décider comment la gérer: réessayez la demande, mettez-la en file d'attente pour plus tard, etc.

Lorsque vous êtes un serveur / proxy

Lorsque vous, en tant que serveur, peut-être un serveur proxy, recevez une demande d'un client, puis commencez à agir en conséquence (ou relayez la demande au serveur en amont), et avant d'avoir préparé la réponse, le client décide d'annuler / d'abandonner la demande.

Cette trace de pile montre ce qui se passe lorsqu'un client annule la demande.

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 ligne http.js:1526:9pointe vers le même que socketCloseListenermentionné ci-dessus par @Blender, en particulier:

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

Il s'agit d'un cas typique si le client est un utilisateur du navigateur. La demande de chargement d'une ressource / page prend du temps et les utilisateurs actualisent simplement la page. Une telle action provoque l'abandon de la demande précédente, ce qui, du côté serveur, génère cette erreur.

Étant donné que cette erreur est causée par le souhait d'un client, il ne s'attend pas à recevoir de message d'erreur. Donc, pas besoin de considérer cette erreur comme critique. N'y faites pas attention. Ceci est encouragé par le fait que sur une telle erreur, le ressocket que votre client a écouté est, bien qu'écritable, détruit.

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

Donc, inutile d'envoyer quoi que ce soit, sauf la fermeture explicite de l'objet de réponse:

res.end();

Cependant, ce que vous devez faire avec certitude si vous êtes un serveur proxy qui a déjà relayé la demande à l'amont, est d'abandonner votre demande interne à l'amont, indiquant votre manque d'intérêt pour la réponse, qui à son tour dira à l'amont serveur pour, peut-être, arrêter une opération coûteuse.


2
Comment puis-je, en tant que client, simplement attendre plus longtemps? C'est une erreur à 35 secondes et j'en ai besoin pour attendre environ une minute.
— Big Money

Je suis confronté au même problème. Est-il possible d'attendre la réponse et de commencer à envoyer la requête suivante comme une exécution une par une. Puis-je savoir comment gérer ce socket raccroché?.
— Deepak

@BigMoney que vous pourriez utiliser setTimeout(). voir cette question: stackoverflow.com/questions/6214902/…
— le holla

Vos détails m'ont survécu de l'enfer, j'utilisais node.js comme serveur proxy entre le serveur en amont et le client, le délai d'expiration sur demande a jeté cette erreur juste parce que j'ai oublié d'utiliser res.send, merci
— Farzad YZ

Vous pouvez recevoir "socket raccrocher" en tant que client lorsque vous essayez de faire une deuxième demande au serveur Web de développement de Django via la même connexion. Cela ne prend pas en charge keep-alive. Et si votre client s'y attend, vous obtenez l'erreur. Il ressemble aux lignes suivantes .
— x-yuri

53

Jetez un oeil à la source :

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

Le message est émis lorsque le serveur n'envoie jamais de réponse.


2
D'un point de vue fonctionnel, pouvez-vous expliquer ce que cela signifie? J'essaie de créer des garanties ici en ajoutant les URL incriminées à un tableau, puis en les supprimant plus tard. J'ai lu à quelques endroits que les erreurs pouvaient être un problème de file d'attente avec Node, je ne sais pas la meilleure façon de remédier et d'éviter cela.
— JVG

5
Mais combien de temps cela attend-il?
— CommaToast

2
Il doit utiliser en.wikipedia.org/wiki/Exponential_backoff pour l'implémentation de "combien de temps".
— Norman H

ce "raccrocher prise" n'a pas de sens. C'est juste une surprise de la part de l'équipe nodejs.
— puchu

45

Un cas mérite d'être mentionné: lors de la connexion de Node.js à Node.js à l'aide d'Express, j'obtiens "socket raccrocher" si je ne préfixe pas le chemin URL demandé avec "/".


1
c'était mon problème, à la fois le client et le serveur en pure http node.js
— ashley willis

1
@silentorb: Pouvez-vous s'il vous plaît montrer un exemple d'URL? Je fais face à la même erreur dans ce cas .. Merci.
— Pritam

4
Erreur: "utilisateur / connexion", Succès: "/ utilisateur / connexion"
— silentorb

4
Mec, j'ai passé presque une heure à le déboguer! Vu votre réponse et pensé SH **, a ajouté le / et cela fonctionne bien :) merci!
— Daniel Gruszczyk

4
Vous m'avez fait gagner des heures avec cette réponse!
— imhotep

32

J'avais l'habitude require('http')de consommer le service https et cela montrait " socket hang up".

Ensuite, je suis passé require('http')à la require('https')place, et cela fonctionne.


Bien que cela puisse être une solution au problème, ce n'est pas une réponse à la question. L'affiche voulait une réponse sur la signification du message d'erreur. De plus, il existe déjà de nombreuses réponses de haute qualité. Le vôtre n'apporte pas de valeur supplémentaire.
— Johannes Dorn

19
Merci pour ton commentaire. Je perds mon temps pour cette erreur. Enfin, je viens d'essayer cette solution et ça marche. Je veux juste partager. J'espère qu'il sera utile pour les autres de ne pas perdre leur temps, pas pour faire l'éloge d'une réponse de haute qualité.
— Aekkawit Chanpen

12
@JohannesDorn Il s'agit d'une réponse implicite à la question de la signification de l'erreur. Et utile à cela.
— Ulad Kasach

30

ci-dessous est un exemple simple où j'ai eu la même erreur lorsque j'ai manqué d'ajouter le code commenté dans l'exemple ci-dessous. La suppression de la mise en commentaire du code req.end()résoudra ce problème.

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
Cela a sauvé ma raison ... Merci!
— PGallagher

Tu es un héros! Je vous remercie.
— Xenhat

17

S'étendant sur la réponse de Blender, cela se produit dans un certain nombre de situations. Les plus courants que je rencontre sont:

  1. Le serveur est tombé en panne.
  2. Le serveur a refusé votre connexion, probablement bloquée par User-Agent.

socketCloseListener, comme indiqué dans la réponse de Blender, n'est pas le seul endroit où des erreurs de blocage sont créées.

Par exemple, on trouve ici :

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

Vous pouvez essayer curlavec les en-têtes et autres qui sont envoyés depuis Node et voir si vous obtenez une réponse. Si vous n'obtenez pas de réponse avec curl, mais que vous obtenez une réponse dans votre navigateur, votre en- User-Agenttête est très probablement bloqué.


3
Une autre raison pour laquelle le serveur pourrait refuser votre connexion (je viens de le toucher lorsque je passe à prod au lieu de QA), c'est si votre serveur attend une demande https au lieu de http.
— mcole

7

Un autre cas qui mérite d'être mentionné (pour Linux et OS X) est que si vous utilisez une bibliothèque comme httpspour effectuer les requêtes, ou si vous passez https://...comme URL de l'instance servie localement, vous utiliserez le port 443qui est un port privé réservé et vous pourrait se retrouver dans Socket hang upou des ECONNREFUSEDerreurs.

Utilisez plutôt port 3000, fe et faites une httprequête.


6

J'ai eu le même problème lors de l'utilisation de la bibliothèque Nano pour se connecter à Couch DB . J'ai essayé d'affiner le pool de connexions avec l'utilisation de la bibliothèque keepaliveagent et il a continué à échouer avec le message de raccrochage du socket .

var KeepAliveAgent = require('agentkeepalive');

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

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

Après quelques difficultés, j'ai pu résoudre le problème - comme il est sorti, c'était une erreur très, très simple. Je me connectais à la base de données via le protocole HTTPS, mais je continuais de transmettre à mon nano objet un agent keepalive créé comme les exemples d'utilisation de cette bibliothèque (ils s'appuient sur certains paramètres par défaut qui utilisent http).

Un simple changement pour utiliser HttpsAgent a fait l'affaire:

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

1
Pour un peu plus de détails, si la demande est configurée pour le port 443 et la demande est émise via le module http plutôt que le module https, alors vous obtenez un socket raccrocher. Ce serait bien s'il y avait plus de détails sur les raisons de la déconnexion (négociation SSL / TLS?). J'ai vu ce niveau de détail dans ASP.NET par exemple.
— Richard Collette

6

Cela m'a causé des problèmes, car je faisais tout ce qui est répertorié ici, mais des erreurs étaient toujours générées. Il s'avère que l'appel de req.abort () génère en fait une erreur, avec un code ECONNRESET, donc vous devez réellement l'attraper dans votre gestionnaire d'erreurs.

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

5

Pour requestles utilisateurs du module

Délais

Il existe deux principaux types de délais: les délais de connexion et les délais de lecture . Un délai d'expiration de connexion se produit si le délai d'expiration est atteint pendant que votre client tente d'établir une connexion avec une machine distante (correspondant à l' connect()appel sur le socket). Un délai de lecture se produit chaque fois que le serveur est trop lent pour renvoyer une partie de la réponse.

Notez que les délais d'expiration de connexion émettent une ETIMEDOUTerreur et que les délais d'expiration de lecture émettent une ECONNRESETerreur.


3

J'ai eu le même problème lors de la demande à un serveur. Dans mon cas, la définition d'une valeur sur User-Agent dans les en-têtes des options de demande m'a aidé.

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

Ce n'est pas un cas général et dépend des paramètres du serveur.


2

La raison peut également être due à l'utilisation de l' appinstance de expressau lieu de à serverpartir de const server = http.createServer(app)lors de la création du socket serveur.

Faux

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

Correct

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

Je fais à la fois le développement Web (nœud) et Android, et j'ouvre le simulateur d'appareil Android Studio et le docker ensemble, les deux utilisent le port 8601, il s'est plaint d'une socket hang uperreur, après avoir fermé le simulateur d'appareil Android Studio et cela fonctionne bien du côté nœud. N'utilisez pas simultanément le simulateur d'appareil Android Studio et le docker.


1

J'ai eu une erreur similaire lors de l'utilisation de CouchDB sur le 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
    }
  }

Ce qui devrait être "http", pas "https", pour me connecter à mon instance CouchDB. J'espère que cela pourrait être utile pour toute personne confrontée à un problème similaire.


0

Dans mon cas, c'était parce qu'une réponse application / json était mal formatée (contient une trace de pile). La réponse n'a jamais été envoyée au serveur. C'était très difficile à déboguer car, il n'y avait pas de journal. Ce fil m'aide beaucoup à comprendre ce qui se passe.


0

Dans le cas où vous utilisez node-http-proxy, veuillez être conscient de ce problème, qui entraînera une erreur de blocage de socket: https://github.com/nodejitsu/node-http-proxy/issues/180 .

Pour la résolution, également dans ce lien, déplacez simplement la déclaration de la route API (pour le proxy) dans les routes express avant express.bodyParser ().


0

J'ai analysé ce problème hier en exécutant mon application Web et mon serveur node.js via IntelliJ IDEA 2016.3.6. Tout ce que j'avais à faire était d'effacer mes cookies et mon cache dans mon navigateur Chrome.


0

Si vous rencontrez cette erreur sur une connexion https et que cela se produit instantanément, cela pourrait être un problème de configuration de la connexion SSL.

Pour moi, c'était ce problème https://github.com/nodejs/node/issues/9845 mais pour vous, cela pourrait être autre chose. Si c'est un problème avec le SSL, vous devriez pouvoir le reproduire avec le package nodejs tls / ssl en essayant simplement de vous connecter au domaine


0

Je pense qu'il vaut la peine de noter ...

Je créais des tests pour les API Google. J'interceptais la demande avec un serveur de fortune, puis je les transmettais à la vraie API. J'essayais simplement de transmettre les en-têtes de la demande, mais quelques en-têtes causaient un problème avec express à l'autre bout.

À savoir, j'ai dû supprimer connection, acceptet les en- content-lengthtêtes avant d'utiliser le module de demande pour transmettre.

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

Dans mon cas, ce n'était pas une erreur, mais un comportement attendu pour le navigateur Chrome. Chrome maintient la connexion tls active (pour la vitesse, je pense), mais le serveur node.js l'arrête après 2 minutes et vous obtenez une erreur.

Si vous essayez de demander GET à l'aide du navigateur Edge, il n'y aura aucune erreur. Si vous fermez la fenêtre chrome - vous obtiendrez immédiatement une erreur.

Alors que faire? 1) Vous pouvez filtrer ces erreurs, car ce ne sont pas vraiment des erreurs. 2) Il y a peut-être une meilleure solution :)


0

Il semble qu'il y ait un cas supplémentaire ici, qui est qu'Electron n'est pas fan du nom de domaine "localhost". Dans mon cas, je devais changer cela:

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

pour ça:

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

Après cela, le problème a disparu.

Cela signifie que la résolution DNS (locale ou distante) peut également causer des problèmes.


0

Après un long débogage dans le code du nœud js, la chaîne de connexion mongodb, la vérification de CORS, etc., pour moi, je passe simplement à un numéro de port différent server.listen(port); fait le travail, dans postman, essayez aussi. Aucune modification des proxyparamètres juste les valeurs par défaut.


-1

Cette erreur peut également se produire lorsque vous travaillez avec http.request , votre demande n'est probablement pas encore terminée.

Exemple:

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

Et vous devez toujours ajouter cette ligne: req.end() Avec cette fonction, nous ordonnerons de terminer l'envoi de la demande.

Comme le dit la documentation:

Avec http.request (), il faut toujours appeler req.end () pour signifier la fin de la requête - même si aucune donnée n'est écrite dans le corps de la requête.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.