uniapp封装websocket

news2025/1/22 9:27:10

WebSocket介绍

后端使用的是springboot+netty做websocket的服务端,参考我其他博文

项目使用场景

        开发uniapp项目时,需要进行实时通信,比如聊天等。需要封装一个工具类,统一对socket进行管理。

uniapp websocket官方文档https://uniapp.dcloud.net.cn/api/request/websocket.html


代码解释

  • 采用单例模式,业务直接引入。
  • 主要是通过事件触发的形式,即收到特定事件,触发对应触发器,前端引入后,只需要添加相对应事件监听器即可。

注意:在收到数据时,已经默认使用JSON去解析了一次

代码

        主要通过消息通信进行操作,这里定义了一个消息实体模板socketMessageTemplate 

功能不齐全,大家按需使用,欢迎补充和讨论

// websocket.js
// 基础消息模板
const socketMessageTemplate = {
    type: "", // 消息类型
    timestamp: null, // 时间戳
    data: null, // 数据
    from: "", // 发送者
    to: "", // 接收者
    msg: "", // 消息内容
};

// WebSocket 管理类
class WebSocketManager {
    constructor({
        // 最大重连次数
        maxReconnectAttempts = 3,
        // 重连间隔(毫秒)
        reconnectInterval = 5000,
        // 心跳间隔(毫秒)
        heartbeatInterval = 60000,
    } = {}) {
        this.socket = null; // WebSocket 实例
        this.listeners = {}; // 事件监听器
        this.status = "closed"; // 连接状态:closed, connecting, connected
        this.heartbeatTimer = null; // 心跳定时器
        this.reconnectAttempts = 0; // 当前重连次数
        this.maxReconnectAttempts = maxReconnectAttempts; // 最大重连次数
        this.reconnectInterval = reconnectInterval; // 重连间隔(毫秒)
        this.heartbeatInterval = heartbeatInterval; // 心跳间隔(毫秒)
        this.url = null; // WebSocket 地址
        this.isManualClose = false; // 是否为用户主动关闭
    }

    /**
     * 连接 WebSocket
     * @param {string} url WebSocket 地址
     * @param {function} successCallback 连接成功回调函数
     */
    connect(url, successCallback = () => { }) {
        if (!url) {
            console.error("WebSocket 地址不能为空");
            return;
        }
        /* if (this.status === "connecting") {
            console.warn("WebSocket 正在连接中,请稍后再试");
            return;
        } */
        if (this.status === "connected") {
            console.warn("WebSocket 已连接,请勿重复连接");
            return;
        }

        this.url = url;
        this.status = "connecting";
        this.isManualClose = false; // 重置手动关闭标志
        this.socket = uni.connectSocket({
            url,
            success: () => {
                successCallback();
                console.log("WebSocket 连接请求已发送");
            },
            fail: (error) => {
                console.error("WebSocket 连接失败", error);
                this.status = "closed";
                this.handleReconnect();
            },
        });
        this.initEventHandlers();
    }

    /**
     * 初始化 WebSocket 事件
     */
    initEventHandlers() {
        uni.onSocketOpen(() => {
            if (this.status === "connected") return; // 避免重复发送初始化消息
            console.log("WebSocket 连接成功");
            this.status = "connected";
            this.reconnectAttempts = 0;
            this.startHeartbeat();
            this.triggerEvent("open");
        });

        uni.onSocketMessage(({ data }) => {
            try {
                const message = JSON.parse(decodeURIComponent(data));
                this.triggerEvent("message", message);
            } catch (err) {
                console.error("消息解析失败", err);
            }
        });

        uni.onSocketError((err) => {
            console.error("WebSocket 错误", err);
            this.triggerEvent("error", err);
        });

        uni.onSocketClose(() => {
            console.log("WebSocket 已关闭");
            this.status = "closed";
            this.stopHeartbeat();
            this.triggerEvent("close");
            if (!this.isManualClose) {
                this.handleReconnect();
            }
        });
    }

