用了那么多年的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();
相关推荐
Danny_FD19 分钟前
Vue2 + Node.js 快速实现带心跳检测与自动重连的 WebSocket 案例
前端
uhakadotcom20 分钟前
将next.js的分享到twitter.com之中时,如何更新分享卡片上的图片?
前端·javascript·面试
韦小勇21 分钟前
el-table 父子数据层级嵌套表格
前端
奔赴_向往23 分钟前
为什么 PWA 至今没能「掘进」主流?
前端
小小愿望23 分钟前
微信小程序开发实战:图片转 Base64 全解析
前端·微信小程序
掘金安东尼26 分钟前
2分钟创建一个“不依赖任何外部库”的粒子动画背景
前端·面试·canvas
电商API大数据接口开发Cris26 分钟前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
前端·数据挖掘·api
小小愿望27 分钟前
解锁前端新技能:让JavaScript与CSS变量共舞
前端·javascript·css
程序员鱼皮30 分钟前
爆肝2月,我的 AI 代码生成平台上线了!
java·前端·编程·软件开发·项目
天生我材必有用_吴用43 分钟前
一文搞懂 useDark:Vue 项目中实现深色模式的正确姿势
前端·vue.js