基于SpringBoot实现WebSocket实时通讯的服务端和客户端

实现功能

服务端注册的客户端的列表;服务端向客户端发送广播消息;服务端向指定客户端发送消息;服务端向多个客户端发送消息;客户端给服务端发送消息;
效果:
请添加图片描述在这里插入图片描述

环境

jdk:1.8
SpringBoot:2.4.17

服务端

1.引入依赖:

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

2.在启动类上加上开启WebSocket的注解

@EnableWebSocket

3.配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** date created : Created in 2024/3/18 16:57* description  : WebSocketConfig 主要解决使用了@ServerEndpoint注解的websocket endpoint不被springboot扫描到的问题* class name   : WebSocketConfig*/
@Configuration
public class WebSocketConfig {/*** 注入ServerEndpointExporter,* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

4.服务端实现

/*** date created : Created in 2024/3/18 16:31* description  : 服务端实现,方法的封装* class name   : WebSocketServer*/
@Component
@Slf4j
@ServerEndpoint("/websocket/{applicationName}")
public class WebSocketServer {//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;// 应用名称private String applicationName;//虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。private static final CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();// 用来存在线连接用户信息private static final ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "applicationName") String applicationName) {try {this.session = session;this.applicationName = applicationName;webSockets.add(this);sessionPool.put(applicationName, session);log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());log.info("【当前客户端列表】:"+ sessionPool.keySet());} catch (Exception e) {}}/*** description : 有连接断开之后的处理方法* method name : onClose* param       : []* return      : void*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.applicationName);log.info("【websocket消息】连接断开,总数为:" + webSockets.size());log.info("【当前客户端列表】:"+ sessionPool.keySet());} catch (Exception e) {}}/*** description : 收到客户端消息的处理方法* method name : onMessage* param       : [message]* return      : void*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:" + message);}/*** description : 错误处理* method name : onError* param       : [session, error]* return      : void*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:" + error.getMessage());error.printStackTrace();}/*** description : 广播消息 给所有注册的客户端发送消息* method name : sendBroadcastMessage* param       : [message]* return      : void*/public void sendBroadcastMessage(String message) {log.info("【websocket消息】广播消息:" + message);for (WebSocketServer webSocket : webSockets) {try {if (webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}/*** description : 给指定的客户端发送消息* method name : sendApplicationMessage* param       : [applicationName 客户端的应用名称, message 要发送的消息]* return      : void*/public void sendApplicationMessage(String applicationName, String message) {Session session = sessionPool.get(applicationName);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}/*** description : 给多个客户端发送消息* method name : sendMassApplicationMessage* param       : [applicationNames 注册的客户端的应用名称, message 要发送的消息]* return      : void*/public void sendMassApplicationMessage(String[] applicationNames, String message) {for (String userId : applicationNames) {Session session = sessionPool.get(userId);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}}

客户端

1.客户端配置

yaml文件的末尾添加

# websocket的配置
websocket:host: localhostport: 19022prefix: websocket

2.客户端配置类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;/*** date created : Created in 2024/3/19 14:36* description  : 注入配置文件中的参数 并生成服务端的对应的url* class name   : WebSocketProperties*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "websocket")
@Configuration
public class WebSocketProperties {@Value("${spring.application.name}")String appName;String host;String port;String prefix;public String getUrl() {return String.format("ws://%s:%s/%s/%s", host, port, prefix,appName);}
}

3.客户端实现

import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;import javax.websocket.ClientEndpoint;
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;/*** date created : Created in 2024/3/18 16:36* description  : 客户端接收服务端的实时消息、发送消息等方法的封装* class name   : WebSocketClient*/@ClientEndpoint
@AutoConfigureBefore(WebSocketProperties.class)
@Component
@Import(WebSocketProperties.class)
@Configuration
public class WebSocketClient {private Session session;public WebSocketClient() {try {WebSocketProperties webSocketProperties = SpringUtils.getBean(WebSocketProperties.class);WebSocketContainer container = ContainerProvider.getWebSocketContainer();container.connectToServer(this, new URI(webSocketProperties.getUrl()));} catch (DeploymentException | URISyntaxException | IOException e) {e.printStackTrace();}}@OnOpenpublic void onOpen(Session session) {this.session = session;System.out.println("Connected to server");}@OnMessagepublic String onMessage(String message) {System.out.println("来自WebSocket的消息: " + message);return message;}@OnClosepublic void onClose() {System.out.println("Disconnected from server");}public void register() {try {session.getBasicRemote().sendText("register");System.out.println("Registered with server");} catch (IOException e) {e.printStackTrace();}}public void unregister() {try {session.getBasicRemote().sendText("unregister");System.out.println("Unregistered from server");} catch (IOException e) {e.printStackTrace();}}}

使用@Autowired注入配置类无法注入,使用工具类获取,工具类:

* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;@Component
public class SpringUtils implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}public static Object getBean(String name) {return context.getBean(name);}public static <T> T getBean(Class<T> clazz) {return context.getBean(clazz);}public static <T> T getBean(String name, Class<T> clazz) {return context.getBean(name, clazz);}
}

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

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

