WebSocket实现消息实时推送

news2024/11/8 4:31:45

文章目录

    • websocket介绍
      • 特点
      • 工作原理
    • 用websocket实现实时推送
      • 引入依赖
      • WebSocket 函数定义
      • 变量声明
      • 初始化 WebSocket 连接
      • WebSocket 连接的初始化和事件处理
      • 连接打开事件
      • 接收消息处理
      • 连接关闭和重连机制
      • 心跳机制
      • 使用 WebSocket
      • 代码完整显示

websocket介绍

WebSocket 是一种网络通信协议,旨在实现客户端和服务器之间的双向通信。它允许在单个 TCP 连接上进行全双工(即同时进行发送和接收)通信。WebSocket 特别适用于需要实时更新的应用,比如在线游戏、聊天应用、实时数据流等。

特点

双向通信:与传统的 HTTP 请求-响应模型不同,WebSocket 允许客户端和服务器同时发送和接收数据,这意味着一方可以主动向另一方发送消息,而不需要等待请求。

持久连接:WebSocket 建立的连接是持久的,客户端和服务器在一次连接后可以持续交换消息,而不需要频繁建立和关闭连接,从而减少了网络延迟和开销。

低延迟:由于使用了持久连接,WebSocket 可以减少消息传输中的延迟,使实时应用的响应速度更快。

轻量级:WebSocket 协议在数据传输中相对轻量,不需要像 HTTP 那样包含大量的头信息,数据包开销更小。

工作原理

握手:首先,客户端通过发送 HTTP 请求向服务器发起 WebSocket 连接。请求中包含一些特定的头信息,表明希望升级到 WebSocket 协议。

建立连接:服务器收到请求后,如果支持 WebSocket,将返回一个响应,确认升级连接。此时,HTTP 连接转变为 WebSocket 连接。

数据传输:连接建立后,双方可以自由地发送和接收消息。消息格式可以是文本(如 JSON)或二进制数据。

关闭连接:一方可以发送关闭帧,另一方收到后也会关闭连接,整个通信过程结束。

用websocket实现实时推送

这个封装提供了一个基础的 WebSocket 功能,可以支持实时消息推送。通过增加事件监听的管理、错误处理、灵活的心跳机制等功能,可以使这个封装更加健壮和灵活。

引入依赖

import { ElMessage, getCache } from "@/utils";

ElMessage: 用于显示消息提示。
getCache: 用于获取缓存数据,这里用来获取用户ID。

WebSocket 函数定义

function webSocket(params: string) {
  ...
  return {
    onMessageFns,
    init,
  };
}

webSocket 函数接收一个参数 params(通常是用户ID),用于建立特定的 WebSocket 连接。

变量声明

  let urlParams: string = params;
  const isReconnect = ref(true);
  let reconnection: any;
  let ws: WebSocket | null = null;
  let websocketConnectedCount = 0;
  let serverTimeoutObj: any = null;
  const hearbeatInterval = 300000; // 心跳间隔

is_reconnect: 用于标识是否可以重连。
ws: 存储 WebSocket 实例。
websocketConnectdCount : 记录连接失败次数。
serverTimeoutObj : 用于管理心跳检测的定时器。

初始化 WebSocket 连接

const init = () => {
  if (!("WebSocket" in window)) {
    ElMessage({
      message: "抱歉,浏览器不支持Websocket!",
      type: "warning",
      duration: 1000,
    });
    return;
  }
  try {
    initWebSocket();
  } catch (e) {
    console.log("尝试创建连接失败");
    reConnect();
  }
};

首先检查浏览器是否支持 WebSocket。如果支持,则尝试初始化连接;如果失败,则调用 reConnect 进行重连。

WebSocket 连接的初始化和事件处理

 function initWebSocket() {
    const baseUrl = import.meta.env.VITE_BaseUrl?.slice(7);
    const url = `ws://${baseUrl}/websocket/${urlParams}`;
    ws = new WebSocket(url);

    ws.onopen = (e: Event) => {
      websocketOpen(e);
    };

    ws.onmessage = (e: MessageEvent) => {
      websocketOnMessage(e);
    };

    ws.onerror = () => {
      console.log("WebSocket连接发生错误");
      isReconnect.value = false;
      websocketConnectedCount++;
      if (websocketConnectedCount <= 5) {
        reConnect();
      }
    };

    ws.onclose = (e: CloseEvent) => {
      websocketClose(e);
    };
  }

