Connection:keep-alive 没有为 NodeJS 中的 HTTP 请求保持套接字连接

Connection:keep-alive is not keeping the socket connection for HTTP request in NodeJS

我听说Connection:Keep-Alive header 会告诉服务器保持客户端和服务器之间的连接一段时间,以防止每次客户端向服务器建立请求时的努力。我试图将其添加到请求的 header 但它没有按预期工作。套接字连接仍会在每次请求时关闭。

你能帮忙解释一下为什么会这样吗?我是否遗漏了有关 Connection:Keep-Alive 的内容,或者我是否以错误的方式实施了它?

客户:

const http = require("http");

const options = {
  port: 4000,
  headers: {
    connection: "keep-alive",
  },
};

function request() {
  console.log(`making a request`);

  const req = http.request(options, (res) => {
    console.log(`STATUS: ${res.statusCode}`);
    console.log(`HEADERS: ${JSON.stringify(res.headers)}`);

    res.setEncoding("utf8");
    res.on("data", (chunk) => {
      console.log(`BODY: ${chunk}`);
    });
    res.on("end", () => {
      console.log("No more data in response.");
    });
  });

  req.on("error", (e) => {
    console.error(`problem with request: ${e.message}`);
  });

  req.end();
}

setInterval(() => {
  request();
}, 3000);

服务器:

const http = require("http");

const server = http.createServer((req, res) => {
  setTimeout(() => {
    res.end();
  }, 500);
});

server.on("connection", function (socket) {
  socket.id = Date.now();
  console.log(
    "A new connection was made by a client." + ` SOCKET ${socket.id}`
  );
  socket.on("end", function () {
    console.log(
      `SOCKET ${socket.id} END: other end of the socket sends a FIN packet`
    );
  });

  socket.on("timeout", function () {
    console.log(`SOCKET ${socket.id} TIMEOUT`);
  });

  socket.on("error", function (error) {
    console.log(`SOCKET ${socket.id} ERROR: ` + JSON.stringify(error));
  });

  socket.on("close", function (had_error) {
    console.log(`SOCKET ${socket.id} CLOSED. IT WAS ERROR: ` + had_error);
  });
});
server.on("clientError", (err, socket) => {
  socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
});

server.listen(4000);

这就是我得到的:

客户:

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:44 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:47 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:50 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

和服务器:

A new connection was made by a client. SOCKET 1586437543111
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437543111 END: other end of the socket sends a FIN packet
SOCKET 1586437543111 CLOSED. IT WAS ERROR: false

A new connection was made by a client. SOCKET 1586437546091
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437546091 END: other end of the socket sends a FIN packet
SOCKET 1586437546091 CLOSED. IT WAS ERROR: false

A new connection was made by a client. SOCKET 1586437549095
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437549095 END: other end of the socket sends a FIN packet
SOCKET 1586437549095 CLOSED. IT WAS ERROR: false

NodeJS 版本:10.15.0


还有一件事让我更困惑的是,当我使用telnet localhost 1234发出请求时:

GET / HTTP/1.1
Connection: Keep-Alive
Host: localhost

然后连接没有关闭,并且没有像预期的那样创建新连接。那是因为telnet接收Connection: Keep-Alive并自行保持连接?

对于您的用例,我认为 websockets 是比传统解决方案更好的解决方案 http/https。客户端和服务器之间的连接将在 websocket 上保持活动状态,直到客户端关闭,或者如果您强制关闭连接。

Node.js websocket package

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });

  ws.send('something');
});

Client side websockets

I have heard that Connection:Keep-Alive header will tell the server to keep the connection between client and server for a while to prevent the effort for each time client establish a request to server.

这是错误的。客户端不要求服务器提供任何东西,它只是向服务器提出一些建议。

Connection: keep-alive 只是告诉服务器 客户端愿意保持连接打开 以便发送更多请求。并隐含地建议,如果服务器能做同样的事情以便为更多请求重用现有连接,那将是很好的。然后 服务器可以自行决定 是在发送响应后保持连接打开还是立即关闭它,或者在某些不活动或其他情况后关闭它。

当然,客户端只发送 HTTP 头是不够的(反正 HTTP/1.1 是隐式的,所以不需要发送)。它实际上必须保持 TCP 连接打开,以便在同一个 TCP 连接上发送更多请求。从服务器日志中可以看出,您当前的实现不会执行此操作,即客户端首先关闭连接:

SOCKET 1586437549095 END: other end of the socket sends a FIN packet

为了在客户端实现真正的保持活动状态,您可以使用代理通过多个 http.request 保持连接打开。有关更多信息,请参阅 。