NodeJS-実際に「ソケットがハングアップする」とはどういう意味ですか?


277

NodeとCheerioを使用してWebスクレイパーを構築していますが、特定のWebサイトで次のエラーが発生します(このWebサイトでのみ発生し、他のWebサイトをスクレイプしようとすると発生しません)。

毎回異なる場所で発生するためurl x、エラーが発生する場合もあれば、url xまったく別のURLである場合もあります。

    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)

これはデバッグが非常に難しいので、どこから始めればいいのか本当にわかりません。開始するには、どのようなISソケットがエラーをハングアップ?404エラーまたはそれと同様のものですか?それとも単にサーバーが接続を拒否したということですか?

これの説明はどこにもありません!

編集:これは、(時々)エラーを返すコードのサンプルです:

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

接続を閉じるための直接呼び出しはありませんが、私は使用しています Node Request、私が知る限りではhttp.get、これを使用しているため、これは必要ありません。間違っている場合は修正してください!

編集2:エラーの原因となっている実際の使用中のコードを以下に示します。prodURLその他の変数は、ほとんどが以前に定義されたjqueryセレクターです。これはasyncノードのライブラリを使用します。

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
これは、ソケットがendタイムアウト期間内に接続イベントを送信しないことを意味します。http.request(ではなくhttp.get)を介してcheerioのリクエストを受け取っている場合。request.end()リクエストの送信を完了するには、電話する必要があります。
— user568109 2013年

1
@ user568109特筆すべきrequestは、特定のhttp.requestリクエストではなくノードサービスを使用していることです(私はノードは非常に新しいので!)。これはgithub.com/mikeal/requestです。これは、リクエストを自動的に終了するようです。編集:ドキュメントによると、http method, defaults to GETそれは問題ではありません。
— JVG 2013年

2
その後、それは問題ではありません。cheerio.loadを含むスクレイピング部分をコメントアウトして同じコンテンツを返すとどうなりますか。ここでの問題cheerio.loadは非同期です。したがって、$を使って作業を開始する前に終了しない場合があります。
— user568109 2013年

4
また、サイトをあまりにも積極的にクロールすると(10以上の同時接続など)、サイトがソケットのハングアップで応答し始める場合があるので、それも原因である可能性があります。
— 2014

1
ちなみに、英語で言うhang upと、接続を切断して電子会話を終了することを意味します。昔ながらの電話を切ることから始まった。
— КонстантинВан

回答:


161

がsocket hang upスローされるのは、次の2つの場合です。

あなたがクライアントであるとき

クライアントとして、リモートサーバーにリクエストを送信し、タイムリーな応答を受信しない場合。ソケットが終了すると、このエラーがスローされます。このエラーをキャッチして、それを処理する方法を決定する必要があります:要求を再試行するか、後でキューに入れるかなど。

サーバー/プロキシの場合

サーバーとして、おそらくプロキシサーバーとして、クライアントからリクエストを受信し、それに基づいて処理を開始(またはリクエストを上流サーバーに中継)し、応答を準備する前に、クライアントはキャンセル/中止を決定します。リクエスト。

このスタックトレースは、クライアントが要求をキャンセルしたときに何が起こるかを示しています。

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)

ラインhttp.js:1526:9同一の点socketCloseListener@Blenderによって上記、特に:

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

これは、クライアントがブラウザーのユーザーである場合の典型的なケースです。一部のリソース/ページをロードするリクエストには時間がかかり、ユーザーはページを更新するだけです。このようなアクションにより、前のリクエストが中止され、サーバー側でこのエラーがスローされます。

このエラーはクライアントの希望によって引き起こされるため、エラーメッセージを受け取ることは想定されていません。したがって、このエラーを重大と見なす必要はありません。無視してください。このようなエラーが発生するresと、クライアントがリッスンしたソケットは、書き込み可能ではありますが、破棄されるという事実により、これは推奨されます。

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

