NodeJS - "套接字挂断"实际意味着什么?

JVG*_*JVG 238 node.js

我正在使用Node和Cheerio构建一个web scraper,对于某个网站,我遇到了以下错误(它只发生在这个网站上,没有其他我试图抓取.

它每次都发生在不同的位置,所以有时它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)
Run Code Online (Sandbox Code Playgroud)

调试非常棘手,我真的不知道从哪里开始.首先,什么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
    });
}
Run Code Online (Sandbox Code Playgroud)

没有直接调用来关闭连接,但我正在使用Node Request它(据我所知)使用http.get所以这不是必需的,如果我错了,请纠正我!

编辑2:这是一个导致错误的实际使用中的代码.prodURL其他变量主要是前面定义的jquery选择器.这使用asyncNode 的库.

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);
                }
            ]);
    });
}
Run Code Online (Sandbox Code Playgroud)

Eye*_*Eye 141

socket hang up抛出时有两种情况:

当你是一个客户

当您作为客户端向远程服务器发送请求时,并未收到任何及时响应.您的套接字已结束,会引发此错误.您应该捕获此错误并决定如何处理它:是否重试请求,将其排队等待以后等.

当您是服务器/代理时

当您作为服务器(可能是代理服务器)从客户端接收请求,然后开始对其进行操作(或将请求中继到上游服务器),并且在准备响应之前,客户端决定取消/中止请求.

此堆栈跟踪显示客户端取消请求时发生的情况.

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)
Run Code Online (Sandbox Code Playgroud)

线路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;
}
Run Code Online (Sandbox Code Playgroud)

如果客户端是浏览器中的用户,则这是典型情况.加载某些资源/页面的请求需要很长时间,用户只需刷新页面即可.此类操作会导致上一个请求中止,这会在服务器端抛出此错误.

由于此错误是由客户端的意愿引起的,因此他们不希望收到任何错误消息.因此,无需将此错误视为关键.只是忽略它.这是因为在这样的错误上res,客户端监听的套接字虽然仍然可写,但却被破坏了.

console.log(res.socket.destroyed); //true
Run Code Online (Sandbox Code Playgroud)

所以,除了显式关闭响应对象之外,没有必要发送任何内容:

res.end();
Run Code Online (Sandbox Code Playgroud)

但是,如果您已经将请求转发给上游的代理服务器,您应该做的就是中止您对上游的内部请求,表明您对响应缺乏兴趣,这反过来会告诉上游服务器,或许,停止昂贵的操作.

  • 作为客户,我怎样才能让请求等待更长时间?这是35秒的错误,我需要它等待大约一分钟. (2认同)

Ble*_*der 48

看看来源:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

当服务器从不发送响应时,将发出该消息.

  • 但它等了多久? (4认同)
  • 从功能角度来看,你能解释一下这意味着什么吗?我试图在这里建立安全措施,将有问题的网址添加到数组中,然后再将其删除.我在一些地方读到错误可能是Node的排队问题,不知道补救和避免这种情况的最佳方法. (2认同)
  • 它应该使用http://en.wikipedia.org/wiki/Exponential_backoff来实现"多长时间". (2认同)

sil*_*orb 44

值得一提的一个案例是:当使用Express从Node.js连接到Node.js时,如果我没有用"/"前缀请求的URL路径,我会得到"socket hang up".

  • 错误:"user/login",成功:"/ user/login" (4认同)
  • 我花了差不多一个小时调试它!看到你的回复,并认为SH**,添加/并且它工作正常:)谢谢! (3认同)
  • 你通过这个回复节省了我的时间! (3认同)

Aek*_*pen 25

require('http')以前使用https服务,它显示" socked hang up".

然后我使用require('https'),它正在工作.

  • 谢谢你的评论.我浪费时间来处理这个错误.最后,我只是尝试这个解决方案,它的工作原理.只是想分享.希望其他人不要浪费时间,而不是赞美作为高质量的答案. (13认同)
  • @JohannesDorn这是对错误含义的问题的隐含答案.那是一个有用的. (7认同)

