NodeJS - “套接字挂断”实际上是啥意思?
Posted
技术标签:
【中文标题】NodeJS - “套接字挂断”实际上是啥意思?【英文标题】:NodeJS - What does "socket hang up" actually mean?NodeJS - “套接字挂断”实际上是什么意思? 【发布时间】:2013-06-04 09:57:26 【问题描述】:我正在使用 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)
调试起来非常棘手,我真的不知道从哪里开始。首先,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 选择器。这使用了 Node 的 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);
]);
);
【问题讨论】:
表示socket在超时时间内没有发送连接end
事件。如果您通过http.request
(不是http.get
)收到cheerio 请求。您必须致电request.end()
才能完成发送请求。
@user568109 我应该注意,我使用的是节点 request
服务,而不是特定的 http.request
请求(我想,我对节点很陌生!)。这是一个:github.com/mikeal/request 这似乎是自动完成请求,不是吗?编辑:根据文档,http method, defaults to GET
所以这不是问题。
那应该不是问题。如果您注释掉包括 Cheerio.load 在内的抓取部分并返回相同的内容会发生什么。这里的问题是,cheerio.load
是异步的。所以在你开始用 $ 做事情之前它可能不会完成。
我有时还发现,如果我对网站的抓取过于激进(例如同时连接 10 个以上),他们会开始响应套接字挂断,所以也可能是这样。
仅供参考,在英语中,hang up
表示 to end an electronic conversation by cutting the connection;源于挂断老式电话。
【参考方案1】:
您的问题也可能来自尝试连接到 HTTP URL,而您的服务仅在 HTTPS 上发布...
绝对是一个耗时的错误!
【讨论】:
【参考方案2】:我在错误地在同一个端口上运行两个应用程序时遇到了这个错误。 我有一个 next.js 应用程序和一个在 nest.js 中的应用程序,都在端口 8080 上运行,当我查看 .env 文件时,我意识到它们具有相同的端口,所以我将一个从 nest.js 更改为 3000 并且一切正常。
我并不是说这是错误的原因,但这是一种可能性。
【讨论】:
【参考方案3】:可能是您的服务器或 Socket 连接意外崩溃。
【讨论】:
【参考方案4】: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();
但是,如果您是已经将请求中继到上游的代理服务器,那么您应该确定的是,中止您对上游的内部请求,表明你对响应不感兴趣,这反过来会告诉上游服务器,也许,停止一个昂贵的操作。
【讨论】:
作为客户,我怎样才能让请求等待更长的时间?它在 35 秒时出错,我需要它等待大约一分钟。 我面临同样的问题。是否可以等待响应并开始发送下一个请求,就像一个一个执行一样。我知道如何处理这个套接字挂起吗?。 @BigMoney 你可以使用setTimeout()
。看到这个问题:***.com/questions/6214902/…
你的详细信息让我从地狱中幸存下来,我使用 node.js 作为上游服务器和客户端之间的代理服务器,请求超时确实抛出了这个错误,只是因为我忘了使用res.send
,谢谢
当您尝试通过同一连接向 Django 的开发 Web 服务器发出第二个请求时,您可以作为客户端接收“套接字挂断”。它不支持keep-alive
。如果您的客户期望它这样做,您会收到错误消息。它看起来沿着following lines。【参考方案5】:
已经很长时间了,但另一种情况是在服务器端执行需要很长时间的请求(超过 2 分钟,这是 express 的默认值)并且在服务器端未配置超时参数。在我的情况下,我正在执行客户端->服务器->服务器请求(Node.js express),我应该在服务器和客户端上的每个请求路由器上设置超时参数。 因此,在两台服务器中,我都需要使用
来设置请求超时req.setTimeout([your needed timeout])
在路由器上。
【讨论】:
【参考方案6】:我使用的是 nano,我花了很长时间才弄清楚这个错误。我的问题是我使用了错误的端口。我有端口 5948 而不是 5984。
var nano = require('nano')('http://localhost:5984');
var db = nano.use('address');
var app = express();
【讨论】:
【参考方案7】:我认为“套接字挂起”是一个相当普遍的错误,表明连接已从服务器端终止。换句话说,用于保持客户端和服务器之间连接的套接字已经断开。 (虽然我确信上面提到的许多观点对不同的人都有帮助,但我认为这是更普遍的答案。)
在我的例子中,我发送了一个负载超过 20K 的请求。这被服务器拒绝了。我通过删除文本并重试直到请求成功来验证这一点。在确定最大可接受长度后,我验证添加单个字符会导致错误出现。我还通过从 Python 应用程序和 Postman 发送相同的请求来确认客户端不是问题。所以无论如何,我有信心,就我而言,有效载荷的长度是我的具体问题。
再一次,问题的根源是轶事。一般问题是“服务器说不”。
【讨论】:
【参考方案8】:使用http.request
时也会发生此错误,可能您的请求尚未完成。
例子:
const req = https.request(options, res => )
而且你总是需要添加这一行:req.end()
使用这个函数,我们将命令完成发送请求。
正如文档中所说:
使用 http.request() 必须始终调用 req.end() 来表示请求的结束 - 即使没有数据写入请求正文。
【讨论】:
【参考方案9】:在对节点 js 代码、mongodb 连接字符串、检查 CORS 等进行长时间调试后,对我来说,只需切换到不同的端口号 server.listen(port);
就可以了,切换到 postman
,也可以试试。 proxy
设置没有更改,只是默认设置。
【讨论】:
【参考方案10】:我在 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
应该是“http”,而不是“https”,才能连接到我的 CouchDB 实例。希望对遇到类似问题的人有所帮助。
【讨论】:
【参考方案11】:我使用require('http')
消费https服务,显示“socket hang up
”。
然后我将 require('http')
改为 require('https')
,它正在工作。
【讨论】:
这对我来说是解决方案......绝对疯狂,他们几乎相同地命名了 2 个库(http 带有设置port: 443
的选项)然后期望人们了解发生了什么。谢谢你回答这个问题,把我的头撞在桌子上整整 3 个小时,你救了我。
由于类似的错误,我得到了socket hang up
...我试图使用ws://
协议连接到为wss://
协议运行的服务和端口【参考方案12】:
这里似乎还有一个额外的情况,那就是 Electron 不是“localhost”域名的粉丝。就我而言,我需要更改:
const backendApiHostUrl = "http://localhost:3000";
到这里:
const backendApiHostUrl = "http://127.0.0.1:3000";
之后问题就消失了。
这意味着 DNS 解析(本地或远程)也可能会导致一些问题。
【讨论】:
【参考方案13】:我的情况不是错误,而是 chrome 浏览器的预期行为。 Chrome 保持 tls 连接处于活动状态(我认为是为了速度),但 node.js 服务器在 2 分钟后停止它,你会收到一个错误。
如果您使用边缘浏览器尝试 GET 请求,则根本不会出错。 如果您关闭 chrome 窗口 - 您将立即收到错误消息。
那该怎么办? 1)您可以过滤此错误,因为它们不是真正的错误。 2)也许有更好的解决方案:)
【讨论】:
【参考方案14】:下面是一个简单的例子,当我错过在下面的例子中添加注释代码时,我得到了同样的错误。取消注释代码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();
【讨论】:
【参考方案15】:这给我带来了问题,因为我正在执行此处列出的所有操作,但仍然出现错误。事实证明,调用 req.abort() 实际上会引发错误,代码为 ECONNRESET,因此您实际上必须在错误处理程序中捕获它。
req.on('error', function(err)
if (err.code === "ECONNRESET")
console.log("Timeout occurs");
return;
//handle normal errors
);
【讨论】:
【参考方案16】:也可能是因为在创建服务器套接字时使用了express
的app
实例而不是const server = http.createServer(app)
中的server
。
错误
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);
);
【讨论】:
它们是一回事。app.listen
在后台使用 http.createServer
github.com/expressjs/express/blob/…【参考方案17】:
request
模块用户
Timeouts
有两种主要的超时类型:连接超时和读取超时。 连接超时 如果在您的客户端尝试与远程机器建立连接时遇到超时(对应于套接字上的
connect()
调用)。每当服务器太慢而无法发回部分响应时,就会发生读取超时。
请注意,连接超时会发出ETIMEDOUT
错误,读取超时会发出ECONNRESET
错误。
【讨论】:
【参考方案18】:我认为值得注意...
我正在为 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.
)
【讨论】:
【参考方案19】:我同时做web(node)和android开发,同时打开android studio设备模拟器和docker,两者都使用8601端口,报socket hang up
错误,关闭android studio设备模拟器后在node上运行良好边。不要同时使用 Android Studio 设备模拟器和 docker。
【讨论】:
【参考方案20】:如果您在通过 https 连接时遇到此错误并且它立即发生,则可能是设置 SSL 连接时出现问题。
对我来说是这个问题https://github.com/nodejs/node/issues/9845,但对你来说可能是别的问题。如果是 ssl 的问题,那么您应该能够使用 nodejs tls/ssl 包来重现它,只是尝试连接到域
【讨论】:
【参考方案21】:我在向某个服务器请求时遇到了同样的问题。就我而言,在请求选项的标头中为 User-Agent 设置任何值对我有帮助。
const httpRequestOptions =
hostname: 'site.address.com',
headers:
'User-Agent': 'Chrome/59.0.3071.115'
;
这不是一般情况,取决于服务器设置。
【讨论】:
【参考方案22】:昨天通过 IntelliJ IDEA 2016.3.6 运行我的 Web 应用程序和 node.js 服务器时遇到了这个问题。我所要做的就是清除我的 cookie 并缓存在我的 Chrome 浏览器中。
【讨论】:
【参考方案23】:如果您使用的是 node-http-proxy,请注意这个问题,这将导致套接字挂起错误:https://github.com/nodejitsu/node-http-proxy/issues/180。
为了解决问题,同样在此链接中,只需在 express.bodyParser() 之前在 express 路由中声明 API 路由(用于代理)。
【讨论】:
【参考方案24】:在我的情况下,这是因为应用程序/json 响应格式错误(包含堆栈跟踪)。响应从未发送到服务器。 调试起来非常棘手,因为没有日志。这个帖子对我理解发生了什么很有帮助。
【讨论】:
【参考方案25】:另一个值得一提的情况(对于 Linux 和 OS X)是,如果您使用像 https
这样的库来执行请求,或者如果您将 https://...
作为本地服务实例的 URL 传递,您将使用端口443
是保留的私有端口,您可能会遇到Socket hang up
或ECONNREFUSED
错误。
改为使用端口3000
,f.e.,并发出http
请求。
【讨论】:
【参考方案26】:我在使用 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;
【讨论】:
更详细一点,如果请求配置为端口 443 并且请求是通过 http 模块而不是 https 模块发出的,那么你会得到一个套接字挂起。如果有更多关于断开连接发生原因的详细信息(SSL/TLS 协商?),那就太好了。例如,我在 ASP.NET 中看到过这种详细程度。【参考方案27】:一个值得一提的案例:当使用 Express 从 Node.js 连接到 Node.js 时,如果我没有在请求的 URL 路径前加上“/”前缀,我会得到“socket hang up”。
【讨论】:
@silentorb:你能显示示例网址吗?在这种情况下,我面临同样的错误。谢谢。 错误:“用户/登录”,成功:“/用户/登录”【参考方案28】:扩展 Blender 的答案,这在许多情况下都会发生。我遇到的最常见的是:
-
服务器崩溃。
服务器拒绝了您的连接,很可能被
User-Agent
阻止。
socketCloseListener
,正如 Blender 的回答中所述,并不是唯一会产生挂断错误的地方。
例如发现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
标头很可能被阻止了。
【讨论】:
服务器可能拒绝您的连接的另一个原因(我只是在转到 prod 而不是 QA 时遇到了这个问题),如果您的服务器期待的是 https 请求而不是 http。【参考方案29】:看看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;
当服务器从不发送响应时发出消息。
【讨论】:
你能从功能的角度解释一下这是什么意思吗?我正在尝试通过将有问题的 url 添加到数组中然后将它们刮掉来在这里建立保护措施。我在几个地方读到这些错误可能是 Node 的排队问题,不知道补救和避免这种情况的最佳方法。 但是要等多久呢? 应该使用en.wikipedia.org/wiki/Exponential_backoff来实现“多久”。以上是关于NodeJS - “套接字挂断”实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章