|
@@ -0,0 +1,182 @@
|
|
|
+// websocket.js
|
|
|
+// 封装一个兼容多端的 uniapp WebSocket 管理类,支持自动重连与心跳机制,便于在uni-app项目中统一管理WebSocket连接
|
|
|
+
|
|
|
+class UniWebSocket {
|
|
|
+ /**
|
|
|
+ * 构造函数,初始化WebSocket管理器
|
|
|
+ * @param {string} url - WebSocket服务器地址
|
|
|
+ * @param {object} options - 可选配置项,如最大重连次数、心跳间隔等
|
|
|
+ */
|
|
|
+ constructor(url, options = {}) {
|
|
|
+ this.url = url; // WebSocket服务器地址
|
|
|
+ this.options = options; // 连接配置参数
|
|
|
+ this.socketTask = null; // uni.connectSocket返回的socket任务对象
|
|
|
+ this.isConnected = false; // 当前连接状态
|
|
|
+ this.reconnectCount = 0; // 当前已重连次数
|
|
|
+ this.maxReconnect = options.maxReconnect || 5; // 最大重连次数,默认5次
|
|
|
+ this.heartbeatInterval = options.heartbeatInterval || 30000; // 心跳包发送间隔,默认30秒
|
|
|
+ this.heartbeatTimer = null; // 心跳定时器
|
|
|
+ this.eventListeners = {
|
|
|
+ open: [], // 连接打开事件监听器
|
|
|
+ message: [], // 消息接收事件监听器
|
|
|
+ error: [], // 错误事件监听器
|
|
|
+ close: [] // 关闭事件监听器
|
|
|
+ };
|
|
|
+ this.manualClose = false; // 是否为手动关闭连接
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 建立WebSocket连接
|
|
|
+ * 若已连接则不重复连接
|
|
|
+ */
|
|
|
+ connect() {
|
|
|
+ if (this.isConnected) return;
|
|
|
+ this.manualClose = false;
|
|
|
+ this.socketTask = uni.connectSocket({
|
|
|
+ url: this.url,
|
|
|
+ ...this.options
|
|
|
+ });
|
|
|
+ this._initEvent(); // 初始化事件监听
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化WebSocket事件监听(open、message、error、close)
|
|
|
+ * 绑定到socketTask对象
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ _initEvent() {
|
|
|
+ // 连接成功事件
|
|
|
+ this.socketTask.onOpen = (res) => {
|
|
|
+ this.isConnected = true;
|
|
|
+ this.reconnectCount = 0;
|
|
|
+ this._emit('open', res);
|
|
|
+ this._startHeartbeat(); // 启动心跳
|
|
|
+ };
|
|
|
+ // 接收到消息事件
|
|
|
+ this.socketTask.onMessage = (res) => {
|
|
|
+ this._emit('message', res);
|
|
|
+ this._startHeartbeat(); // 收到消息后重置心跳
|
|
|
+ };
|
|
|
+ // 连接错误事件
|
|
|
+ this.socketTask.onError = (err) => {
|
|
|
+ this._emit('error', err);
|
|
|
+ this._reconnect(); // 尝试重连
|
|
|
+ };
|
|
|
+ // 连接关闭事件
|
|
|
+ this.socketTask.onClose = (res) => {
|
|
|
+ this.isConnected = false;
|
|
|
+ this._emit('close', res);
|
|
|
+ this._stopHeartbeat(); // 停止心跳
|
|
|
+ if (!this.manualClose) {
|
|
|
+ this._reconnect(); // 非手动关闭时自动重连
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送消息到WebSocket服务器
|
|
|
+ * @param {string|object} data - 要发送的数据,支持字符串或对象
|
|
|
+ * @returns {Promise} - 发送成功/失败的Promise
|
|
|
+ */
|
|
|
+ send(data) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!this.isConnected) {
|
|
|
+ reject('WebSocket 未连接');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.socketTask.send({
|
|
|
+ data: typeof data === 'string' ? data : JSON.stringify(data),
|
|
|
+ success: resolve,
|
|
|
+ fail: reject
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 主动关闭WebSocket连接
|
|
|
+ * @param {number} code - 关闭码,默认1000
|
|
|
+ * @param {string} reason - 关闭原因
|
|
|
+ */
|
|
|
+ close(code = 1000, reason = '') {
|
|
|
+ this.manualClose = true;
|
|
|
+ if (this.socketTask) {
|
|
|
+ this.socketTask.close({ code, reason });
|
|
|
+ }
|
|
|
+ this._stopHeartbeat();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注册事件监听器
|
|
|
+ * @param {string} event - 事件名(open/message/error/close)
|
|
|
+ * @param {function} callback - 回调函数
|
|
|
+ */
|
|
|
+ on(event, callback) {
|
|
|
+ if (this.eventListeners[event]) {
|
|
|
+ this.eventListeners[event].push(callback);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 移除事件监听器
|
|
|
+ * @param {string} event - 事件名
|
|
|
+ * @param {function} callback - 需移除的回调函数
|
|
|
+ */
|
|
|
+ off(event, callback) {
|
|
|
+ if (this.eventListeners[event]) {
|
|
|
+ this.eventListeners[event] = this.eventListeners[event].filter(fn => fn !== callback);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 触发事件,调用所有注册的监听器
|
|
|
+ * @param {string} event - 事件名
|
|
|
+ * @param {any} data - 事件数据
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ _emit(event, data) {
|
|
|
+ if (this.eventListeners[event]) {
|
|
|
+ this.eventListeners[event].forEach(fn => fn(data));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动重连逻辑,递增重连次数,超出最大次数则停止
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ _reconnect() {
|
|
|
+ if (this.reconnectCount < this.maxReconnect) {
|
|
|
+ this.reconnectCount++;
|
|
|
+ setTimeout(() => {
|
|
|
+ this.connect();
|
|
|
+ }, 1000 * this.reconnectCount); // 重连间隔递增
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动心跳机制,定时发送ping包保持连接活跃
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ _startHeartbeat() {
|
|
|
+ this._stopHeartbeat();
|
|
|
+ if (this.heartbeatInterval > 0) {
|
|
|
+ this.heartbeatTimer = setInterval(() => {
|
|
|
+ if (this.isConnected) {
|
|
|
+ this.send('ping').catch(() => {});
|
|
|
+ }
|
|
|
+ }, this.heartbeatInterval);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停止心跳机制
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ _stopHeartbeat() {
|
|
|
+ if (this.heartbeatTimer) {
|
|
|
+ clearInterval(this.heartbeatTimer);
|
|
|
+ this.heartbeatTimer = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default UniWebSocket;
|