Websockt断线重连和心跳机制
WebSocket心跳机制原理
1.客户端建立WebSocket连接。
2.客户端向服务器发送心跳数据包,服务器接收并返回一个表示接收到心跳数据包的响应。
3.当服务器没有及时接收到客户端发送的心跳数据包时,服务器会发送一个关闭连接的请求。
4.服务器定时向客户端发送心跳数据包,客户端接收并返回一个表示接收到心跳数据包的响应。
5.当客户端没有及时接收到服务器发送的心跳数据包时,客户端会重新连接WebSocket
现代浏览器已经不需要前端写心跳机制
其实websocket协议里头,是有控制帧的,就是ping,pong
协议规定,连接两端,一端发送了Ping帧, 那么接收方必须尽快的回复Pong帧数据
参考这个
The Ping frame contains an opcode of 0x9.
The Pong frame contains an opcode of 0xA.
chrome是实现了ping/pong的,只要服务端发送了ping, 那么会立即收到一个pong ,以chrome浏览器举例,只要服务器发送ping消息,浏览器会自动返回一个pong消息。反之也一样。这就是浏览器自带的心跳机制。
各位可以参考下面这个代码自行测试一下:
js
const WebSocket = require("ws");
const HEARTBEAT_INTERVAL = 5000; // 心跳间隔(毫秒)
const CLIENT_TIMEOUT = 15000; // 客户端超时时间(毫秒)
const wss = new WebSocket.Server({ port: 3000 });
wss.on("connection", function connection(ws) {
console.log("Client connected");
let heartbeatInterval;
let messageCounter = 0;
const intervalId = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
const message = JSON.stringify({ type: "msg", text: messageCounter });
ws.send(message);
messageCounter++;
} else {
clearInterval(intervalId);
}
}, 2000); // 每2秒发送一条消息
// 客户端心跳检测
function heartbeat() {
clearInterval(heartbeatInterval);
heartbeatInterval = setInterval(() => {
if (ws.isAlive === false) {
// console.log("Client is not responding. Attempting to reconnect...");
console.log("Client is not responding.");
// 如果客户端由于网络,宕机等原因停止,服务端就停止wsocket连接
return ws.terminate();
} else {
// 如果客户端仍然存活,发送心跳消息
ws.isAlive = false;
ws.ping();
console.log("ping~~");
}
}, HEARTBEAT_INTERVAL);
}
ws.isAlive = true;
// 监听来自客户端的消息
ws.on("message", function incoming(message) {
console.log("received: %s", message);
// 解析消息
let parsedMessage;
try {
parsedMessage = JSON.parse(message);
} catch (error) {
console.error("Error parsing message:", error);
return;
}
// 处理消息(示例:如果收到的是心跳消息,则更新客户端状态)
if (parsedMessage.type === "heartbeat") {
// console.log("Received heartbeat");
ws.isAlive = true;
return;
}
// 发送消息给客户端
ws.send(JSON.stringify({ type: "chat", text: parsedMessage.text }));
});
// 监听心跳消息
ws.on("pong", function heartbeatResponse() {
console.log("pong received");
ws.isAlive = true;
});
// 启动心跳检测
heartbeat();
// 监听连接关闭事件
ws.on("close", function close() {
console.log("Client disconnected");
clearInterval(heartbeatInterval);
clearInterval(intervalId);
});
});
console.log("WebSocket server running at ws://localhost:3000/");
用浏览器连接一下本机的ws, new WebSocket('http://localhost:3000')
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebSocket Client</title>
</head>
<style>
#messages {
height: 300px;
width: 612px;
overflow: auto;
border: 1px dashed #ccc;
}
</style>
<body>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Enter message" />
<button onclick="sendMessage()">Send</button>
<button onclick="closeWebSocket()">Close WebSocket</button>
<button onclick="toggleReconnect()">Toggle Heartbeat</button>
<button onclick="openWebSocket()">Open WebSocket</button>
<script>
const messagesDiv = document.getElementById("messages");
let ws;
let heartbeatInterval;
let shouldReconnect = true;
function connectWebSocket() {
ws = new WebSocket("ws://localhost:3000");
ws.addEventListener("open", function (event) {
console.log("Connected to WebSocket server");
messagesDiv.innerHTML += "<p>Connected to WebSocket server</p>";
startHeartbeat();
});
ws.addEventListener("message", function (event) {
console.log("Received message:", event.data);
const message = JSON.parse(event.data);
if (message.type === "chat") {
messagesDiv.innerHTML +=
"<p>Received message: " + message.text + "</p>";
resetHeartbeat();
}
// else if (message.type === "reconnect") {
// console.log(
// "Received reconnect signal from server. Reconnecting..."
// );
// reconnectWebSocket();
// }
});
ws.addEventListener("close", function (event) {
console.log("Disconnected from WebSocket server");
messagesDiv.innerHTML += "<p>Disconnected from WebSocket server</p>";
stopHeartbeat();
if (shouldReconnect) {
reconnectWebSocket();
}
});
ws.addEventListener("error", function (event) {
console.error("WebSocket error:", event);
// 可以在这里处理错误,例如无法连接到服务器、连接中断等,以及WebSocket API本身的一些限制和错误。
});
}
function sendMessage() {
const message = document.getElementById("messageInput").value;
ws.send(JSON.stringify({ type: "chat", text: message }));
console.log("Sent message: " + message);
messagesDiv.innerHTML += "<p>Sent message: " + message + "</p>";
}
function startHeartbeat() {
heartbeatInterval = setInterval(function () {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "heartbeat" }));
console.log("Sent heartbeat");
} else {
console.log("WebSocket connection is not open");
}
}, 5000);
}
function resetHeartbeat() {
clearInterval(heartbeatInterval);
if (shouldReconnect) {
startHeartbeat();
}
}
function stopHeartbeat() {
clearInterval(heartbeatInterval);
}
function closeWebSocket() {
shouldReconnect = false; // 关闭 WebSocket 连接后取消重连
ws.close();
}
function toggleReconnect() {
shouldReconnect = !shouldReconnect; // 切换重连选项
console.log("Reconnect is", shouldReconnect ? "enabled" : "disabled");
if (!shouldReconnect) {
stopHeartbeat();
} else {
startHeartbeat();
}
}
function openWebSocket() {
shouldReconnect = true; // 打开 WebSocket 连接时允许重连
connectWebSocket();
}
function reconnectWebSocket() {
console.log("Reconnecting to WebSocket server...");
setTimeout(function () {
connectWebSocket();
}, 2000);
}
// 初始化时连接 WebSocket
connectWebSocket();
</script>
</body>
</html>
然后就可以看到服务端打印出来

然后你在chrome里面是看不见这个消息的,js代码也不需要任何处理,服务端不停的ping,客户端就会自动pong回来.