对于node.js HTTP,res.end() 如何保证套接字断开连接?
这是node.js的end
实现:
OutgoingMessage.prototype.end = function(data, encoding) {
if (this.finished) {
return false;
}
if (!this._header) {
this._implicitHeader();
}
if (data && !this._hasBody) {
console.error('This type of response MUST NOT have a body. ' +
'Ignoring data passed to end().');
data = false;
}
var ret;
var hot = this._headerSent === false &&
typeof(data) === 'string' &&
data.length > 0 &&
this.output.length === 0 &&
this.connection &&
this.connection.writable &&
this.connection._httpMessage === this;
if (hot) {
// Hot path. They're doing
// res.writeHead();
// res.end(blah);
// HACKY.
if (this.chunkedEncoding) {
var l = Buffer.byteLength(data, encoding).toString(16);
ret = this.connection.write(this._header + l + CRLF +
data + '\r\n0\r\n' +
this._trailer + '\r\n', encoding);
} else {
ret = this.connection.write(this._header + data, encoding);
}
this._headerSent = true;
} else if (data) {
// Normal body write.
ret = this.write(data, encoding);
}
if (!hot) {
if (this.chunkedEncoding) {
ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
} else {
// Force a flush, HACK.
ret = this._send('');
}
}
this.finished = true;
// There is the first message on the outgoing queue, and we've sent
// everything to the socket.
if (this.output.length === 0 && this.connection._httpMessage === this) {
debug('outgoing message end.');
this._finish();
}
return ret;
};
来源:https://github.com/joyent/node/blob/master/lib/http.js#L645
显然,只有当 output.length === 0 时,连接才“完成”
。
因此,如果仍有数据等待写入,并且接收客户端由于某种原因无法接收该数据,那么请求是否会结束?
我还看到过这样的问题:在尝试结束 Flash 上传器发出的 http 请求时,结束无效。我最终做了以下事情,这确实有帮助:
res.end(failureJSON, 'utf8');
req.once('end', function _destroyConn() {
req.connection.destroy();
});
看起来很黑客。无论如何,是否需要使用 req.connection.destroy
的这种行为来保证与套接字的断开连接?
This is node.js' end
implementation:
OutgoingMessage.prototype.end = function(data, encoding) {
if (this.finished) {
return false;
}
if (!this._header) {
this._implicitHeader();
}
if (data && !this._hasBody) {
console.error('This type of response MUST NOT have a body. ' +
'Ignoring data passed to end().');
data = false;
}
var ret;
var hot = this._headerSent === false &&
typeof(data) === 'string' &&
data.length > 0 &&
this.output.length === 0 &&
this.connection &&
this.connection.writable &&
this.connection._httpMessage === this;
if (hot) {
// Hot path. They're doing
// res.writeHead();
// res.end(blah);
// HACKY.
if (this.chunkedEncoding) {
var l = Buffer.byteLength(data, encoding).toString(16);
ret = this.connection.write(this._header + l + CRLF +
data + '\r\n0\r\n' +
this._trailer + '\r\n', encoding);
} else {
ret = this.connection.write(this._header + data, encoding);
}
this._headerSent = true;
} else if (data) {
// Normal body write.
ret = this.write(data, encoding);
}
if (!hot) {
if (this.chunkedEncoding) {
ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
} else {
// Force a flush, HACK.
ret = this._send('');
}
}
this.finished = true;
// There is the first message on the outgoing queue, and we've sent
// everything to the socket.
if (this.output.length === 0 && this.connection._httpMessage === this) {
debug('outgoing message end.');
this._finish();
}
return ret;
};
Source: https://github.com/joyent/node/blob/master/lib/http.js#L645
Apparently, the connection is only "finished" when output.length === 0
.
So, if there is still data waiting to be written, and the receiving client for some reason is dodgy about receiving this data, will the request ever be ended?
I have also seen such issue where an end is not effective while trying to end a http request made by a flash uploader. I ended up doing the following, which did help:
res.end(failureJSON, 'utf8');
req.once('end', function _destroyConn() {
req.connection.destroy();
});
Seems very hackish. Anyway, is such behavior with req.connection.destroy
needed to guarantee a disconnection from the socket?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不幸的是,res.end() 并不直接“保证套接字断开连接”,因为它需要考虑 HTTP Keep-Alive。根据文档,
end
告诉服务器所有内容都已发送,并且响应已完成。是否立即断开连接完全取决于服务器对象。为了更具体地回答您的问题,重要的是响应需要发出
finish
事件。如果您看一下_finish()
的实现,它几乎只是发出事件。正如您所指出的,它并不总是直接调用
_finish()
…但它确实设置了this.finished = true
。当_flush()
执行时,它会发送所有剩余的数据,然后调用_finish()
。这有点复杂,我认为我不能在没有犯错的风险的情况下详细讨论。
至于连接有时不会关闭,您是否检查过
keep-alive
配置是否正确?如果默认使用keep-alive
设置 HTTP 连接,则调用end
将不会关闭套接字。如果您打印出
res.shouldKeepAlive
,它会告诉您服务器是否正在尝试使用keep-alive
。如果您想阻止服务器执行此操作,请在请求处理程序开始时将其设置为false
。Unfortunately,
res.end()
does not directly “guarantee a disconnection of the socket” because it needs to account for HTTP Keep-Alive. According to the docs,end
tells the server that everything has been sent, and that the response is complete. It’s entirely up to the server object whether or not to disconnect immediately.To answer your question more specifically, the important thing is that the response needs to emit a
finish
event. If you take a look at the implementation of_finish()
, it pretty much just emits the event.As you noted though, it doesn’t always call
_finish()
directly…but it did setthis.finished = true
. When_flush()
executes, it sends any remaining data and THEN calls_finish()
.It’s kind of complicated, and I don’t think I can go into any more detail without the risk of being wrong.
As far as connections sometimes not closing, have you checked if you have
keep-alive
configured properly? If the HTTP connection is set up withkeep-alive
by default, then callingend
will not close the socket.If your print out
res.shouldKeepAlive
, it will tell you if your server is trying to usekeep-alive
. Set it tofalse
at the start of your request handler if you want to stop the server from doing this.我不知道这是否对您有帮助,因为我正在为节点 4.4+ 构建框架,但我已经确认您可以在响应中发送
Connection: close
标头,以让节点关闭连接。另外,您的销毁代码可以使用以下调整:
我不太确定这是否是您想要的 close 事件。我通常尝试使用库并远离
net
api,所以这只是一个猜测。I don't know if this helps you as I am building my framework for node 4.4+ but I have confirmed that you can send
Connection: close
header in your response to get node to close the connection.Also your destroy code could use the following tweak:
I'm not quite sure if it's the close event you want. I normally try to use a library and stay away from the
net
api so this is just a guess.