したがって、応答オブジェクトを明示的に閉じる以外は何も送信する意味がありません。

res.end();

しかし、あなたは何をすべきあなたがあれば確かにあり、既に上流への要求を中継したプロキシサーバー、順番に上流を教えてくれますどの、応答への関心のあなたの欠如を示す、上流にあなたの内部要求を中止することですサーバーは、おそらく、高価な操作を停止します。


2
クライアントとして、リクエストをより長く待機させるにはどうすればよいですか?35秒でエラーが発生し、1分ほど待つ必要があります。
— ビッグマネー

同じ問題に直面しています。応答を待って、1つずつ実行するように次の要求の送信を開始することは可能ですか?このソケットのハングアップの処理方法を知っていますか?
— Deepak

@BigMoneyを使用できますsetTimeout()。:この質問を参照stackoverflow.com/questions/6214902/...
— 叫び声

あなたの詳細については、地獄から私を生き延び、私は上流のサーバーとクライアント間のプロキシサーバとしてのNode.jsを使っていた、リクエストのタイムアウトは、私が使うのを忘れていましたからといって、このエラーをスローしましたres.sendおかげで、
— Farzad YZ

同じ接続を介してDjangoの開発用Webサーバーに2番目のリクエストを行おうとすると、クライアントとして「ソケットのハングアップ」を受け取る可能性があります。はサポートしていませんkeep-alive。そして、クライアントが期待している場合は、エラーが発生します。次のように見えます。
— x-yuri

53

ソースを見てみましょう:

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

サーバーが応答を送信しない場合、メッセージが発行されます。


2
機能の観点から、これが何を意味するか説明できますか?ここでは、問題のあるURLを配列に追加し、後でそれらをスクレイピングすることによって、保護手段を構築しようとしています。エラーがノードのキューイングの問題である可能性があることをいくつかの場所で読みましたが、これを修正して回避するための最良の方法がわかりません。
— JVG 2013年

5
しかし、それはどれくらい待つのでしょうか?
— CommaToast 2014年

2
「どのくらい」の実装にはen.wikipedia.org/wiki/Exponential_backoffを使用する必要があります。
— Norman H

この「ソケットのハングアップ」は意味がありません。これはnodejsチームからの驚きです。
— ぷちゅ2016年

45

言及する価値のある1つのケース:Expressを使用してNode.jsからNode.jsに接続するときに、要求されたURLパスの前に「/」を付けないと、「ソケットがハングアップ」します。


1
それが私の問題でした、純粋なhttp node.jsのクライアントとサーバーの両方
— アシュリー・ウィリス14年

1
@silentorb:URLの例を見せていただけますか?この場合も同じエラーが発生します。ありがとうございます。
— Pritam

4
エラー:「user / login」、成功:「/ user / login」
— silentorb '13

4
私はそれをデバッグするのにほぼ1時間費やしました!あなたの返事を見て、SH **を考え、/を追加しました、そしてそれはうまくいきます:)
— Daniel Gruszczyk 2015

4
この返事で何時間も節約できました!
— imhotep

32

以前require('http')はhttpsサービスを使用していましたが、「socket hang up」と表示されていました。

それから私は代わりに変更require('http')しrequire('https')、それは働いています。


これは問題の解決策になるかもしれませんが、質問に対する回答ではありません。ポスターは、エラーメッセージの意味についての回答を求めていました。さらに、すでに多くの高品質の回答があります。あなたの付加価値はありません。
— ヨハネスドーン2017

19
コメントありがとうございます。このエラーのために時間を無駄にしています。最後に、私はこのソリューションを試してみればうまくいきます。共有したいだけです。他の人が自分の時間を無駄にしないこと、高品質の回答として賞賛することではないことを願っています。
— Aekkawit Chanpen 2017

12
@JohannesDornこれは、エラーの意味についての質問に対する暗黙の回答です。そしてそれで便利なもの。
— Ulad Kasach 2017年

