【开源宝藏】Jeepay VUE和React构建WebSocket通用模板

news2025/1/19 4:13:12

WebSocket 服务实现:Spring Boot 示例

在现代应用程序中,WebSocket 是实现双向实时通信的重要技术。本文将介绍如何使用 Spring Boot 创建一个简单的 WebSocket 服务,并提供相关的代码示例。

1. WebSocket 简介

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。与传统的 HTTP 请求-响应模式相比,WebSocket 允许服务器主动向客户端推送消息,适用于实时应用,如在线聊天、实时通知和游戏等。

2. 项目结构

在开始之前,确保你的项目中包含必要的依赖。在 pom.xml 中添加以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>

3. WebSocket 配置类

为了启用 WebSocket 支持,我们需要创建一个配置类 WebSocketConfig,如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 开启WebSocket支持
 * 
 * @author terrfly
 * @site https://www.jeequan.com
 * @date 2021/6/22 12:57
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3.1 配置类解析

  • @Configuration: 表示该类是一个配置类,Spring 会在启动时加载它。
  • ServerEndpointExporter: 这个 Bean 会自动注册所有带有 @ServerEndpoint 注解的 WebSocket 端点。

4. WebSocket 服务类

以下是一个简单的 WebSocket 服务类 WsChannelUserIdServer 的实现。该类负责处理客户端的连接、消息接收和发送。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/api/anon/ws/channelUserId/{appId}/{cid}")
@Component
public class WsChannelUserIdServer {

    private final static Logger logger = LoggerFactory.getLogger(WsChannelUserIdServer.class);

    private static int onlineClientSize = 0;
    private static Map<String, Set<WsChannelUserIdServer>> wsAppIdMap = new ConcurrentHashMap<>();

    private Session session;
    private String cid = "";
    private String appId = "";

    @OnOpen
    public void onOpen(Session session, @PathParam("appId") String appId, @PathParam("cid") String cid) {
        try {
            this.cid = cid;
            this.appId = appId;
            this.session = session;

            Set<WsChannelUserIdServer> wsServerSet = wsAppIdMap.get(appId);
            if (wsServerSet == null) {
                wsServerSet = new CopyOnWriteArraySet<>();
            }
            wsServerSet.add(this);
            wsAppIdMap.put(appId, wsServerSet);

            addOnlineCount();
            logger.info("cid[{}], appId[{}] 连接开启监听!当前在线人数为 {}", cid, appId, onlineClientSize);

        } catch (Exception e) {
            logger.error("ws监听异常 cid[{}], appId[{}]", cid, appId, e);
        }
    }

    @OnClose
    public void onClose() {
        Set<WsChannelUserIdServer> wsSet = wsAppIdMap.get(this.appId);
        wsSet.remove(this);
        if (wsSet.isEmpty()) {
            wsAppIdMap.remove(this.appId);
        }

        subOnlineCount();
        logger.info("cid[{}], appId[{}] 连接关闭!当前在线人数为 {}", cid, appId, onlineClientSize);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("ws发生错误", error);
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static void sendMsgByAppAndCid(String appId, String cid, String msg) {
        try {
            logger.info("推送ws消息到浏览器, appId={}, cid={}, msg={}", appId, cid, msg);

            Set<WsChannelUserIdServer> wsSet = wsAppIdMap.get(appId);
            if (wsSet == null || wsSet.isEmpty()) {
                logger.info("appId[{}] 无ws监听客户端", appId);
                return;
            }

            for (WsChannelUserIdServer item : wsSet) {
                if (!cid.equals(item.cid)) {
                    continue;
                }
                try {
                    item.sendMessage(msg);
                } catch (Exception e) {
                    logger.info("推送设备消息时异常,appId={}, cid={}", appId, item.cid, e);
                }
            }
        } catch (Exception e) {
            logger.info("推送消息时异常,appId={}", appId, e);
        }
    }

    public static synchronized int getOnlineClientSize() {
        return onlineClientSize;
    }

    public static synchronized void addOnlineCount() {
        onlineClientSize++;
    }

    public static synchronized void subOnlineCount() {
        onlineClientSize--;
    }
}

5. 代码解析

5.1 连接管理

  • @OnOpen: 当客户端连接成功时调用此方法。可以在此方法中获取客户端的 appIdcid,并将当前连接的会话存储到 wsAppIdMap 中。
  • @OnClose: 当客户端连接关闭时调用此方法,从 wsAppIdMap 中移除该连接。
  • @OnError: 处理连接错误。

5.2 消息发送