小智 24

下面是一个简单的例子,当我错过在下面的示例中添加注释代码时,我得到了相同的错误.取消注释代码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();
Run Code Online (Sandbox Code Playgroud)

  • 这节省了我的理智......谢谢! (2认同)

Sly*_*Sly 15

扩展Blender的答案,这在许多情况下都会发生.我遇到的最常见的是:

  1. 服务器崩溃了.
  2. 服务器拒绝您的连接,很可能被阻止User-Agent.

socketCloseListener,正如Blender的回答所述,并不是唯一可以创建挂起错误的地方.

例如,在这里找到:

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();
}
Run Code Online (Sandbox Code Playgroud)

您可以尝试curl使用从Node发出的标题,并查看是否在那里得到响应.如果您没有收到回复curl,但确实在浏览器中收到了回复,那么您的User-Agent标题很可能会被阻止.

  • 服务器可以拒绝连接的另一个原因(我只是在转移到prod而不是QA时点击这个),如果你的服务器期望一个https请求而不是http. (3认同)

Eri*_*lan 9

我认为“套接字挂起”是一个相当普遍的错误,表明连接已从服务器端终止。换句话说,用于维持客户端和服务器之间连接的套接字已断开。(虽然我确信上面提到的许多观点对不同的人都有帮助,但我认为这是更普遍的答案。)

就我而言,我发送的请求的负载超过 20K。这被服务器拒绝了。我通过删除文本并重试直到请求成功来验证这一点。确定最大可接受长度后,我验证了添加单个字符会导致错误显现。我还通过从 Python 应用程序和 Postman 发送相同的请求来确认客户端不是问题。所以无论如何,我相信,就我而言,有效负载的长度是我的具体问题。

问题的根源再次是轶事。一般问题是“服务器说不”。


Mil*_*iez 7

值得一提的另一个案例(对于Linux和OS X)是,如果您使用类似于https执行请求的库,或者如果您https://...作为本地服务实例的URL 传递,那么您将使用端口443作为保留的私有端口并且您可能会结束Socket hang upECONNREFUSED错误.

相反,使用port 3000,fe,并执行http请求.


Mar*_*ski 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
    }
});
Run Code Online (Sandbox Code Playgroud)

经过一番挣扎,我能够解决这个问题 - 因为它出现了非常非常简单的错误.我通过HTTPS协议连接到数据库,但我不断向我的nano对象传递一个keepalive代理,作为使用此库展示的示例(它们依赖于一些使用http的默认值).

使用HttpsAgent的一个简单的改变就是诀窍:

var KeepAliveAgent = require('agentkeepalive').HttpsAgent;
Run Code Online (Sandbox Code Playgroud)


Кон*_*Ван 6

对于request模块用户

超时

有两种主要类型的超时:连接超时读取超时。一个连接超时如果在您的客户端试图建立到远程计算机(对应于一个连接超时被击中发生connect()在插座上的呼叫)。一个读超时发生时随时使用的服务器回送响应的一部分太慢。

请注意,连接超时会发出ETIMEDOUT错误,读取超时会发出ECONNRESET错误。


Jef*_*mon 6

这给我带来了问题,因为我正在执行此处列出的所有操作,但仍然出现错误。事实证明,调用 req.abort() 实际上会抛出一个错误,代码为 ECONNRESET,因此您实际上必须在错误处理程序中捕获该错误。

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


小智 5

我在向某些服务器请求时遇到了同样的问题。就我而言,在请求选项的标头中为 User-Agent 设置任何值对我有帮助。

const httpRequestOptions = {
    hostname: 'site.address.com',
    headers: {
       'User-Agent': 'Chrome/59.0.3071.115'
    }
};
Run Code Online (Sandbox Code Playgroud)

这不是一般情况,取决于服务器设置。