30

以下は、以下の例のコメント付きコードを追加し忘れたときに同じエラーが発生した簡単な例です。コードのコメントを外すと、req.end()この問題が解決します。

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
これは私の正気を救ってくれました...ありがとう!
— PGallagher、2018年

あなたはヒーローです!ありがとうございました。
— Xenhat

17

Blenderの答えを拡張すると、これは多くの状況で起こります。私が遭遇する最も一般的なものは:

  1. サーバーがクラッシュしました。
  2. サーバーが接続を拒否しましたUser-Agent。おそらくによってブロックされています。

socketCloseListenerBlenderの回答で概説されているように、ハングアップエラーが発生するのはこの場所だけではありません。

たとえば、ここにあります:

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

curlNodeから送信されているヘッダーなどを試して、そこに応答があるかどうかを確認できます。で応答が得られないが、curlブラウザーで応答が得られる場合は、User-Agentヘッダーがブロックされている可能性が高いです。


3
サーバーが接続を拒否するもう1つの理由(QAではなくprodに移動するときにこれを押しただけです)は、サーバーがhttpではなくhttpsリクエストを予期している場合です。
— mcole 2014

7

言及する価値のあるもう1つのケース(LinuxおよびOS Xの場合)はhttps、リクエストの実行にライブラリを使用する場合、またはhttps://...ローカルで提供されるインスタンスのURLとして渡す場合443、予約済みのプライベートポートであるポートを使用することです。Socket hang upまたはECONNREFUSEDエラーになる可能性があります。

代わりに、port 3000、feを使用して、httpリクエストを実行します。


6

Nanoライブラリを使用してCouch DBに接続しているときにも同じ問題が発生しました。keepaliveagentライブラリを使用して接続プールを微調整しようとすると、ソケットのハングアップメッセージで失敗し続けました。

var KeepAliveAgent = require('agentkeepalive');

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

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

苦労した後、私は問題を明らかにすることができました-それが出たとき、それは非常に非常に単純な間違いでした。私はHTTPSプロトコルを介してデータベースに接続していましたが、このライブラリの使用例として作成されたキープアライブエージェントをナノオブジェクトに渡し続けました(httpを使用するいくつかのデフォルトに依存しています)。

HttpsAgentを使用するための簡単な変更の1つがトリックです。

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

1
もう少し詳しく説明すると、リクエストがポート443用に構成されており、リクエストがhttpsモジュールではなくhttpモジュールを介して発行されている場合、ソケットがハングアップします。切断が発生した理由(SSL / TLSネゴシエーション)の詳細があった場合、それは良いことです。たとえば、ASP.NETでそのレベルの詳細を見てきました。
— Richard Collette 2016年

6

ここにリストされているすべてを実行していたが、それでもエラーがスローされていたため、問題が発生しました。実際には、req.abort()を呼び出すとECONNRESETというコードでエラーがスローされるため、実際にはエラーハンドラーでキャッチする必要があります。

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

5

以下のためのrequestモジュールのユーザー

タイムアウト

タイムアウトには主に2つのタイプがあります。接続タイムアウトと読み取りタイムアウトです。接続タイムアウトは、クライアントが(に対応するリモートマシンへの接続を確立しようとしている間にタイムアウトがヒットした場合に発生しconnect()たソケットのコールを)。読み取りタイムアウトは、サーバーが応答の一部を返送するには遅すぎるいつでも起こります。

なお、接続タイムアウトが発するETIMEDOUTエラー、および読み取りタイムアウトが発するECONNRESETエラー。


3

一部のサーバーへのリクエスト中に同じ問題が発生しました。私の場合、リクエストオプションのヘッダーでUser-Agentに任意の値を設定すると役に立ちました。

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

これは一般的なケースではなく、サーバーの設定によって異なります。


2

from appのexpress代わりにのインスタンスを使用していることが理由である可能性もありますserverconst server = http.createServer(app)、サーバーソケットの作成中ます。