  • sendMessage(String message): 通过当前会话向客户端发送消息。
  • sendMsgByAppAndCid(String appId, String cid, String msg): 根据 appIdcid 向特定客户端推送消息。

5.3 在线人数管理

使用 onlineClientSize 变量记录当前在线的客户端数量,并提供相应的增减方法。

好的!下面我将为你提供一个简单的前端实现示例,使用 Vue.jsReact 来连接我们之前创建的 WebSocket 服务。这样,你可以看到如何在前端与后端进行实时通信。

6. Vue.js 前端实现

6.1 安装 Vue.js

如果你还没有创建 Vue 项目,可以使用 Vue CLI 创建一个新的项目:

npm install -g @vue/cli
vue create websocket-demo
cd websocket-demo

6.2 创建 WebSocket 组件

src/components 目录下创建一个名为 WebSocketComponent.vue 的文件,并添加以下代码:

<template>
  <div>
    <h1>WebSocket Demo</h1>
    <input v-model="message" placeholder="Type a message" />
    <button @click="sendMessage">Send</button>
    <div>
      <h2>Messages:</h2>
      <ul>
        <li v-for="(msg, index) in messages" :key="index">{{ msg }}</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      socket: null,
      message: '',
      messages: [],
      appId: 'yourAppId', // 替换为你的 appId
      cid: 'yourClientId'  // 替换为你的客户端自定义ID
    };
  },
  created() {
    this.connect();
  },
  methods: {
    connect() {
      this.socket = new WebSocket(`ws://localhost:8080/api/anon/ws/channelUserId/${this.appId}/${this.cid}`);

      this.socket.onopen = () => {
        console.log('WebSocket connection established.');
      };

      this.socket.onmessage = (event) => {
        const data = JSON.parse(event.data);
        this.messages.push(data.message);
      };

      this.socket.onclose = () => {
        console.log('WebSocket connection closed.');
      };

      this.socket.onerror = (error) => {
        console.error('WebSocket error:', error);
      };
    },
    sendMessage() {
      if (this.socket && this.socket.readyState === WebSocket.OPEN) {
        this.socket.send(JSON.stringify({ message: this.message }));
        this.message = ''; // 清空输入框
      } else {
        console.error('WebSocket is not open.');
      }
    }
  }
};
</script>

<style scoped>
/* 添加样式 */
</style>

6.3 使用组件

src/App.vue 中使用这个组件:

<template>
  <div id="app">
    <WebSocketComponent />
  </div>
</template>

<script>
import WebSocketComponent from './components/WebSocketComponent.vue';

export default {
  components: {
    WebSocketComponent
  }
};
</script>

<style>
/* 添加样式 */
</style>

6.4 运行 Vue 应用

在项目根目录下运行以下命令启动 Vue 应用:

npm run serve

7. React 前端实现

7.1 安装 React

如果你还没有创建 React 项目,可以使用 Create React App 创建一个新的项目:

npx create-react-app websocket-demo
cd websocket-demo

7.2 创建 WebSocket 组件

src 目录下创建一个名为 WebSocketComponent.js 的文件,并添加以下代码:

import React, { useEffect, useState } from 'react';

