心跳机制在 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; // 发送 ping 消息的间隔时间(毫秒)
this.pongTimeout = 3000; // 等待 pong 响应的超时时间(毫秒)
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响应;若收到其他消息,则进行相应处理。