uniapp封装纯js版wesocket

46 阅读3分钟

1、配置文件

export default {
    // 服务器地址 自行配置
    serverUrl: `ws://127.0.0.1/socket`,
    // 备用服务器地址(如果主地址无法连接,可以尝试使用备用地址)
    backupServerUrl: '',
    // 连接超时时间(毫秒)
    connectTimeout: 10000,
    // 重连相关配置
    reconnect: {
        // 最大重连次数
        maxAttempts: 10,
        // 初始重连间隔
        initialDelay: 3000,
        // 是否指数退避(每次重连间隔增加)
        exponentialBackoff: true
    }
    // 心跳相关配置
    heartbeat: {
        // 客户端心跳间隔
        clientInterval: 30000,
        // 服务端心跳超时 - 调整为大于客户端心跳间隔
        serverTimeout: 40000
    }
}

2、wesocket.js文件

import wsConfig from './ws-config.js';
/**
 * WebSocket连接管理器
 * 用于处理与服务器的WebSocket通信,支持设备状态实时更新
 */
const cleanServerAddress = () => {
    try {
        let serverAddress = JSON.parse(uni.getStorageSync('serverAddress'))
        // 如果地址包含http://或https://前缀,则移除
        if (serverAddress.includes('http://')) {
            return serverAddress.replace('http://', '');
        } else if (serverAddress.includes('https://')) {
            return serverAddress.replace('https://', '');
        }
        return serverAddress;
    } catch (error) {
        console.error('获取服务器地址失败:', error);
        return '/api/v1'; // 或返回一个默认值
    }
};

// WebSocket实例
let ws = null;
// WebSocket连接状态
let wsState = 'CLOSED';
// 当前使用的服务器地址
let currentServerUrl = wsConfig.serverUrl;
// 重连计时器
let reconnectTimer = null;
// 重连次数
let reconnectCount = 0;
// 心跳定时器
let heartbeatTimer = null;
// 服务端心跳响应定时器
let serverHeartbeatTimer = null;
// 消息回调函数(只保留一个回调)
let messageCallback = null;

/**
 * 设置消息回调函数
 */
export const setMessageCallback = (callback) => {
    if (typeof callback === 'function') {
        messageCallback = callback;
    }
};

/**
 * 初始化WebSocket连接
 */
export const initWebSocket = () => {
    // 如果已经有连接,先关闭
    // if (ws) {
    //   closeWebSocket();
    // }

    // 创建WebSocket连接
    ws = uni.connectSocket({
        url: currentServerUrl,
        header: {
            'content-type': 'application/json'
        },
        protocols: ['protocol1'],
        complete: () => { }
    });

    // 监听连接打开
    ws.onOpen((res) => {
        wsState = 'OPEN';
        reconnectCount = 0;
        // 通知回调函数
        if (messageCallback) {
            try {
                messageCallback({ type: 'connected', data: res });
            } catch (error) {
                console.error('WebSocket连接打开回调错误:', error);
            }
        }
        // 开始发送心跳
        startClientHeartbeat();
    });

    // 监听连接关闭
    ws.onClose((res) => {
        wsState = 'CLOSED';
        // 通知回调函数
        if (messageCallback) {
            try {
                messageCallback({ type: 'disconnected', data: res });
            } catch (error) {
                console.error('WebSocket连接关闭回调错误:', error);
            }
        }
        // 停止心跳
        stopClientHeartbeat();
        stopServerHeartbeatCheck();
        // 尝试重连
        if (reconnectCount < wsConfig.reconnect.maxAttempts) {
            startReconnect();
        } else {
            // 通知重连失败
            if (messageCallback) {
                try {
                    messageCallback({
                        type: 'reconnect_failed',
                        data: { attempts: wsConfig.reconnect.maxAttempts }
                    });
                } catch (error) {
                    console.error('WebSocket重连失败回调错误:', error);
                }
            }
        }
    });

    // 监听连接错误
    ws.onError((err) => {
        console.error('WebSocket连接错误:', err);
        wsState = 'ERROR';
        // 通知回调函数
        if (messageCallback) {
            try {
                messageCallback({ type: 'error', data: err });
            } catch (error) {
                console.error('WebSocket错误回调错误:', error);
            }
        }
        // 错误发生后关闭连接,触发重连逻辑
        closeWebSocket();
    });

    // 监听消息接收
    ws.onMessage((res) => {
        try {
            // 解析消息
            const message = JSON.parse(res.data);
            // 如果是服务端心跳响应,重置服务端心跳定时器
            if (message.type === 'server_hb') {
                resetServerHeartbeatCheck();
            }
            // 如果是设备状态变更
            else if (message.type === 'device_status_change') {
                // 通知回调函数
                if (messageCallback) {
                    try {
                        messageCallback({ type: 'message', data: message });
                    } catch (error) {
                        console.error('WebSocket消息回调错误:', error);
                    }
                }
                // 对服务器消息进行ACK响应
                sendMessage({
                    type: 'ack',
                    data: {
                        messageId: message.messageId
                    }
                });
            }
            // 其他类型的消息
            else {
                if (messageCallback) {
                    try {
                        messageCallback({ type: 'message', data: message });
                    } catch (error) {
                        console.error('WebSocket消息回调错误:', error);
                    }
                }
            }
        } catch (error) {
            console.error('WebSocket消息解析错误:', error);
            if (messageCallback) {
                try {
                    messageCallback({
                        type: 'parse_error',
                        data: {
                            originalData: res.data,
                            error: error.message
                        }
                    });
                } catch (callbackError) {
                    console.error('WebSocket解析错误回调错误:', callbackError);
                }
            }
        }
    });
};