    /**
     * 处理重连逻辑
     */
    handleReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            console.log(`尝试第 ${this.reconnectAttempts} 次重连...`);
            this.status = "connecting";
            setTimeout(() => this.connect(this.url), this.reconnectInterval);
        } else {
            this.status = "fail";
            console.error("重连次数已达上限,停止重连");
        }
    }

    /**
     * 启动心跳机制
     */
    startHeartbeat() {
        this.stopHeartbeat(); // 避免重复启动
        this.heartbeatTimer = setInterval(() => {
            if (this.status === "connected") {
                this.sendMessage({ type: "HEARTBEAT" });
                // console.log('心跳检测中...');
            }
        }, this.heartbeatInterval);
    }

    /**
     * 停止心跳机制
     */
    stopHeartbeat() {
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
    }

    /**
     * 添加事件监听器
     * @param {string} event 事件类型
     * @param {function} callback 回调函数
     */
    addListener(event, callback) {
        if (!this.listeners[event]) {
            this.listeners[event] = [];
        }
        this.listeners[event].push(callback);
    }

    /**
     * 移除事件监听器
     * @param {string} event 事件类型
     * @param {function} callback 回调函数
     */
    removeListener(event, callback) {
        if (this.listeners[event]) {
            this.listeners[event] = this.listeners[event].filter(
                (cb) => cb !== callback
            );
        }
    }

    /**
     * 移除所有事件监听器
     */
    removeAllListeners() {
        this.listeners = {}; // 清空所有事件监听器
    }

    /**
     * 触发事件
     * @param {string} event 事件类型
     * @param {any} data 回调数据
     */
    triggerEvent(event, data = null) {
        (this.listeners[event] || []).forEach((callback) => callback(data));
    }

    /**
     * 发送消息 异步
     * @param {object} message 消息内容
     */
    async sendMessageWithRetry(message, retryCount = 3, delayMs = 1000) {
        for (let attempt = 0; attempt < retryCount; attempt++) {
            if (this.status === "connected") {
                // 处理type类型全部改为大写
                message.type = message.type.toUpperCase();
                const payload = JSON.stringify({
                    ...socketMessageTemplate,
                    ...message,
                    timestamp: Date.now(),
                });

                uni.sendSocketMessage({
                    data: payload,
                    success: () => {
                        console.log("消息发送成功", payload)
                    },
                    fail: (err) => console.error("消息发送失败", err),
                });
                return;
            }
            console.warn(`WebSocket 未连接,重试中 (${attempt + 1}/${retryCount})...`);
            await new Promise((resolve) => setTimeout(resolve, delayMs));
        }
        console.error("多次尝试发送消息失败,WebSocket 未连接");
    }
    /**
     * 发送消息
     * @param {object} message 消息内容
     */
    sendMessage(message) {
        if (this.status === "connecting") {
            console.error("WebSocket 正在连接中,无法发送消息");
            return;
        }
        if (this.status === "fail") {
            console.error("WebSocket 连接已失败,无法发送消息");
            return;
        }
        if (this.status !== "connected") {
            console.error("WebSocket 未连接,无法发送消息");
            this.handleReconnect(); // 如果未连接,尝试重连
            return;
        }

        message.type = message.type.toUpperCase();
        const payload = JSON.stringify({
            ...socketMessageTemplate,
            ...message,
            timestamp: Date.now(),
        });

        uni.sendSocketMessage({
            data: payload,
            success: () => {
                // console.log("消息发送成功", payload)
            },
            fail: (err) => {
                console.error("消息发送失败", err);
            },
        });
    }

    /**
     * 关闭 WebSocket 连接
     */
    close() {
        if (this.status === "closed") {
            console.warn("WebSocket 未连接,无需关闭");
            return;
        }
        if (this.status === "fail" || this.status === "connecting") {
            console.warn("WebSocket 当前状态无法正常关闭,重置状态");
            this.status = "closed"; // 强制重置状态
            this.isManualClose = true;
            return;
        }
        this.isManualClose = true; // 设置为手动关闭
        uni.closeSocket({
            success: () => {
                console.log("WebSocket 连接已关闭");
                this.status = "closed";
                this.stopHeartbeat();
                this.removeAllListeners(); // 关闭时移除所有事件监听器
            },
            fail: (err) => console.error("关闭 WebSocket 失败", err),
        });
    }
}