相关文章

大模型+强化学习_通过强化学习对齐大模型和环境

英文名称: True Knowledge Comes from Practice: Aligning LLMs with Embodied Environments via Reinforcement Learning 中文名称: 实践出真知&#xff1a;通过强化学习将LLMS与具体环境对齐 链接: https://arxiv.org/abs/2401.14151 代码: https://github.com/WeihaoTan/TWO…

BI技巧丨个性化视觉对象

BOSS&#xff1a;那个&#xff0c;那个谁&#xff0c;最近用户反映了&#xff0c;说是你们做的报表不太行啊&#xff1f;&#xff01; 白茶&#xff1a;&#xff08;&#xff1f;&#xff1f;&#xff1f;&#xff09;老板&#xff0c;怎么说&#xff1f; BOSS&#xff1a;就是…

网络原理(5)——IP协议(网络层)

目录 一、IP协议报头介绍 1、4位版本 2、4位首部长度 3、8位服务器类型 4、16位总长度 5、16位标识位 6、3位标志位 7、13位偏移量 8、8位生存空间 9、8位协议 10、16位首部检验和 11、32位源IP地址 12、32位目的IP地址 二、IP协议如何管理地址&#xff1f; 1、动…

Redis入门到实战-第三弹

Redis入门到实战 Redis数据类型官网地址Redis概述Redis数据类型介绍更新计划 Redis数据类型 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#…

《优化接口设计的思路》系列:第九篇—用好缓存,让你的接口速度飞起来

一、前言 大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 作为一名从业已达六年的老码农&#xff0c…

2024Python计算机二级13

一维数据采用线性方式组织&#xff0c;集合不属于线性结构 程调度仅负责对CPU进行分配 快速排序每经过一次元素的交换会产生新的逆序 记住队列中为一个元素的情况 区分二叉树与完全二叉树&#xff0c;前序序列是先访问根节点再访问左子树和右子树&#xff0c;中序序列是访问左子…

《定时执行专家》:Nircmd 的超级搭档,解锁自动化新境界

目录 Nircmd 简介 《定时执行专家》与 Nircmd 的结合 示例&#xff1a; 自动清理电脑垃圾: 定时发送邮件: 定时关闭电脑: 《定时执行专家》的优势: 总结: 以下是一些其他使用示例&#xff1a; 立即下载《定时执行专家》&#xff1a; Nircmd 官方网站&#xff1a; 更…

【数字IC/FPGA】书籍推荐(1)----《轻松成为设计高手--Verilog HDL实用精解》

在下这几年关于数字电路、Verilog、FPGA和IC方面的书前前后后都读了不少&#xff0c;发现了不少好书&#xff0c;也在一些废话书上浪费过时间。接下来会写一系列文章&#xff0c;把一部分读过的书做个测评&#xff0c;根据个人标准按十分制满分来打分分享给大家。 书名&#xf…

企业微信可以更换公司主体吗?

企业微信变更主体有什么作用&#xff1f;当我们的企业因为各种原因需要注销或已经注销&#xff0c;或者运营变更等情况&#xff0c;企业微信无法继续使用原主体继续使用时&#xff0c;可以申请企业主体变更&#xff0c;变更为新的主体。企业微信变更主体的条件有哪些&#xff1…

