
WebSocket的介绍WebSocket是一种在单个TCP连接上进行全双工通信的协议它允许服务端主动向客户端推送数据。在WebSocket API中浏览器和服务器只需要完成一次握手两者之间就可以创建持久性的连接并进行双向数据传输。WebSocket 的优势较少的控制开销连接创建后数据交换时协议头较小。更强的实时性由于协议是全双工的所以服务器可以随时主动发送数据给客户端。保持连接状态与HTTP不同WebSocket需要先创建连接这就使得其成为一种有状态的协议之后通信时可以省略部分状态信息。更好的二进制支持WebSocket定义了二进制帧可以更轻松地处理二进制数据。支持扩展开发者可以扩展协议、实现部分自定义的子协议。项目需求由于最近项目上的大屏接口要换成WebSocket进行实时数据展示于是需要对WebSocket进行封装主要目标是要实现与后端持续保持心跳连接不要让WebSocket中断例如每10秒就要和后端通讯一次发送和后端沟通好固定的心跳标识符后端拿到心跳标识符就会持续保持连接如果一直没有收到前端返回的心跳标识符那么就会中断WebSocket的连接。源代码const DEFAULT_OPTIONS { // 大屏通常需要长期保持连接默认无限重连可设置为 0 禁用重连。 maxReconnectAttempts: Infinity, reconnectDelay: 1000, maxReconnectDelay: 30000, reconnectDecay: 2, protocols: undefined, onOpen: null, onClose: null, onError: null, onReconnect: null, }; export default class WebSocketClient { /** * param {string} url WebSocket 服务地址 * param {Function|Object} callback 接收消息的回调或配置对象 * param {Object} options 重连及生命周期配置 */ constructor(url, callback, options {}) { if (callback typeof callback object) { options callback; callback options.onMessage; } this.url url; this.callback typeof callback function ? callback : null; this.options { ...DEFAULT_OPTIONS, ...options }; this.ws null; this.reconnectAttempts 0; this.reconnectTimer null; this.connectPromise null; this.shouldReconnect false; } /** * 开启连接。重复调用时会复用正在建立的连接不会创建多个 WebSocket。 * returns {PromiseEvent|undefined} */ start() { this.shouldReconnect true; this.#clearReconnectTimer(); if (this.ws?.readyState WebSocket.OPEN) { return Promise.resolve(); } if (this.ws?.readyState WebSocket.CONNECTING this.connectPromise) { return this.connectPromise; } return this.#connect(); } /** start 的语义化别名。 */ open() { return this.start(); } /** * 发送原始 WebSocket 数据。 * returns {boolean} 是否发送成功 */ send(message) { if (!this.isConnected) { console.warn(WebSocket 未连接消息发送失败); return false; } try { this.ws.send(message); return true; } catch (error) { this.#emitError(error); return false; } } /** 动态设置接收消息的回调。 */ setMessageHandler(callback) { this.callback typeof callback function ? callback : null; return this; } /** * 主动关闭连接。主动关闭后不会重连且会清除尚未执行的重连任务。 */ close(code 1000, reason Closed by client) { this.shouldReconnect false; this.reconnectAttempts 0; this.#clearReconnectTimer(); const socket this.ws; if (!socket) return; if ( socket.readyState WebSocket.CONNECTING || socket.readyState WebSocket.OPEN ) { socket.close(code, reason); } else if (socket.readyState WebSocket.CLOSED) { this.ws null; } } get isConnected() { return this.ws?.readyState WebSocket.OPEN; } get readyState() { return this.ws?.readyState ?? WebSocket.CLOSED; } #connect() { let socket; try { socket this.options.protocols ? new WebSocket(this.url, this.options.protocols) : new WebSocket(this.url); } catch (error) { this.#emitError(error); this.#scheduleReconnect(); return Promise.reject(error); } this.ws socket; this.connectPromise new Promise((resolve, reject) { let settled false; const resolveConnection (event) { if (settled) return; settled true; resolve(event); }; const rejectConnection (error) { if (settled) return; settled true; reject(error); }; socket.onopen (event) { if (socket ! this.ws) return; this.reconnectAttempts 0; this.connectPromise null; resolveConnection(event); this.#invoke(this.options.onOpen, event); }; socket.onmessage (event) { if (socket ! this.ws) return; this.#invoke(this.callback, event.data, event); }; socket.onerror (event) { if (socket ! this.ws) return; const error new Error(WebSocket 连接或通信异常); error.event event; rejectConnection(error); this.#emitError(error, event); }; socket.onclose (event) { if (socket ! this.ws) return; this.ws null; this.connectPromise null; rejectConnection(new Error(WebSocket 连接已关闭code: ${event.code})); this.#invoke(this.options.onClose, event); if (this.shouldReconnect) { this.#scheduleReconnect(); } }; }); return this.connectPromise; } #scheduleReconnect() { if (!this.shouldReconnect || this.reconnectTimer) return; const maxAttempts this.options.maxReconnectAttempts; if (this.reconnectAttempts maxAttempts) { console.warn(WebSocket 已达到最大重连次数停止重连); return; } this.reconnectAttempts 1; const delay Math.min( this.options.reconnectDelay * this.options.reconnectDecay ** (this.reconnectAttempts - 1), this.options.maxReconnectDelay, ); this.#invoke(this.options.onReconnect, this.reconnectAttempts, delay); this.reconnectTimer setTimeout(() { this.reconnectTimer null; if (!this.shouldReconnect) return; // 重连由类内部维护捕获失败避免产生未处理的 Promise rejection。 this.#connect().catch(() {}); }, delay); } #clearReconnectTimer() { if (!this.reconnectTimer) return; clearTimeout(this.reconnectTimer); this.reconnectTimer null; } #emitError(error, event) { if (typeof this.options.onError function) { this.#invoke(this.options.onError, error, event); return; } console.error(WebSocket 异常:, error); } #invoke(callback, ...args) { if (typeof callback ! function) return; try { callback(...args); } catch (error) { console.error(WebSocket 回调执行异常:, error); } } }用法import WebSocketClient from /request/ws export default { data(){ return{ wsClient:null } }, mounted(){ this.wsClient new WebSocketClient(ws://192.168.31.21:8090/ws, this.getWSMessage); this.wsClient.start() }, methods:{ getWSMessage(data){ console.log(data,ddddddddddddddd) }, sendMessage(){ let obj { type: 555, data: 111111 } this.wsClient.send(JSON.stringify(obj)) } }, beforeDestroy() { this.wsClient.close() } }