// 单例模式
const WebSocketInstance = new WebSocketManager();
export default WebSocketInstance;

举例

这里用uniapp 举例

<template>
    <view class="container">
        <view>
            <text>WebSocket 通信示例</text>
        </view>
        <view>
            <button @click="connectWebSocket">连接 WebSocket</button>
            <button @click="sendMessage">发送消息</button>
            <button @click="closeWebSocket">关闭 WebSocket</button>
        </view>
        <view>
            <text>接收到的消息:</text>
            <text>{
  
  { receivedMessage }}</text>
        </view>
    </view>
</template>

<script setup>
import { ref } from "vue";
import WebSocketInstance from "@/utils/websocket"; // 引入封装的 WebSocket 管理类
import { useWebSocket } from '@/utils/common';

const { url } = useWebSocket(12345);

// 接收到的消息
const receivedMessage = ref("");

// 连接 WebSocket
const connectWebSocket = () => {
    WebSocketInstance.connect(url);

    // 添加事件监听
    WebSocketInstance.addListener("open", () => {
        // console.log("WebSocket 已连接");
    });

    WebSocketInstance.addListener("message", (data) => {
        // console.log("接收到消息:", data);
        receivedMessage.value = JSON.stringify(data);
    });

    WebSocketInstance.addListener("close", () => {
        // console.log("WebSocket 已关闭");
    });

    WebSocketInstance.addListener("error", (error) => {
        console.error("WebSocket 错误", error);
    });
};

// 发送消息
const sendMessage = () => {
    WebSocketInstance.sendMessageWithRetry({
        type: "CHAT",
        from: "user1",
        to: "SYSTEM",
        msg: "你好,这是一条测试消息!",
    });
};

// 关闭 WebSocket
const closeWebSocket = () => {
    WebSocketInstance.close();
};
</script>

<style>
.container {
    padding: 20px;
}

button {
    margin: 10px 0;
}
</style>

WebSocket介绍

WebSocket是一种在Web浏览器和Web服务器之间进行实时双向通信的协议。与HTTP协议不同的是,WebSocket在建立连接后,不需要通过发起HTTP请求来获取数据,而是可以直接在连接上发送和接收数据。

WebSocket的特点包括以下几个方面:

  1. 实时性:WebSocket可以在客户端和服务器之间实时地发送消息,而不需要等待服务器响应或轮询。
  2. 双向通信:WebSocket支持客户端和服务器之间的双向通信,可以在任一方向上发送或接收消息。
  3. 高效性:WebSocket使用TCP协议作为传输层协议,相较于HTTP协议,可以减少通信的开销和延迟。
  4. 跨域支持:WebSocket通过在HTTP握手过程中发送特定的头部信息来支持跨域通信。
  5. 推送功能:WebSocket支持服务器主动向客户端推送消息,不再需要客户端主动发送请求。

WebSocket的工作原理是通过建立一个持久化的连接,使用HTTP协议完成初始的握手过程,然后通过升级协议从HTTP协议切换到WebSocket协议。一旦建立连接,客户端和服务器可以通过发送消息来进行通信,而不需要再次发送HTTP请求。

WebSocket在实时聊天、实时数据更新、实时游戏等场景中具有广泛应用。由于其高效的双向通信特性,WebSocket也在移动应用和物联网等领域得到了广泛的应用。

 

后端Websocket配合使用参考我博客其他文章,好久没写博文了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2280279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