const WebSocketComponent = () => {
  const [socket, setSocket] = useState(null);
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
  const appId = 'yourAppId'; // 替换为你的 appId
  const cid = 'yourClientId'; // 替换为你的客户端自定义ID

  useEffect(() => {
    const ws = new WebSocket(`ws://localhost:8080/api/anon/ws/channelUserId/${appId}/${cid}`);
    setSocket(ws);

    ws.onopen = () => {
      console.log('WebSocket connection established.');
    };

    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      setMessages((prevMessages) => [...prevMessages, data.message]);
    };

    ws.onclose = () => {
      console.log('WebSocket connection closed.');
    };

    ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };

    return () => {
      ws.close();
    };
  }, [appId, cid]);

  const sendMessage = () => {
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ message }));
      setMessage(''); // 清空输入框
    } else {
      console.error('WebSocket is not open.');
    }
  };

  return (
    <div>
      <h1>WebSocket Demo</h1>
      <input
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Type a message"
      />
      <button onClick={sendMessage}>Send</button>
      <div>
        <h2>Messages:</h2>
        <ul>
          {messages.map((msg, index) => (
            <li key={index}>{msg}</li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default WebSocketComponent;

7.3 使用组件

src/App.js 中使用这个组件:

import React from 'react';
import WebSocketComponent from './WebSocketComponent';

function App() {
  return (
    <div className="App">
      <WebSocketComponent />
    </div>
  );
}

export default App;

7.4 运行 React 应用

在项目根目录下运行以下命令启动 React 应用:

npm start

8. 总结

通过以上步骤,我们实现了一个简单的 WebSocket 前端示例,分别使用了 Vue.js 和 React。用户可以通过输入框发送消息,接收来自 WebSocket 服务器的消息。

8.1 注意事项

  • 确保 WebSocket 服务器正在运行,并且前端应用能够访问到它。
  • 替换 yourAppIdyourClientId 为实际的应用 ID 和客户端 ID。

希望这能帮助你更好地理解如何在前端实现 WebSocket 通信!如有任何问题,请随时询问。

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

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

相关文章

音频语言模型与多模态体系结构

音频语言模型与多模态体系结构 多模态模型正在创造语言、视觉和语音等以前独立的研究领域的协同效应。这些模型使用通用架构,将每种模式视为不同的“token”,使它们能够以一种与人类认知非常相似的方式联合建模和理解世界。 ​ ​可以将多模态分为两个主要领域:输入空间(…

几个Linux系统安装体验(续): 深度桌面系统

本文介绍深度桌面系统&#xff08;deepin&#xff09;的安装。 下载 下载地址&#xff1a; https://www.deepin.org/zh/download/ 下载文件&#xff1a;本文下载文件名称为NFSDesktop-5.0-G230-240806-amd64.iso。 下载注意事项&#xff1a;镜像可直接下载&#xff0c;无须…

LabVIEW实车四轮轮速信号再现系统

开发了一个基于LabVIEW的实车四轮轮速信号再现系统。该系统解决现有电机驱动传感器成本高、重复性差、真实性差和精度低等问题&#xff0c;提供一种高精度、低成本的轮速信号再现解决方案。 项目背景 ABS轮速传感器在现代汽车安全系统中发挥着至关重要的作用。为保证其准确性和…

C#异步多线程——浅谈async/await底层原理

async/await是块语法糖&#xff0c;编译器帮助我们做了很多工作&#xff0c;下面我们就简单剖析一下async/await的底层原理。 反编译工具ILSpy安装 我用的是ILSpy反编译生成的dll程序集。还没有ILSpy工具的小伙伴可以直接在VS中安装&#xff1b;点击Extensions>Manage Ext…

1,Linux环境变量基本定义(基于Ubuntu示例进行讲解)

linux环境变量的概念 Linux环境变量&#xff08;准确说应该是shell变量&#xff09;&#xff0c;是直接存储在操作系统中的一组键值对&#xff08;dict类型&#xff09;&#xff0c;用于配置系统和应用程序的操作行为。 【有经验的描述】&#xff1a;它们的工作原理很简单&am…

【Python通过UDP协议传输视频数据】(界面识别)

提示&#xff1a;界面识别项目 前言 随着网络通信技术的发展&#xff0c;视频数据的实时传输在各种场景中得到了广泛应用。UDP&#xff08;User Datagram Protocol&#xff09;作为一种无连接的协议&#xff0c;凭借其低延迟、高效率的特性&#xff0c;在实时性要求较高的视频…

深度学习中的张量 - 使用PyTorch进行广播和元素级操作

深度学习中的张量 - 使用PyTorch进行广播和元素级操作 元素级是什么意思&#xff1f; 元素级操作在神经网络编程中与张量的使用非常常见。让我们从一个元素级操作的定义开始这次讨论。 一个_元素级_操作是在两个张量之间进行的操作&#xff0c;它作用于各自张量中的相应元素…

几个Linux系统安装体验(续): 中科方德服务器系统

本文介绍中科方德服务器系统&#xff08;NFSDesktop&#xff09;的安装。 下载 下载地址&#xff1a; https://www.nfschina.com/index.php?catid68 下载文件&#xff1a;本文下载的文件名称为NFSCNS-4.0-G330-x86_64-241128.iso。 下载注意事项&#xff1a;无法直接下载&…

浅谈计算机网络03 | 现代网络组成

现代网络组成 一 、网络生态体系1.1网络生态系统的多元主体1.2 网络接入设施的多样类型 二、现代网络的典型体系结构解析三、高速网络技术3.1 以太网技术3.2 Wi-Fi技术的深度剖析3.2.1 应用场景的多元覆盖3.2.2 标准升级与性能提升 3.3 4G/5G蜂窝网的技术演进3.3.1 蜂窝技术的代…

JavaWeb 前端基础 html + CSS 快速入门 | 018

今日推荐语 指望别人的救赎&#xff0c;势必走向毁灭——波伏娃 日期 学习内容 打卡编号2025年01月17日JavaWeb 前端基础 html CSS018 前言 哈喽&#xff0c;我是菜鸟阿康。 今天 正式进入JavaWeb 的学习&#xff0c;简单学习 html CSS 这2各前端基础部分&am…

内网渗透测试工具及渗透测试安全审计方法总结

1. 内网安全检查/渗透介绍 1.1 攻击思路 有2种思路&#xff1a; 攻击外网服务器&#xff0c;获取外网服务器的权限&#xff0c;接着利用入侵成功的外网服务器作为跳板&#xff0c;攻击内网其他服务器&#xff0c;最后获得敏感数据&#xff0c;并将数据传递到攻击者&#xff0…

Git 安装 操作 命令 远程仓库 多人协作

Git作用 Git诞生史 很多人都知道&#xff0c;Linus在1991年创建了开源的Linux&#xff0c;从此&#xff0c;Linux系统不断发展&#xff0c;已经成为最大的服务器系统软件了。Linus虽然创建了Linux&#xff0c;但Linux的壮大是靠全世界热心的志愿者参与的&#xff0c;这么多人在…

Mockito+PowerMock+Junit单元测试

一、单元测试用途 1、日常开发团队要求规范&#xff0c;需要对开发需求代码进行单元测试并要求行覆盖率达到要求&#xff0c;DevOps流水线也会开设相关门禁阀值阻断代码提交&#xff0c;一般新增代码行覆盖率80%左右。 二、Mock测试介绍 1、Mock是为了解决不同的单元之间由于…

2024CVPR《HomoFormer》

这篇论文提出了一种名为HomoFormer的新型Transformer模型,用于图像阴影去除。论文的主要贡献和创新点如下: 1. 研究背景与动机 阴影去除的挑战:阴影在自然场景图像中普遍存在,影响图像质量并限制后续计算机视觉任务的性能。阴影的空间分布不均匀且模式多样,导致传统的卷积…

JavaEE之CAS

上文我们认识了许许多多的锁&#xff0c;此篇我们的CAS就是从上文的锁策略开展的新概念&#xff0c;我们来一探究竟吧 1. 什么是CAS&#xff1f; CAS: 全称Compare and swap&#xff0c;字⾯意思:“比较并交换”&#xff0c;⼀个CAS涉及到以下操作&#xff1a; 我们假设内存中…

国产编辑器EverEdit - 复制为RTF

1 复制为RTF 1.1 应用背景 在写产品手册或者其他文档时&#xff0c;可能会用到要将产品代码以样例的形式放到文档中&#xff0c;一般的文本编辑器拷贝粘贴到Word中也就是普通文本&#xff0c;没有语法着色&#xff0c;这样感观上不是太好&#xff0c;为了让读者的感观更好一点…

LLM - 大模型 ScallingLaws 的 C=6ND 公式推导 教程(1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145185794 Scaling Laws (缩放法则) 是大模型领域中&#xff0c;用于描述 模型性能(Loss) 与 模型规模N、数据量D、计算资源C 之间关系的经验规律…

CSS认识与实践

目录 CSS 是什么 基本语法规范 引入方式 内部样式表 行内样式表 外部样式 空格规范 选择器 选择器的功能 选择器的种类 基础选择器 标签选择器 类选择器 id 选择器 通配符选择器 基础选择器小结 复合选择器 后代选择器 子选择器 并集选择器 伪类选择器 复合…

vue项目引入阿里云svg资源图标

1&#xff1a;生成svg图标 登录阿里云官网 1.1 创建项目组 1.2 从阿里云网站上面获取喜欢的图标加入到已有的项目组 1.3 如果团队有自己的设计师&#xff0c;也可以让设计师上传自己的svg图标到阿里云指定的项目组&#xff1b; 使用的时候&#xff0c;把 资源包下载到本地项…

Redis 中 TTL 的基本知识与禁用缓存键的实现策略(Java)

目录 前言1. 基本知识2. Java代码 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 单纯学习Redis可以看我前言的Java基本知识路线&#xff01;&#xff01; 对于Java的基本知识推荐阅读&#xff1a; java框架…