创建 WebSocket 实例,并设置各类事件处理函数(打开、接收消息、错误、关闭)。

连接打开事件

function websocketOpen(e: Event) {
    console.log("连接成功");
    reset();
    start();
    const data = { sendType: "HEALTH" };
    ws?.send(JSON.stringify(data));
  }
}

连接成功时,重置心跳并发送健康检查消息。

接收消息处理

const onMessageFns = new Set<(e: MessageEvent) => void>();
  function websocketOnMessage(e: MessageEvent) {
    onMessageFns.forEach((callback) => callback(e));
    reset();
    start();
    return e.data;
  }

接收到消息时,执行所有注册的回调函数,并重置心跳。

连接关闭和重连机制

function websocketclose(e: any) {
  console.log(e);
  is_reconnect.value = false;
  console.log("connection closed (" + e.code + ")");
}

let reConnect = () => {
  console.log("尝试重新连接");
  if (is_reconnect) return; // 如果已经连上就不再重连
  reconnection && clearTimeout(reconnection);
  reconnection = setTimeout(function () {
    init();
  }, 5000);
};

连接关闭时设置标识,并在指定时间后尝试重连。

心跳机制

 const reset = () => {
    clearTimeout(serverTimeoutObj);
  };

  const start = () => {
    serverTimeoutObj = setInterval(() => {
      if (ws?.readyState === WebSocket.OPEN) {
        console.log("连接状态,发送消息保持连接");
        const data = { sendType: "HEALTH" };
        ws?.send(JSON.stringify(data));
        reset();
      } else {
        console.log("断开连接, 尝试重连");
        webSocket(urlParams);
      }
    }, hearbeatInterval);
  };

定期发送健康检查消息,保持连接活跃。

使用 WebSocket

import webSocket from "@/utils/websocket";

const userId = getCache("userId");
const ws = webSocket(userId)!;

onMounted(async () => {
  const addMessage = () => {
    ws.onMessageFns.add((value: MessageEvent) => {
      try {
        if (value.data !== "来自后台的反馈:连接成功" && value.data !== "SUCCESS") {
          const parsedData = JSON.parse(value.data);
          console.log("获取到的信息", parsedData);
        }
      } catch (error) {
        console.error("消息解析错误:", error);
      }
    });
  };
  addMessage();
});

在组件挂载时,获取用户ID并创建 WebSocket 实例。添加消息接收处理,过滤特定消息并处理 JSON 数据。

代码完整显示

import { ElMessage, getCache } from "@/utils";

function webSocket(params: string) {
  let urlParams: string = params;
  const isReconnect = ref(true);
  let reconnection: any;
  let ws: WebSocket | null = null;
  let websocketConnectedCount = 0;
  let serverTimeoutObj: any = null;
  const hearbeatInterval = 300000; // 心跳间隔

  const init = () => {
    if (!("WebSocket" in window)) {
      ElMessage({
        message: "抱歉,浏览器不支持WebSocket!",
        type: "warning",
        duration: 1000,
      });
      return;
    }
    try {
      initWebSocket();
    } catch (e) {
      console.log("尝试创建连接失败");
      reConnect();
    }
  };

  function initWebSocket() {
    const baseUrl = import.meta.env.VITE_BaseUrl?.slice(7);
    const url = `ws://${baseUrl}/websocket/${urlParams}`;
    ws = new WebSocket(url);

    ws.onopen = (e: Event) => {
      websocketOpen(e);
    };

    ws.onmessage = (e: MessageEvent) => {
      websocketOnMessage(e);
    };

    ws.onerror = () => {
      console.log("WebSocket连接发生错误");
      isReconnect.value = false;
      websocketConnectedCount++;
      if (websocketConnectedCount <= 5) {
        reConnect();
      }
    };

    ws.onclose = (e: CloseEvent) => {
      websocketClose(e);
    };
  }

  function websocketOpen(e: Event) {
    console.log("连接成功");
    reset();
    start();
    const data = { sendType: "HEALTH" };
    ws?.send(JSON.stringify(data));
  }

  const onMessageFns = new Set<(e: MessageEvent) => void>();
  function websocketOnMessage(e: MessageEvent) {
    onMessageFns.forEach((callback) => callback(e));
    reset();
    start();
    return e.data;
  }

  function websocketClose(e: CloseEvent) {
    console.log("connection closed (" + e.code + ")");
    isReconnect.value = false;
  }

  const reConnect = () => {
    console.log("尝试重新连接");
    if (isReconnect.value) return; // 如果已经连上就不再重连
    reconnection && clearTimeout(reconnection);
    reconnection = setTimeout(init, 5000);
  };

  const reset = () => {
    clearTimeout(serverTimeoutObj);
  };

  const start = () => {
    serverTimeoutObj = setInterval(() => {
      if (ws?.readyState === WebSocket.OPEN) {
        console.log("连接状态,发送消息保持连接");
        const data = { sendType: "HEALTH" };
        ws?.send(JSON.stringify(data));
        reset();
      } else {
        console.log("断开连接, 尝试重连");
        webSocket(urlParams);
      }
    }, hearbeatInterval);
  };

  return {
    onMessageFns,
    init,
  };
}