渗透测试--攻击常见的Web应用

本文章咱主要讨论&#xff0c;常见Web应用的攻击手法&#xff0c;其中并不完全&#xff0c;因为Web应用是在太多无法囊括全部&#xff0c;但其中的手法思想却值得我们借鉴&#xff0c;所以俺在此做了记录&#xff0c;希望对大家有帮助&#xff01;主要有以下内容&#xff1a; 1…

Spring Boot自动配置原理:如何实现零配置启动

引言 在现代软件开发中&#xff0c;Spring 框架已经成为 Java 开发领域不可或缺的一部分。而 Spring Boot 的出现&#xff0c;更是为 Spring 应用的开发带来了革命性的变化。Spring Boot 的核心优势之一就是它的“自动配置”能力&#xff0c;它极大地简化了 Spring 应用的配置…

PHP同城配送小程序

&#x1f680; 同城极速达——您生活中的极速配送大师 &#x1f4f1; 一款专为现代都市快节奏生活量身打造的同城配送小程序&#xff0c;同城极速达&#xff0c;集高效、便捷、智能于一身&#xff0c;依托ThinkPHPGatewayWorkerUniapp的强大架构&#xff0c;巧妙融合用户端、骑…

Kotlin Bytedeco OpenCV 图像图像57 图像ROI

Kotlin Bytedeco OpenCV 图像图像57 图像ROI 1 添加依赖2 测试代码3 测试结果 1 添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns"http://maven.apache.o…

RabbitMQ集群安装rabbitmq_delayed_message_exchange

1、单节点安装rabbitmq安装延迟队列 安装延迟队列rabbitmq_delayed_message_exchange可以参考这个文章&#xff1a; rabbitmq安装延迟队列-CSDN博客 2、集群安装rabbitmq_delayed_message_exchange 在第二个节点 join_cluster 之后&#xff0c;start_app 就会报错了 (CaseC…

蓝桥与力扣刷题(73 矩阵置零)

题目&#xff1a;给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&…

华为E9000刀箱服务器监控指标解读

美信监控易内置了数千种常见设备监测器&#xff0c;能够监测超过20万项指标。这些指标涵盖了从硬件设备到软件系统&#xff0c;从网络性能到安全状态等各个方面。如下基于美信监控易——IT基础监控模块&#xff0c;对华为E9000刀箱服务器部分监控指标进行解读。 一、华为E9000…

【0x04】HCI_Connection_Request事件详解

目录 一、事件概述 二、事件格式及参数 2.1. HCI_Connection_Request 事件格式 2.2. BD_ADDR 2.3. Class_Of_Device 2.4. Link_Type 三、主机响应 3.1. ACL链接类型 3.2. SCO或eSCO链接类型 四、应用场景 4.1. 设备配对场景 4.2. 蓝牙文件传输场景 4.3. 蓝牙物联网…

PIC单片机设置bootloader程序和app程序地址方法

在调试bootloader和app程序的时候通常都需要设置程序的偏移地址&#xff0c;下面就总结一下使用MPLAB X IDE 设置程序地址的方法。 打开bootloader工程 工程上单击鼠标右键&#xff0c;选择Properties,打工工程属性窗口。 此时会打开项目属性对话框 左边类别选择XC8 Line…

10_异步加载场景前打开加载窗口

首先要在资源加载场景时 先加载LoadingWnd 加载窗口 逻辑上是 1.先加载 加载窗口LoadingWnd 2.在加载场景的同时 显示加载进度 3.最后在加载完成时关闭 加载窗口LoadingWnd 4.打开 登录窗口LoginWnd 如果想控制 窗口类Wnd.cs 需要创建Wnd.cs脚本 创建一个文件夹UIWindow用来…