/**
 * 发送消息到服务器
 */
export const sendMessage = (message) => {
    if (!ws || wsState !== 'OPEN') {
        return false;
    }

    const data = typeof message === 'string' ? message : JSON.stringify(message);

    ws.send({
        data: data,
        success: (res) => { },
        fail: (err) => {
            console.error('WebSocket消息发送失败:', err);
            if (messageCallback) {
                try {
                    messageCallback({
                        type: 'send_error',
                        data: { message, error: err }
                    });
                } catch (error) {
                    console.error('WebSocket发送错误回调错误:', error);
                }
            }
        }
    });

    return true;
};

/**
 * 关闭WebSocket连接
 */
export const closeWebSocket = () => {
    if (ws) {
        ws.close({
            success: (res) => {
                console.log('WebSocket连接已成功关闭:', res);
            },
            fail: (err) => {
                console.error('WebSocket关闭失败:', err);
            }
        });
        ws = null;
        wsState = 'CLOSED';
    }
};

/**
 * 获取当前重连延迟时间
 */
const getReconnectDelay = () => {
    if (wsConfig.reconnect.exponentialBackoff) {
        // 指数退避,但不超过最大10秒
        return Math.min(wsConfig.reconnect.initialDelay * Math.pow(2, reconnectCount - 1), 10000);
    } else {
        return wsConfig.reconnect.initialDelay;
    }
};

/**
 * 开始重连
 */
const startReconnect = () => {
    reconnectCount++;
    const delay = getReconnectDelay();
    console.log(`WebSocket重连尝试 ${reconnectCount}/${wsConfig.reconnect.maxAttempts},延迟 ${delay}ms`);

    reconnectTimer = setTimeout(() => {
        initWebSocket();
    }, delay);
};
/**
 * 开始客户端心跳
 */
const startClientHeartbeat = () => {
    stopClientHeartbeat();

    heartbeatTimer = setInterval(() => {
        if (ws && wsState === 'OPEN') {
            sendMessage({
                type: 'client_hb',
                timestamp: Date.now()
            });
            // 同时启动服务端心跳检查
            startServerHeartbeatCheck();
        }
    }, wsConfig.heartbeat.clientInterval);
};

/**
 * 停止客户端心跳
 */
const stopClientHeartbeat = () => {
    if (heartbeatTimer) {
        clearInterval(heartbeatTimer);
        heartbeatTimer = null;
    }
};

/**
 * 开始服务端心跳检查
 */
const startServerHeartbeatCheck = () => {
    stopServerHeartbeatCheck();

    serverHeartbeatTimer = setTimeout(() => {
        console.error('WebSocket服务端心跳超时,断开连接');
        if (messageCallback) {
            try {
                messageCallback({
                    type: 'heartbeat_timeout',
                    data: { timeout: wsConfig.heartbeat.serverTimeout }
                });
            } catch (error) {
                console.error('WebSocket心跳超时回调错误:', error);
            }
        }
        closeWebSocket();
    }, wsConfig.heartbeat.serverTimeout);
};

/**
 * 重置服务端心跳检查
 */
const resetServerHeartbeatCheck = () => {
    startServerHeartbeatCheck();
};

/**
 * 停止服务端心跳检查
 */
const stopServerHeartbeatCheck = () => {
    if (serverHeartbeatTimer) {
        clearTimeout(serverHeartbeatTimer);
        serverHeartbeatTimer = null;
    }
};

/**
 * 获取WebSocket连接状态
 */
export const getWebSocketState = () => {
    return wsState;
};

// 导出默认对象
export default {
    initWebSocket,
    sendMessage,
    closeWebSocket,
    setMessageCallback,
    getWebSocketState
};

3、页面中使用

import wsManager from "@/api/websocket";
onLaunch(() => {
    // 初始化WebSocket连接
    wsManager.initWebSocket();
});

onShow(() => {
    const wsState = wsManager.getWebSocketState();
    // // 如果WebSocket已关闭,重新连接
    if (wsState === "CLOSED") {
        wsManager.initWebSocket();
    }
});

onLoad(() => {
    const wsState = wsManager.getWebSocketState();
    if (wsState === "CLOSED") {
        wsManager.initWebSocket();
    }
    // 设置WebSocket消息回调函数
    wsManager.setMessageCallback(handleWebSocketMessage);
});
//消息回调函数
const handleWebSocketMessage = ({ type, data }) => {
    // console.log(`收到WebSocket消息: ${type}`, data);

    switch (type) {
        case "connected":
            "WebSocket已连接";
            break;

        case "disconnected":
            "WebSocket连接断开";
            break;

        case "error":
            "WebSocket连接错误";
            break;

        case "message":

            "WebSocket已连接 收到消息: " + data.message;
            break;

        case "reconnect_failed":
            "WebSocket重连失败";
            break;

        case "heartbeat_timeout":
            "WebSocket心跳超时";
            break;

        default:
    }
};