用了那么多年的WebSocket尽然发现断线重连后不会收到消息

实现一个健壮的WebSocket连接管理器

在现代Web应用中,实时通信变得越来越重要。WebSocket作为一种全双工通信协议,为前端应用提供了强大的实时数据交互能力。本文将介绍如何实现一个功能完善的WebSocket连接管理器,包含自动重连、心跳检测等关键特性。

WebSocket管理器的核心功能

实现的useWebSocket函数提供了以下核心功能:

  1. 自动连接:初始化时自动建立WebSocket连接
  2. 事件监听:通过事件机制处理消息、连接状态变化
  3. 心跳检测:定期发送心跳包保持连接活跃
  4. 自动重连:连接断开时自动尝试重新连接
  5. 手动控制:提供手动关闭连接的接口

实现解析

初始化与连接建立

js 复制代码
// 生成 digits 位随机数
const randomNumber = (digits = 8) =>{
    const min = Math.pow(10, digits - 1); // 10^(digits-1) 例如 8 位数最小是 10000000
    const max = Math.pow(10, digits) - 1; // 10^digits - 1 例如 8 位数最大是 99999999
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
export const useWebSocket = ({url = null, content, time = 10, heartbeat = 10, protocols = null}) => {
    const info = {
        // 存储各种状态和配置
        heartbeatInterval: null,
        time: time || 10,          // 重连间隔(秒)
        heartbeat: heartbeat || 10, // 心跳间隔(秒)
        TIMEID: null,              // 重连定时器ID
        socket: null,              // WebSocket实例
        protocols: protocols || null,
        url: url || null,
        content: content || 'ping',
        eventTarget: new EventTarget(), // 事件系统
        isManuallyClosed: false,  // 是否手动关闭
        isConnected: false         // 连接状态
    }
    
    const initWebSocket = () => {
        if (info.isManuallyClosed) return;
        
        info.socket?.close();
        info.socket = null;
        
        const protocol = location.protocol === 'https' ? 'wss' : 'ws';
        // 根据实际需求可以在后面拼接随机数
        const adder = info.url || import.meta.env.VITE_SOKET_URL;
        info.socket = info.protocols 
            ? new WebSocket(`${protocol}://${adder}`, info.protocols) 
            : new WebSocket(`${protocol}://${adder}`);
            
        setupEventHandlers();
    }
}

事件处理

js 复制代码
const setupEventHandlers = () => {
    // 连接成功
    info.socket.onopen = () => {
        info.eventTarget.dispatchEvent(new CustomEvent('open', {detail: "onopen"}));
        info.isConnected = true;
        send(info.content);
        startHeartbeat();
    }
    
    // 接收消息
    info.socket.onmessage = (evt) => {
        info.eventTarget.dispatchEvent(new CustomEvent('message', {detail: evt.data}));
    }
    
    // 连接关闭
    info.socket.onclose = (e) => {
        info.isConnected = false;
        info.eventTarget.dispatchEvent(new CustomEvent('close', {detail: e}));
        if (!info.isManuallyClosed) attemptReconnect();
    }
    
    // 连接错误
    info.socket.onerror = (e) => {
        info.isConnected = false;
        info.eventTarget.dispatchEvent(new CustomEvent('error', {detail: e}));
        attemptReconnect();
    }
}

心跳机制

js 复制代码
// 开始心跳检测
const startHeartbeat = () => {
    stopHeartbeat();
    info.heartbeatInterval = setInterval(
        () => send(info.content), 
        info.heartbeat * 1000
    );
}

// 停止心跳检测
const stopHeartbeat = () => {
    clearInterval(info.heartbeatInterval);
    info.heartbeatInterval = null;
};

自动重连机制

js 复制代码
// 尝试重新连接
const attemptReconnect = () => {
    stopHeartbeat();
    clearTimeout(info.TIMEID);
    info.TIMEID = setTimeout(initWebSocket, info.time * 1000);
}

公共API

js 复制代码
return {
    // 发送消息
    send: (msg) => info.socket?.send(msg),
    
    // 监听事件
    on: (event, handler) => info.eventTarget?.addEventListener(event, handler),
    
    // 关闭连接
    close: (code = 1000, msg = '正常手动关闭') => {
        info.isManuallyClosed = true;
        clearTimeout(info.TIMEID);
        stopHeartbeat();
        info.socket?.close(code, msg);
        info.socket = null;
        info.isConnected = false;
        removeEventListeners();
    }
}

使用示例

js 复制代码
// 创建WebSocket连接
const ws = useWebSocket({
    url: '192.168.3.21:9001/websoket/user',
    time: 5,       // 5秒重连间隔
    heartbeat: 15  // 15秒心跳间隔
});

// 监听消息
ws.on('message', (event) => {// 断线重连后收到消息依旧会触发
    console.log('收到消息:', event.detail);
});

// 监听连接打开
ws.on('open', () => {
    console.log('连接已建立');
});

// 监听连接关闭
ws.on('close', () => {
    console.log('连接已关闭');
});

// 发送消息
ws.send('Hello WebSocket!');

// 手动关闭连接
ws.close();
相关推荐
拾光拾趣录31 分钟前
一张 8K 海报差点把首屏拖垮
前端·性能优化
天涯学馆37 分钟前
为什么越来越多开发者偷偷用上了 Svelte?
前端·javascript·svelte
Silver〄line1 小时前
前端图像视频实时检测
前端·目标检测·canva可画
三月的一天1 小时前
React+threejs两种3D多场景渲染方案
前端·react.js·前端框架
拾光拾趣录1 小时前
为什么浏览器那条“假进度”救不了我们?
前端·javascript·浏览器
香菜狗1 小时前
vue3响应式数据(ref,reactive)详解
前端·javascript·vue.js
拾光拾趣录1 小时前
老板突然要看“代码当量 KPI”
前端·node.js
拾光拾趣录1 小时前
为什么我们要亲手“捏”一个 Vue 项目?
前端·vue.js·性能优化
油丶酸萝卜别吃2 小时前
SSE与Websocket有什么区别?
前端·javascript·网络·网络协议
27669582922 小时前
拼多多小程序 anti_content 分析
java·javascript·python·node·拼多多·anti-content·anti_content