基于Docker的JMeter分布式压测!

一个JMeter实例可能无法产生足够的负载来对你的应用程序进行压力测试。如本网站所示&#xff0c;一个JMeter实例将能够控制许多其他的远程JMeter实例&#xff0c;并对你的应用程序产生更大的负载。JMeter使用Java RMI[远程方法调用]来与分布式网络中的对象进行交互。JMeter主站…

Vue js封装接口

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.安装axios npm install axios -g 2.在src下新建一个Api文件夹,再创建一个js文件 import axios from axios let configuration {url:"http://localhost:9090" } /*** 请求项目数据的请求体*/ async function h…

CV论文--2024.3.20

1、Graph Expansion in Pruned Recurrent Neural Network Layers Preserve Performance 中文标题&#xff1a;图扩展在修剪的循环神经网络层中保持性能 简介&#xff1a;这段摘要讨论了图的扩展性质&#xff0c;包括强连通性和稀疏性。研究表明&#xff0c;深度神经网络可以通…

linux -- I2C设备驱动 -- MS32006(低压5V多通道电机驱动器)

产品简述 MS32006 是一款多通道电机驱动芯片, 其中包含两路步进电机驱动, 一路直流电机驱动; 每个通道的电流最高电流1.0A; 支持两相四线与四相五线步进电机。芯片采用 I2C 的通信接口控制模式, 兼容 3.3V/5V 的标准工业接口。 MS32006 总共集成了两路步进电机驱动器与一…

【c++入门】引用,内联函数,auto

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本节我们来到c中一个重要的部分&#xff1a;引用 目录 1.引用的基本概念与用法1.1引用特性1.2使用场景1.3传值、传引用效率比较1.4引用做返回值1.5引用和指针的对…

公司调研 | 空间机械臂GITAI | 日企迁美

最近做的一些公司 / 产品调研没有从技术角度出发&#xff0c;而更关注宏观发展&#xff1a;主营方向、产品介绍、商业化落地情况、融资历程、公司愿景、创始人背景等。部分调研放在知乎上&#xff0c;大部分在飞书私人链接上 最近较关注人形Robot的发展情况&#xff0c;欢迎感兴…

【Java】Map和Set

文章目录 一、Map和Set的概念二、模型三、Map的说明3.1 Map.Entry<K, V>的说明3.2 Map 的常用方法 四、Set的说明4.1 Set的常用方法 一、Map和Set的概念 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关&#xff0c…

在线播放视频网站源码系统 带完整的安装代码包以及搭建教程

在线播放视频网站源码系统的开发&#xff0c;源于对当前视频市场的深入洞察和用户需求的精准把握。随着视频内容的爆炸式增长&#xff0c;用户对视频播放的需求也日益多样化。他们希望能够随时随地观看自己感兴趣的视频内容&#xff0c;同时还希望能够在观看过程中享受到流畅、…

用vscode调试cpp程序相关操作记录

需要在服务器上用vscode调试cpp程序&#xff0c;写此记录launch.json配置和相关步骤错误导致的问题 1.在需要运行程序的服务器上安装C/C Extension Pack&#xff08;之前只在本地装了&#xff09;&#xff0c;可以支持调试C/C应用程序(设置断点&#xff0c;单步执行&#xff0c…

分类预测 | Matlab实现PSO-KELM粒子群优化算法优化核极限学习机分类预测

分类预测 | Matlab实现PSO-KELM粒子群优化算法优化核极限学习机分类预测 目录 分类预测 | Matlab实现PSO-KELM粒子群优化算法优化核极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现PSO-KELM粒子群优化算法优化核极限学习机分类预测(完整源…

ubuntu20.04安裝輸入法

文章目录 前言一、操作過程1、安装fcitx-googlepinyin2、配置language support 前言 參考文獻 一、操作過程 1、安装fcitx-googlepinyin sudo apt-get install fcitx-googlepinyin2、配置language support 第一次點擊進去&#xff0c;會讓你安裝 點擊ctrl和空格切換中英文…