2011-09-07 43 views
1

這是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是否需要保證從插座斷開連接?

回答

5

不幸的是,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

0

我不知道這是否可以幫助您,因爲我正在構建節點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,所以這只是一個猜測。

相關問題