心跳机制在 WebSocket 通信中是一种常用的技术,用于维持连接的稳定性、检测连接是否正常。以下为你详细介绍在前端使用 WebSocket 时如何实现心跳机制,以及相关代码示例。
实现思路
- 发送心跳包:客户端定期向服务器发送一个特定格式的消息(心跳包),以表明自己处于活跃状态。
- 接收响应:服务器收到心跳包后,返回一个响应消息,客户端通过检查是否收到响应来判断连接是否正常。
- 超时处理:如果客户端在一定时间内没有收到服务器的响应,认为连接可能出现问题,尝试重新连接。
示例代码
class WebSocketClient {
constructor(url) {
this.url = url;
this.socket = null;
this.reconnectInterval = 5000;
this.reconnectTimer = null;
this.messageHandlers = [];
this.errorHandlers = [];
this.openHandlers = [];
this.closeHandlers = [];
this.heartbeatInterval = 3000;
this.heartbeatTimer = null;
this.lastHeartbeatResponseTime = null;
this.heartbeatTimeout = 5000;
this.heartbeatTimeoutTimer = null;
this.tryConnect();
}
tryConnect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket 连接已建立');
clearInterval(this.reconnectTimer);
this.openHandlers.forEach(handler => handler());
this.startHeartbeat();
};
this.socket.onmessage = (event) => {
if (event.data === 'heartbeat_response') {
this.lastHeartbeatResponseTime = Date.now();
clearTimeout(this.heartbeatTimeoutTimer);
this.heartbeatTimeoutTimer = setTimeout(() => {
this.handleHeartbeatTimeout();
}, this.heartbeatTimeout);
} else {
this.messageHandlers.forEach(handler => handler(event.data));
}
};
this.socket.onerror = (error) => {
console.error('WebSocket 连接出错:', error);
this.errorHandlers.forEach(handler => handler(error));
this.reconnect();
};
this.socket.onclose = (event) => {
console.log('WebSocket 连接已关闭,代码:', event.code, '原因:', event.reason);
this.closeHandlers.forEach(handler => handler(event));
this.reconnect();
this.stopHeartbeat();
};
}
reconnect() {
if (!this.reconnectTimer) {
this.reconnectTimer = setInterval(() => {
console.log('尝试重新连接 WebSocket...');
this.tryConnect();
}, this.reconnectInterval);
}
}
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
console.error('无法发送消息,WebSocket 未连接');
}
}
onMessage(handler) {
this.messageHandlers.push(handler);
}
onError(handler) {
this.errorHandlers.push(handler);
}
onOpen(handler) {
this.openHandlers.push(handler);
}
onClose(handler) {
this.closeHandlers.push(handler);
}
close() {
if (this.socket) {
clearInterval(this.reconnectTimer);
this.socket.close();
this.stopHeartbeat();
}
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
this.sendMessage('heartbeat');
this.heartbeatTimeoutTimer = setTimeout(() => {
this.handleHeartbeatTimeout();
}, this.heartbeatTimeout);
}, this.heartbeatInterval);
}
stopHeartbeat() {
clearInterval(this.heartbeatTimer);
clearTimeout(this.heartbeatTimeoutTimer);
}
handleHeartbeatTimeout() {
console.log('心跳超时,尝试重新连接...');
this.socket.close();
}
}
const socketClient = new WebSocketClient('ws://echo.websocket.org');
socketClient.onMessage((message) => {
console.log('收到消息:', message);
});
socketClient.onOpen(() => {
console.log('连接已打开');
});
socketClient.onError((error) => {
console.error('发生错误:', error);
});
socketClient.onClose((event) => {
console.log('连接关闭:', event);
});
代码解释
- 构造函数:除了初始化基本的 WebSocket 连接参数外,还初始化了心跳机制相关的参数,如心跳间隔时间
heartbeatInterval
、心跳超时时间 heartbeatTimeout
等。 startHeartbeat
方法:使用 setInterval
定时发送心跳包(heartbeat
),并启动一个定时器 heartbeatTimeoutTimer
用于检测心跳超时。onmessage
事件处理:当收到服务器的心跳响应消息(heartbeat_response
)时,更新最后一次收到响应的时间 lastHeartbeatResponseTime
,并重置心跳超时定时器。handleHeartbeatTimeout
方法:当心跳超时发生时,关闭当前的 WebSocket 连接,触发重连机制。stopHeartbeat
方法:停止心跳定时器和超时定时器。
通过上述代码,你可以在前端 WebSocket 通信中实现一个简单的心跳机制,确保连接的稳定性。同时,服务器端也需要相应地处理心跳包并返回响应消息。
分享
用ping\pong来实现
在 WebSocket 通信里,ping
和 pong
是标准的心跳机制实现方式。ping
是客户端或服务器发送的探测消息,pong
则是对 ping
的响应。下面是一个使用 JavaScript 封装 WebSocket 并利用 ping
/pong
实现心跳机制的详细示例。
实现思路
- 客户端:周期性地发送
ping
消息,同时监听 pong
响应,若超时未收到 pong
则认为连接异常。 - 服务器端:接收到
ping
消息后,立即返回 pong
响应。
前端代码示例
class WebSocketWithPingPong {
constructor(url) {
this.url = url;
this.socket = null;
this.pingInterval = 5000;
this.pongTimeout = 3000;
this.pingTimer = null;
this.pongTimeoutTimer = null;
this.reconnectInterval = 5000;
this.reconnectTimer = null;
this.messageHandlers = [];
this.errorHandlers = [];
this.openHandlers = [];
this.closeHandlers = [];
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket 连接已建立');
clearInterval(this.reconnectTimer);
this.openHandlers.forEach(handler => handler());
this.startPing();
};
this.socket.onmessage = (event) => {
if (event.data === 'pong') {
clearTimeout(this.pongTimeoutTimer);
} else {
this.messageHandlers.forEach(handler => handler(event.data));
}
};
this.socket.onerror = (error) => {
console.error('WebSocket 连接出错:', error);
this.errorHandlers.forEach(handler => handler(error));
this.reconnect();
};
this.socket.onclose = (event) => {
console.log('WebSocket 连接已关闭,代码:', event.code, '原因:', event.reason);
this.closeHandlers.forEach(handler => handler(event));
this.reconnect();
this.stopPing();
};
}
reconnect() {
if (!this.reconnectTimer) {
this.reconnectTimer = setInterval(() => {
console.log('尝试重新连接 WebSocket...');
this.connect();
}, this.reconnectInterval);
}
}
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
console.error('无法发送消息,WebSocket 未连接');
}
}
onMessage(handler) {
this.messageHandlers.push(handler);
}
onError(handler) {
this.errorHandlers.push(handler);
}
onOpen(handler) {
this.openHandlers.push(handler);
}
onClose(handler) {
this.closeHandlers.push(handler);
}
close() {
if (this.socket) {
clearInterval(this.reconnectTimer);
this.socket.close();
this.stopPing();
}
}
startPing() {
this.pingTimer = setInterval(() => {
this.sendMessage('ping');
this.pongTimeoutTimer = setTimeout(() => {
this.handlePongTimeout();
}, this.pongTimeout);
}, this.pingInterval);
}
stopPing() {
clearInterval(this.pingTimer);
clearTimeout(this.pongTimeoutTimer);
}
handlePongTimeout() {
console.log('未收到 pong 响应,尝试重新连接...');
this.socket.close();
}
}
const socket = new WebSocketWithPingPong('ws://your-websocket-server-url');
socket.onOpen(() => {
console.log('连接已打开');
});
socket.onMessage((message) => {
console.log('收到消息:', message);
});
socket.onError((error) => {
console.error('发生错误:', error);
});
socket.onClose((event) => {
console.log('连接关闭:', event);
});
代码解释
- 构造函数:初始化 WebSocket 连接所需的参数,包括
ping
发送间隔、pong
超时时间、重连间隔等,并且调用 connect
方法开始建立连接。 connect
方法:创建 WebSocket 实例,为 onopen
、onmessage
、onerror
和 onclose
事件绑定处理函数。连接建立成功后,启动 ping
发送机制。startPing
方法:使用 setInterval
周期性地发送 ping
消息,同时启动一个 pong
超时定时器。onmessage
事件处理:当收到 pong
响应时,清除 pong
超时定时器;若收到其他消息,则调用注册的消息处理函数。handlePongTimeout
方法:若在 pong
超时时间内未收到 pong
响应,关闭当前连接并触发重连机制。- 其他方法:如
sendMessage
用于发送消息,onMessage
、onError
、onOpen
和 onClose
用于注册相应的事件处理函数,close
用于关闭连接,stopPing
用于停止 ping
发送和 pong
超时检测。
服务器端示例(使用 Node.js 和 ws
库)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
if (message === 'ping') {
ws.send('pong');
} else {
console.log('收到消息:', message);
}
});
});
console.log('WebSocket 服务器已启动,监听端口 8080');
服务器端代码解释
- 使用
ws
库创建一个 WebSocket 服务器,监听 8080 端口。 - 当有客户端连接时,监听
message
事件,若收到 ping
消息,立即返回 pong
响应;若收到其他消息,则进行相应处理。