博客之星2024年度-技术总结:技术探险家小板的一年的征程

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 技术探险家的新一年征程 2.0 数据库管理与优化&#xff1a;MySQL 的魔法森林 2.1 穿越基础概念的迷雾 2.2 实践应用&#xff1a;成为森林的主人 2.3 性能调优&…

用于牙科的多任务视频增强

Multi-task Video Enhancement for Dental Interventions 2022 miccai Abstract 微型照相机牢牢地固定在牙科手机上&#xff0c;这样牙医就可以持续地监测保守牙科手术的进展情况。但视频辅助牙科干预中的视频增强减轻了低光、噪音、模糊和相机握手等降低视觉舒适度的问题。…

Linux应用编程(五)USB应用开发-libusb库

一、基础知识 1. USB接口是什么&#xff1f; USB接口&#xff08;Universal Serial Bus&#xff09;是一种通用串行总线&#xff0c;广泛使用的接口标准&#xff0c;主要用于连接计算机与外围设备&#xff08;如键盘、鼠标、打印机、存储设备等&#xff09;之间的数据传输和电…

学习golang语言时遇到的难点语法

作者是java选手&#xff0c;实习需要转go&#xff0c;记录学习go中遇到的一些与java不同的语法。 defer defer特性 1. 关键字 defer 用于注册延迟调用。 2. 这些调用直到 return 前才被执。因此&#xff0c;可以用来做资源清理。 3. 多个defer语句&#xff0c;按先进…

cocosCreator动态调整pageView下面的标记indicator

pageView是我们在开发过程中经常使用到的一个组件&#xff0c;但是之前很少去动态修改过该属性的indicator,一般都是使用的默认的。今天产品要求实现一个动态效果&#xff0c;就是当页面左滑或者右滑时&#xff0c;下面的标记也会有一个左右滑动的效果(不知道怎么描述合适&…

C语言进阶习题【1】指针和数组(4)——指针笔试题4

笔试题7&#xff1a;下面代码输出是是什么&#xff1f; #include <stdio.h> int main() {char *a[] {"work","at","alibaba"};char**pa a;pa;printf("%s\n", *pa);return 0; }分析 代码结果 笔试题8&#xff1a;下面代码输…

服务化架构 IM 系统之应用 MQ

在微服务化系统中&#xff0c;存在三个最核心的组件&#xff0c;分别是 RPC、注册中心和MQ。 在前面的两篇文章&#xff08;见《服务化架构 IM 系统之应用 RPC》和《服务化架构 IM 系统之应用注册中心》&#xff09;中&#xff0c;我们站在应用的视角分析了普适性的 RPC 和 注…

【Rabbitmq】Rabbitmq高级特性-发送者可靠性

Rabbitmq发送者可靠性 发送者重连发送者确认1.开启确认机制2.ReturnCallback3.ConfirmCallback MQ的可靠性数据持久化交换机持久化队列持久化消息持久化 Lazy Queue 总结其他文章 Rabbitmq提供了两种发送来保证发送者的可靠性&#xff0c;第一种叫发送者重连&#xff0c;第二种…

【技术总结类】2024,一场关于海量数据治理以及合理建模的系列写作

目录 1.今年的创作路线 2.先说第一条线 2.1.由日志引出的海量文本数据存储和分析问题 2.2.监控以及监控的可视化 2.3.数据量级再往上走牵扯出了大数据 2.4.由大数据牵扯出的JAVA线程高级内容 3.第二条线&#xff0c;也是2025要继续的主线 1.今年的创作路线 今年的写作内…

【深度学习项目】语义分割-DeepLab网络(DeepLabV3介绍、基于Pytorch实现DeepLabV3网络)

文章目录 介绍深度学习语义分割的关键特点主要架构和技术数据集和评价指标总结 DeepLabDeepLab 的核心技术DeepLab 的发展历史DeepLab V3网络结构获取多尺度信息架构Cascade ModelASPP ModelMulti-GridPytorch官方实现的DeepLab V3该项目主要是来自pytorch官方torchvision模块中…