NodeJS-“套接字挂起”实际上是什么意思?


276

我正在使用Node和Cheerio构建网络抓取工具,对于某个网站,我遇到以下错误(它仅在该网站上发生,没有其他我尝试抓取的错误。

每次都在不同的位置发生,因此有时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)

调试起来非常棘手,我真的不知道从哪里开始。首先,什么套接字挂起错误?是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库用于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
这意味着套接字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

2
那就不应该是问题。如果您注释掉包括cheerio.load的抓取部分并返回相同的内容,将会发生什么。这里的问题cheerio.load是异步的。因此,在开始使用$进行操作之前,它可能无法完成。
user568109 2013年

4
我还发现有时候,如果我过于激进地爬网(例如10个以上的同时连接),它们将开始响应套接字挂起,因此也可能是这样。
tobek

1
FYI用英语hang up表示通过断开连接来结束电子对话;源自挂断老式电话。
КонстантинВан

Answers:


161

有两种情况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)

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秒时出错,我需要等待大约一分钟。
Big Money

我面临着同样的问题。是否可以等待响应并开始像一个接一个的执行一样发送下一个请求。我可以知道如何处理此套接字挂起的情况吗?
迪帕克

您可以使用@BigMoney setTimeout()。看到这个问题:stackoverflow.com/questions/6214902/...
在霍拉

您的详细信息使我幸免于难,我使用了node.js作为上游服务器和客户端之间的代理服务器,由于我忘记了使用res.send,所以请求超时确实引发了此错误,谢谢
Farzad YZ

当您尝试通过同一连接向Django的开发Web服务器发出第二个请求时,您可能会收到“ socket hang up”(客户端挂断)消息。它不支持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
您可以从功能角度解释这意味着什么吗?我正在尝试通过将有问题的网址添加到数组中,然后在以后将其抓取来构建保护措施。我在一些地方读到,错误可能是Node的一个排队问题,不知道补救和避免这种情况的最佳方法。
JVG 2013年

5
但是要等多久?
CommaToast 2014年

2
应该使用en.wikipedia.org/wiki/Exponential_backoff来实现“多长时间”。
Norman H

这种“挂掉电话”是没有意义的。这只是来自nodejs团队的惊喜。
puchu

45

值得一提的情况是:使用Express从Node.js连接到Node.js时,如果未在请求的URL路径前加上“ /”作为前缀,则会出现“套接字挂断”的情况。


1
那就是我的问题,纯HTTP node.js中的客户端和服务器
ashley willis 2014年

1
@silentorb:能否请您显示示例网址?在这种情况下,我也面临同样的错误。
Pritam

4
错误:“用户/登录”,成功:“ /用户/登录”
–silentorb

4
老兄,我花了将近一个小时来调试它!看到您的答复并以为SH **,添加了/,效果很好:)谢谢!
Daniel Gruszczyk

4
您的回复节省了我几个小时!
imhotep '16

32

我曾经require('http')使用过https服务,但显示为“ socket hang up”。

然后,我改变require('http')require('https'),而是和它工作。


尽管这可能是解决问题的方法,但这不是问题的答案。张贴者希望得到有关错误消息含义的答案。此外,已经有许多高质量的答案。您的没有提供额外的价值。
约翰内斯·多恩

19
感谢您的评论。我把时间浪费在这个错误上。最后,我只是尝试这种解决方案,它可以工作。只是想分享。希望它对其他人有用,不要浪费时间,不要称赞它为高质量的答案。
Aekkawit Chanpen

12
@JohannesDorn这是错误含义的隐式答案。在那很有用。
乌拉德·卡萨赫

29

下面是一个简单的示例,在以下示例中,我错过添加注释的代码时遇到了相同的错误。取消注释代码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

你是英雄!谢谢。
Xenhat

17

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

您可以尝试curl使用从Header发送的标头等,看看在那里是否收到响应。如果您没有通过收到响应curl,但是在浏览器中确实得到了响应,则User-Agent很可能是标题被阻止了。


3
服务器可能拒绝您连接的另一个原因(我只是在移动到prod而不是QA时碰到了此问题),如果您的服务器希望使用https请求而不是http。
mcole 2014年

7

另一个值得一提的情况(对于Linux和OS X)是,如果您使用类似的库https来执行请求,或者如果您将其https://...作为本地服务实例的URL进行传递,则将使用443作为保留专用端口的port,并且可能最终以Socket hang upECONNREFUSED出现错误。

而是使用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协议连接到数据库,但是我一直将创建的keepalive代理传递给我的nano对象,该代理作为使用此库的示例显示出来(它们依赖于一些使用http的默认值)。

使用HttpsAgent的一个简单更改便达到了目的:

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

1
稍微详细一点,如果将请求配置为端口443,并且该请求是通过http模块而不是https模块发出的,则套接字会挂断。如果有更详细的原因说明断开连接发生的原因(SSL / TLS协商?),那将是很好的。例如,我已经在ASP.NET中看到了详细程度。
理查德·科莱特

6

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

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

5

对于request模块用户

超时时间

超时主要有两种类型:连接超时读取超时。如果在客户端尝试建立与远程计算机的连接时发生超时(对应于connect()套接字上的调用),则会发生连接超时。只要服务器速度太慢而无法发送回一部分响应,就会发生读取超时

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


3

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

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

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


2

另外原因可能是因为使用app实例express代替serverfromconst 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设备模拟器和docker,它们都使用端口8601 socket hang up,在关闭Android Studio设备模拟器后,它报错,并且在节点端运行良好。请勿同时使用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实例连接时,应为“ http”,而不是“ https”。希望它对遇到类似问题的人有所帮助。


0

在我的情况下,这是因为application / json响应的格式错误(包含堆栈跟踪)。响应从未发送到服务器。调试起来非常棘手,因为没有日志。这个线程对我很有帮助,以了解发生了什么。



0

昨天通过IntelliJ IDEA 2016.3.6运行我的Web应用程序和node.js服务器,遇到了这个问题。我所要做的就是清除我的Cookie并在Chrome浏览器中缓存。


0

如果您通过https连接遇到此错误,并且该错误立即发生,则设置SSL连接可能会出现问题。

对我来说,这就是这个问题https://github.com/nodejs/node/issues/9845,但对您而言,可能是其他问题。如果ssl有问题,那么您应该能够使用nodejs tls / ssl软件包重现它,而只需尝试连接到域


0

我认为值得一提...

我正在为Google API创建测试。我当时正在使用临时服务器拦截请求,然后将其转发到真正的api。我试图只传递请求中的标头,但是一些标头在另一端导致了表达问题。

也就是说,在使用请求模块进行转发之前,我必须删除connectionacceptcontent-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

这里似乎还有另外一种情况,那就是Electron不喜欢“ localhost”域名。就我而言,我需要更改此设置:

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

对此:

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

之后,问题就消失了。

这意味着DNS解析(本地或远程)也可能引起一些问题。


0

在长时间调试节点js代码,mongodb连接字符串,检查CORS等之后,对于我来说,切换到其他端口号server.listen(port);使其生效postman,也可以尝试。proxy仅默认设置即可更改设置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.