// 使用 WebSocket
import webSocket from "@/utils/websocket";

const userId = getCache("userId");
const ws = webSocket(userId)!;

onMounted(async () => {
  const addMessage = () => {
    ws.onMessageFns.add((value: MessageEvent) => {
      try {
        if (value.data !== "来自后台的反馈:连接成功" && value.data !== "SUCCESS") {
          const parsedData = JSON.parse(value.data);
          console.log("获取到的信息", parsedData);
        }
      } catch (error) {
        console.error("消息解析错误:", error);
      }
    });
  };
  addMessage();
});

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

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

相关文章

一文看懂芯片算力TOPS

AI算力的发展与TOPS&#xff08;Tera Operations Per Second&#xff09;紧密相关。TOPS是衡量芯片每秒能执行的基本操作次数的单位&#xff0c;通常用于评估AI芯片性能&#xff0c;特别是在处理大量整数或定点运算任务时。随着AI技术进步&#xff0c;对算力的需求不断增加&…

在Linux系统中高效查找文件所在位置的方法

目录 引言 一、使用find命令查找文件 二、使用locate命令快速查找文件 三、利用which和whereis命令查找可执行文件 四、使用findmnt和mount命令查找挂载点 五、其他查找文件的方法 六、总结与最佳实践 引言 Linux操作系统以其强大的功能和灵活性而闻名&#xff0c;其文件…

连锁餐饮企业-凡塔斯,用千里聆RPA搭建用户评价管理系统,提升门店服务满意度

凡塔斯是大型连锁餐饮企业昊澜餐饮集团旗下餐饮品牌&#xff0c;是牛排自助餐头部品牌&#xff0c;旗下拥有凡塔斯、百分好、食物链KING自助烤肉及餐饮人才商学院等多个行业知名品牌。 创立至今&#xff0c;集团管理门店已发展到福建、广东、江西、浙江等十多个省市&#xff0c…

MES管理系统的生产绩效分析与资源可追踪性

在探讨MES管理系统的核心功能时&#xff0c;生产绩效分析与资源可追踪性是两个不可或缺的关键要素。它们共同构成了MES管理系统中对于生产效率、成本控制以及产品质量进行精细管理的基石。以下是对这两个关键领域的深入剖析与重新阐述。 MES管理系统中的生产绩效分析&#xff0…

远程控制项目第四天 功能实现

发送屏幕内容 代码详解 1. 创建 CImage 对象并获取屏幕内容 首先&#xff0c;我们创建一个 CImage 对象&#xff0c;用于接收屏幕上的内容。要获取屏幕内容&#xff0c;我们需要先获取当前设备上下文&#xff08;DC&#xff09;。调用 ::GetDC(NULL) 函数&#xff0c;参数 NU…

无线领夹麦克风哪个品牌音质最好?无线麦克风品牌排行榜前十名

​无线领夹麦克风哪个品牌音质最好&#xff1f;选择无线麦克风时&#xff0c;音质和耐用性至关重要。近年来&#xff0c;无线麦克风产品越来越智能化和多样化&#xff0c;但也存在着大量的劣质产品。作为测评师&#xff0c;我发现这些低质量无线麦克风由于缺乏专业调校&#xf…

【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】

在 Kubernetes 中&#xff0c;NodePort 类型的 Service 允许用户通过每个节点的 IP 地址和指定的端口访问应用程序。如果 NodePort 类型的 Service 无法通过节点的 IP 地址和指定端口进行访问&#xff0c;可能会导致用户无法访问应用。本文将详细分析该问题的常见原因及其解决方…

