使用node.js HTTP,res.end()如何保证套接字的断开连接?

问题描述:

这是Node.js的end实现:使用node.js HTTP,res.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

那么,如果仍有数据等待写入,并且接收客户端由于某种原因对接收这些数据不利,那么请求是否会结束?

我也看到了这样的问题,当试图结束由闪存上传器发出的http请求时,结束无效。我结束了做以下,这有帮助:

res.end(failureJSON, 'utf8'); 
    req.once('end', function _destroyConn() { 
     req.connection.destroy(); 
    }); 

似乎很hackish。无论如何,req.connection.destroy是否需要保证从插座断开连接?

不幸的是,res.end()不直接“保证断开连接”,因为它需要考虑HTTP Keep-Alive。根据文档,end告诉服务器,一切都已发送,并且响应已完成。无论是否立即断开,完全取决于服务器对象。

要更具体地回答您的问题,重要的是响应需要发出一个finish事件。如果你看看_finish()的实现,它几乎只是发出事件。

正如您指出的是,它并不总是叫_finish()直接......但它确实设置this.finished = true。当_flush()执行时,它发送任何剩余的数据,然后调用_finish()

这有点复杂,我不认为我可以进入更多的细节,没有错误的风险。

至于连接有时不关闭,你有检查,如果你有keep-alive配置正确?如果HTTP连接默认设置为keep-alive,则调用end将不会关闭套接字。

如果您的打印输出为res.shouldKeepAlive,它会告诉您您的服务器是否尝试使用keep-alive。如果您想停止服务器执行此操作,请在请求处理程序的开始处将其设置为false

+0

如果客户声称连接保持活动会怎样?我在考虑Flush命令没有被调用,因为客户端正在阻止它(例如通过发送数据)。 – Tom

+0

如果我正确阅读,设置'res.shouldKeepAlive = false;'应该覆盖任何客户端设置。 – loganfsmyth

+0

所以你的结论是,正确结束连接是执行以下序列? '''res.shouldKeepAlive = false; res.end();''' – Tom

我不知道这是否可以帮助您,因为我正在构建节点4.4+的框架,但是我已确认您可以在响应中发送Connection: close标头以获取节点以关闭连接。

let res = getResponseSomehow() 

res.statusCode = 408 
res.setHeader("Connection", "close") 
res.end() 

而且你摧毁的代码可以使用如下的调整:

// First we give node the time to close the connection 
// We can give it 2 seconds 
let socket = getSocketSomehow(); 
let timer = setTimeout(function() { 
    socket.destroy(); 
}, 2000); 

socket.on("close", function() { 
    clearTimeout(timer); 
}); 

我不太清楚,如果这是你想要的密切事件。我通常尝试使用库并远离net api,所以这只是一个猜测。