違う

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

正しい

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

私はウェブ(ノード)とAndroidの両方の開発を行い、Android Studioデバイスシミュレーターとドッカーを一緒に開きます。どちらもポート8601を使用しますsocket hang up。AndroidStudioデバイスシミュレーターを閉じた後、エラーが表示され、ノード側でうまく機能します。Android StudioデバイスシミュレータとDockerを一緒に使用しないでください。


1

OCPクラスターでCouchDBを使用すると、同様のエラーが発生しました。

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

CouchDBインスタンスに接続するには、「https」ではなく「http」にする必要があります。同様の問題に直面している人にとって役立つことを願っています。


0

私の場合、それはアプリケーション/ jsonの応答が正しくフォーマットされていない(スタックトレースが含まれている)ためです。応答がサーバーに送信されることはありません。ログがないため、これはデバッグが非常に困難でした。このスレッドは、何が起こるかを理解するのに大いに役立ちます。


0

node-http-proxyを使用している場合は、この問題に注意してください。これにより、ソケットのハングアップエラーが発生します:https : //github.com/nodejitsu/node-http-proxy/issues/180。

解決のために、このリンクでも、express.bodyParser()の前にExpressルート内でAPIルート(プロキシ用)を宣言するだけです。


0

昨日、IntelliJ IDEA 2016.3.6を介してWebアプリケーションとnode.jsサーバーを実行しているこの問題に遭遇しました。私がしなければならなかったのは、私のChromeブラウザーでCookieとキャッシュをクリアすることだけでした。


0

https接続でこのエラーが発生し、それがすぐに発生する場合は、SSL接続の設定に問題がある可能性があります。

私にとってそれはこの問題https://github.com/nodejs/node/issues/9845でしたが、あなたにとってそれは何か他のものである可能性があります。それがsslに問題がある場合は、ドメインに接続しようとしているだけのnodejs tls / sslパッケージでそれを再現できるはずです


0

注目に値すると思います...

Google APIのテストを作成していました。私はその場しのぎのサーバーでリクエストをインターセプトし、それらを実際のAPIに転送していました。リクエストでヘッダーを渡すだけでしたが、いくつかのヘッダーが相手側のExpressで問題を引き起こしていました。

つまり、私は削除しなければならなかったconnection、acceptとcontent-length一緒に転送するように要求モジュールを使用する前に、ヘッダー。

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

私の場合、それはエラーではありませんでしたが、Chromeブラウザの予想される動作です。Chromeはtls接続を維持しますが(速度については)、node.jsサーバーは2分後に停止し、エラーが発生します。

エッジブラウザを使用してGETリクエストを実行しても、エラーは発生しません。Chromeウィンドウを閉じると、すぐにエラーが発生します。

じゃあ何をすればいいの?1)実際にはエラーではないため、このエラーをフィルターに掛けることができます。2)もっと良い解決策があるかもしれません:)


0

ここでは、エレクトロンが「localhost」ドメイン名のファンではない、もう1つのケースがあるようです。私の場合、これを変更する必要がありました:

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

これに:

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

その後、問題はなくなりました。

つまり、DNS解決(ローカルまたはリモート)もいくつかの問題を引き起こしている可能性があります。


0

ノードのjsコード、mongodb接続文字列、CORSの確認などに長いデバッグを行った後、別のポート番号に切り替えるだけserver.listen(port);で機能するようになりましたpostman。proxyデフォルトのみの設定への変更はありません。


-1

このエラーは、を使用しているときにも発生する可能性http.requestがあります。おそらくリクエストがまだ完了していません。

例:

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

そして、常にこの行を追加する必要があります。req.end() この関数を使用して、リクエストの送信を完了するように命令します。

ドキュメントのように言っています:

http.request()では、リクエスト本文にデータが書き込まれていない場合でも、リクエストの終了を示すために常にreq.end()を呼び出す必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.