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

时间:2013-06-08 01:45:26

标签: node.js

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

它每次都发生在不同的位置,所以有时它会引发错误url x,其他时候url x很好,它完全是一个不同的网址:

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

27 个答案:

答案 0 :(得分:125)

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指向@Blender上面提到的socketCloseListener,特别是:

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

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

答案 1 :(得分:47)

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

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

答案 2 :(得分:40)

值得一提的一个案例是:当使用Express从Node.js连接到Node.js时,我得到" socket挂断"如果我没有在请求的URL路径前加上" /"。

答案 3 :(得分:23)

我使用 require('http') 来使用 https 服务,并显示“ socked hang up ”。

然后我使用 require('https') ,而且它正在运作。

答案 4 :(得分:19)

下面的

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

答案 5 :(得分:15)

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

  1. 服务器崩溃。
  2. 服务器拒绝了您的连接,很可能被User-Agent阻止。
  3. 正如Blender的回答所述,

    socketCloseListener并不是唯一可以创建挂断错误的地方。

    例如,找到here

    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标题,以及从Node发出的标题,看看您是否收到了回复。如果您没有收到curl的回复,但是您的浏览器确实收到了回复,那么您的User-Agent标题最有可能被阻止。

答案 6 :(得分:8)

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

相反,请使用端口3000,然后执行http请求。

答案 7 :(得分:6)

使用 Nano 库连接 Couch DB 时遇到了同样的问题。我尝试使用 keepaliveagent 库来微调连接池,并且 socket hang up 消息仍然失败。

var KeepAliveAgent = require('agentkeepalive');

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

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

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

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

答案 8 :(得分:3)

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

<%- truncate((post.excerpt), {length: 25, omission: '... (continued)'}) || post.content%> 

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

答案 9 :(得分:3)

对于request模块用户

  

Timeouts

     

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

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

答案 10 :(得分:2)

另外一个原因可能是因为在创建服务器套接字时使用declare @table1 as columnnames insert @table1 values ('salesrepid'),('sale') exec delete_dupes '_original' , @table1 app实例express来代替server

<强>错误

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

答案 11 :(得分:1)

我同时进行web(节点)和Android开发,并打开Android Studio设备模拟器和docker,两者都使用端口8601,它抱怨socket hang up错误,关闭Android Studio设备模拟器后效果很好在节点方面。请勿同时使用Android Studio设备模拟器和泊坞窗。

答案 12 :(得分:1)

使用http.request时也会发生此错误,可能您的请求尚未完成。

示例:

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

您总是需要添加以下行:req.end() 使用此功能,我们将命令完成发送请求。

如文档所述:

对于http.request(),即使没有数据写入请求正文,也必须始终调用req.end()来表示请求结束。

答案 13 :(得分:1)

这引起了我的问题,因为我正在做这里列出的所有事情,但仍然会出现错误。事实证明,调用req.abort()实际上会抛出错误,代码为ECONNRESET,因此您实际上必须在错误处理程序中捕获它。

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

答案 14 :(得分: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”。希望它对遇到类似问题的人有所帮助。

答案 15 :(得分:0)

可能是您的服务器或 Socket 连接意外崩溃。

答案 16 :(得分:0)

我认为“套接字挂起”是一个相当普遍的错误,指示连接已从服务器端终止。换句话说,用于维护客户端和服务器之间的连接的套接字已断开连接。 (虽然我确定上述许多要点对各个人都有帮助,但我认为这是更笼统的答案。)

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

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

答案 17 :(得分:0)

我正在使用nano,所以花了很长时间才能弄清这个错误。我的问题是我使用了错误的端口。我使用的是5948端口,而不是5984。

import React, { ReactNode } from 'react';
import { Button } from 'antd';
import { Link } from 'react-router-dom';

import logo from 'img/logo.png';
import styles from './Layout.module.css';

interface Props {
  children: ReactNode;
}

const Layout = ({ children }: Props) => (
  <>
    <div className={styles.appbar}>
      <Link to="/">
        <Button type="primary">Root</Button>
      </Link>
      <Link to="/app/dashboard">
        <Button type="primary">Dashboard</Button>
      </Link>
      <div className={styles.icon}>
        <img src={logo} alt="Logo" />
      </div>
    </div>
    <div className={styles.content}>{children}</div>
  </>
);

export default Layout;

答案 18 :(得分:0)

已经很长时间了,但是另一种情况是在服务器端执行需要很长时间的请求(超过2分钟,这是express的默认值),并且没有在服务器端配置timeout参数。在我的情况下,我正在执行客户端->服务器->服务器请求(Node.js表达),并且应该在服务器和客户端上的每个请求路由器上设置超时参数。 因此,在两台服务器中,我都需要通过使用

来设置请求超时
req.setTimeout([your needed timeout])

在路由器上。

答案 19 :(得分:0)

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

答案 20 :(得分:0)

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

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

对此:

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

那之后问题就消失了。

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

答案 21 :(得分:0)

我个人认为这不是错误,而是chrome浏览器的预期行为。 Chrome使tls连接保持活动状态(为了提高速度),但是node.js服务器在2分钟后将其停止,您会收到错误消息。

如果您尝试使用边缘浏览器进行GET请求,则完全没有错误。 如果您关闭Chrome窗口-您将立即收到错误消息。

那该怎么办? 1)您可以过滤此错误,因为它们不是真正的错误。 2)也许有更好的解决方案:)

答案 22 :(得分:0)

我认为值得注意......

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

即,在使用请求模块转发之前,我必须删除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.
})

答案 23 :(得分:0)

如果您在https连接上遇到此错误,并且它立即发生,则可能是设置SSL连接时出现问题。

对我而言,这是https://github.com/nodejs/node/issues/9845这个问题,但对你而言,它可能是另一回事。如果它是ssl的问题,那么你应该能够使用nodejs tls / ssl包重新创建它,只是尝试连接到域

答案 24 :(得分:0)

昨天通过IntelliJ IDEA 2016.3.6运行我的Web应用程序和node.js服务器。我所要做的就是清除我的Chrome浏览器中的cookie和缓存。

答案 25 :(得分:0)

如果您使用的是node-http-proxy,请注意此问题,这会导致套接字挂起错误:https://github.com/nodejitsu/node-http-proxy/issues/180

对于解决方案,也可以在此链接中,在express.bodyParser()之前简单地在快速路由中声明API路由(用于代理)。

答案 26 :(得分:0)

就我而言,这是因为应用程序/ json响应格式错误(包含堆栈跟踪)。响应从未发送到服务器。 调试非常棘手,因为没有日志。这个帖子帮助我理解发生了什么。