如何在javascript 中实现Ping/Pong 的webSocket 连接请求?

How to implement Ping/Pong request for webSocket connection alive in javascript?

我在 javascript 中使用 websocket。但是连接在一分钟后关闭。

我想知道一些事情:

1- Websocket 不是自然地提供 Ping/Pong 消息来关闭连接吗?我认为它必须。否则websocket和TCP连接有什么区别?

2- 如果我必须发送 ping/pong 消息,ping 消息是如何发送的?我需要做什么? WebSocket 对象是否提供 ping 方法?或者我应该将方法称为 websocket.send("ping") 吗?我在 javascipt 中使用自然的 WebSocket 对象。

3- 服务器应该用 Pong 响应 Ping 请求吗?这应该在服务器端单独实现吗?

注意:对不起我的英语。

是的,websockets 中有 ping/pong 个帧。这是一个使用 ws 模块的示例,其中服务器正在发起 ping 请求:

const http = require('http');
const ws = require('ws');

const server = http.createServer(function(req_stream_in, res_stream_out) {
  // handle regular HTTP requests here
});
const webSocketServer = new ws.Server({
  path: "/websocket",
  server: server
});

const connected_clients = new Map();

webSocketServer.on('connection', function connection(ws_client_stream) {
  // NOTE: only for demonstration, will cause collisions.  Use a UUID or some other identifier that's actually unique.
  const this_stream_id = Array.from(connected_clients.values()).length;

  // Keep track of the stream, so that we can send all of them messages.
  connected_clients.set(this_stream_id, ws_client_stream);

  // Attach event handler to mark this client as alive when pinged.
  ws_client_stream.is_alive = true;
  ws_client_stream.on('pong', () => { ws_client_stream.is_alive = true; });

  // When the stream is closed, clean up the stream reference.
  ws_client_stream.on('close', function() {
    connected_clients.delete(this_stream_id);
  });
});

setInterval(function ping() {
  Array.from(connected_clients.values()).forEach(function each(client_stream) {
    if (!client_stream.is_alive) { client_stream.terminate(); return; }
    client_stream.is_alive = false;
    client_stream.ping();
  });
}, 1000);

此时,心跳通常在服务器端实现:客户端无能为力。

但是,如果服务器不断终止您的套接字连接,而您无法控制它,则客户端有可能在一定时间间隔内向 websocket 发送任意数据:

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your grave!
  heartbeat();
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();

我强烈建议尝试弄清楚服务器端发生了什么,而不是尝试在客户端解决它。

在 ouni 的解决方案中,heartbeat() 没有启动。它在像这样的 open 事件中工作:

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your grave!
  socket.on("open", heartbeat); // heartbeat when the socket is open
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();

Mozilla 记录了 ping/pong 的专用约定。

At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.

A ping or pong is just a regular frame, but it's a control frame. Pings have an opcode of 0x9, and pongs have an opcode of 0xA. When you get a ping, send back a pong with the exact same Payload Data as the ping (for pings and pongs, the max payload length is 125). You might also get a pong without ever sending a ping; ignore this if it happens.

If you have gotten more than one ping before you get the chance to send a pong, you only send one pong.

参见:https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets

在此处从浏览器端找到关于 ping/pong 的更多深入讨论:Sending websocket ping/pong frame from browser

更具体地说,阅读有关 ping/pong 的 Websocket RFC 6455