并发编程(8)—— std::async、std::future 源码解析

文章目录 八、day81. std::async2. std::future2.1 wait()2.2 get() 八、day8 之前说过&#xff0c;std::async内部的处理逻辑和std::thread相似&#xff0c;而且std::async和std::future有密不可分的联系。今天&#xff0c;通过对std::async和std::future源码进行解析&#x…

在VSCode中读取Markdown文件

在VSCode安装Markdown All in One或Markdown Preview Enhanced即可 插件Markdown All in One GitHub&#xff1a;https://github.com/yzhang-gh/vscode-markdown v3.6.2下载链接&#xff1a;https://marketplace.visualstudio.com/_apis/public/gallery/publishers/yzhang/vs…

成都睿明智科技有限公司共赴抖音电商蓝海

在这个短视频风起云涌的时代&#xff0c;抖音作为现象级的社交媒体平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;更悄然间重塑了电商行业的格局。在这片充满机遇与挑战的蓝海中&#xff0c;成都睿明智科技有限公司凭借其敏锐的市场洞察力和专业的服务能力&#xff0c;…

MySQL_聚合函数分组查询

上篇复习&#xff1a; 设计数据库时的三大范式1.第一范式&#xff0c;一行数据中每一列不可再分 关系型数据库必须要满足第一范式&#xff0c;设计表的时候&#xff0c;如果每一列都可以用SQL规定的数据类型描述&#xff0c;就天然满足第一范式. 2.第二范式&#xff0c;在第一…

深入了解逻辑回归:机器学习中的经典算法

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

TESSY学习笔记—project view界面的架构

1&#xff1a;project view界面能添加的元素 project view界面能添加的元素&#xff08;暂且称为元素&#xff09;&#xff0c;打开project view界面&#xff0c;下图中红框勾选出来的就是 2&#xff1a;一共存在5种可添加元素 **1&#xff09;Test collection 测试集合&…

distrobox install in ubuntu 22.04 / 在 ubuntu 22.04 上安装 distrobox (***) OK

要点&#xff1a; 本测试实验&#xff0c;采用的是 podman distrobox 在沙盒 snap 中&#xff0c;安装 distrobox 需要使用 --devmode 开发模式&#xff1b;可以避开 distrobox 的版本检查&#xff1f; distrobox 官方文档显示&#xff0c; Installation https://distrobox.i…

跨域及解决跨域

什么是跨域 前端与后端不在同一个域名下&#xff1a; 解决 import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component;import java.io.IOException…

使用Jest进行JavaScript单元测试

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Jest进行JavaScript单元测试 引言 Jest 简介 安装 Jest 创建基本配置 编写测试用例 运行测试 快照测试 模拟函数 代码覆盖率…

【读书笔记/深入理解K8S】集群网络

前言 上一章讲了集群控制器的一个大概的原理&#xff0c;这一章讲一下集群网络。网络是集群通信的载体&#xff0c;因为该书是阿里云团队出品的&#xff0c;所以也以阿里云的集群网络方案为例&#xff0c;其他云厂商的网络集群方案一般来说也大同小异。所以通过本章的学习&…

Varjo核电厂虚拟仿真培训解决方案

虚拟现实技术的融入将帮助核电厂设计验证和操作员培训跟有效的进行。 芬兰的Loviisa工厂是世界上第一个为控制室操作员建造专用VR培训室的核电站。该工厂使用了Varjo混合现实头显设备为员工提供虚拟仿真训练。 案例Fortum: VR为核电厂操纵员培训带来明显优势 与构建物理模拟器相…

推荐一款基于Flash的交互式园林设计工具:Garden Planner

Garden Planner是一款由Artifact Interactive开发的基于Flash的交互式园林设计工具。它允许用户以拖放的方式安排植物、树木、建筑物和各种对象&#xff0c;使园林规划变得简单直观。此外&#xff0c;Garden Planner提供工具来快速创建铺路、路径和围栏&#xff0c;帮助用户设计…

HTML 标签属性——id、class、style 等全局属性详解

文章目录 1. id属性2. class属性3. style属性4. title属性5. lang属性6. dir属性7. accesskey属性8. tabindex属性小结HTML全局属性是一组可以应用于几乎所有HTML元素的特殊属性。这些属性提供了额外的功能和信息,使得网页开发者能够更好地控制元素的行为、样式和可访问性。 …