С помощью node.js HTTP как res.end () гарантирует отключение сокета? - PullRequest
1 голос
/ 07 сентября 2011

Это реализация 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.

Итак, если все еще есть данные, ожидающие записи, и клиент-получатель по какой-то причине не совсем уверен в получении этих данных, будет ли когда-нибудь завершен запрос?

Я также видел такую ​​проблему, когдазавершение неэффективно при попытке завершить запрос http, сделанный загрузчиком флэш-памяти.Я закончил тем, что сделал следующее, что помогло:

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

Кажется очень хакерским.В любом случае, необходимо ли такое поведение с req.connection.destroy, чтобы гарантировать отключение от сокета?

Ответы [ 2 ]

6 голосов
/ 08 сентября 2011

К сожалению, 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 голосов
/ 26 июня 2016

Я не знаю, поможет ли это вам, поскольку я создаю свою среду для узла 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);
});

Я не совсем уверен, что вы хотите, чтобы это событие close .Я обычно пытаюсь использовать библиотеку и держаться подальше от net API, так что это всего лишь предположение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...