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回来.