websocket协议,如何实现 ping / pong 心跳机制?
发布于 作者:苏南大叔 来源:程序如此灵动~

聚焦于websocket
协议的心跳机制,按着最通俗的理解,服务器和客户端双方,一方发送ping
,另外一方回复pong
,就完成了心跳检测了。在websocket
协议里面,都是ws.send(msg)
发送消息的。所以,只是把msg
换成ping
或者pong
字符串么?显然这是个陷阱式问题,答案必然不是这样。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10
,nodejs@20.18.0
,ws@8.18.0
。
前文回顾
基于nodejs
,可以实现websocket
的双方通信。参考文章:
服务器端需要安装ws
第三方客户端。参考命令:
npm i ws --save
心跳机制
不同于通俗理解上的ping
和pong
消息发送。websocket
协议里面的ping
和pong
,仅仅是报文里面的一个状态位置,并不是消息体。
服务器端的ws
拥有ws.ping()
和ws.on('pong')
事件,而浏览器客户端的原生WebSocket
里面,并没有.ping()
或者.pong()
函数。
逻辑是这样的:
服务器端,定期轮询去ping()
客户端,而浏览器客户端,被动隐式的.pong()
(代码里面是没写的)。而通过服务器端的ws.on('pong')
事件,来获得pong
事件。
服务器端:
let WebSocket = require("ws");
let server = new WebSocket.Server({ port: 8080 });
const clients = new Set();
let interval;
server.on("connection", function connection(ws) {
clients.add(ws);
ws.isAlive = true;
interval = setInterval(function ping() { // ping
clients.forEach((ws) => {
if (ws.isAlive === false) {
clients.delete(ws);
return ws.terminate();
}
ws.ping(() => {
console.log('ping sent');
});
});
}, 5000);
ws.on('pong', () => { // pong
ws.isAlive = true;
console.log('pong received');
});
ws.on("close", function close() {
ws.isAlive = false;
clients.delete(ws);
});
});
server.on("close", function close() {
clearInterval(interval);
});
客户端浏览器什么都不做,因为没有办法主动ping
,pong
还是个被动技能。
广播
广播并不是内置的功能,它是建立在对客户端ws
的收集的基础上的。通过对在线的客户端进行遍历,然后每个都发送广播消息。
let WebSocket = require("ws");
let server = new WebSocket.Server({ port: 8080 });
const clients = new Set();
server.on("connection", function connection(ws) {
clients.add(ws);
ws.isAlive = true;
//...
function broadcast(data) {
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
}
// setInterval(()=>{
// broadcast(Date.now());
// }, 5000);
});
踢人
在一定的机制下,服务器端是可以主动断开链接的,也就是俗称的踢人。关键代码如下:
server.clients.forEach((ws)=>{
if (ws.isAlive === false) {
//...
return ws.terminate();
}
});
完整代码
服务端:
let WebSocket = require("ws");
let server = new WebSocket.Server({ port: 8080 });
const clients = new Set();
let interval;
server.on("connection", function connection(ws) {
clients.add(ws);
// ################################################
// 心跳检测机制
ws.isAlive = true;
interval = setInterval(function ping() { // ping
clients.forEach((ws) => {
if (ws.isAlive === false) {
clients.delete(ws);
return ws.terminate();
}
ws.ping(() => {
console.log('ping sent');
});
});
}, 5000);
ws.on('pong', () => { // pong
ws.isAlive = true;
console.log('pong received');
});
ws.on("close", function close() {
ws.isAlive = false;
clients.delete(ws);
});
// ################################################
// 消息发送/广播
ws.on("message", (msg) => {
console.log("received: %s", msg);
});
function broadcast(data) {
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
}
// setInterval(()=>{
// broadcast(Date.now());
// }, 15000);
// ################################################
});
server.on("close", function close() {
clearInterval(interval);
});
客户端:
let ws = new WebSocket("ws://localhost:8080");
ws.onopen = function () {
ws.send("苏南");
};
ws.onclose = () => {
console.log("服务器断开链接");
};
ws.onerror = () => {
console.log("有错误发生");
};
ws.onmessage = function (e) {
let msg = e.data;
console.log("客户端收到消息:%s", msg);
};
相关文档
- 在线的客户端测试,可以发送和接收消息 http://www.websocket-test.com/


