心跳机制在 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响应;若收到其他消息,则进行相应处理。