基于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.rhkb.cn/news/283345.html

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

相关文章

如何确保多人游戏的配对体验快速而顺利

开发人员知道,游戏玩家在玩游戏时最快乐,他们等待进入多人游戏的时间越长,你失去他们的速度就越快。玩家不喜欢在大厅里等待;他们想马上参与行动。这就是为什么优化匹配是关键。 谷歌(Google)和Unity的Open Match等系统正在加速使用自定义逻辑构建匹配器的能力,使开发人…

跳过mysql权限验证来修改密码-GPT纯享版

建议重新配置一遍&#xff0c;弄成功好多次了&#xff0c;每次都出bug&#xff0c;又要重新弄&#xff0c;不是过期就是又登不进去了&#xff0c;我服了 电脑配置MySQL环境&#xff08;详细&#xff09;这个哥们的10min配完&#xff0c;轻轻松松&#xff0c; 旧方法&#xff…

Python-VBA编程500例-015-03(入门级)

飞行棋(Flying Chess)算法是一种搜索算法&#xff0c;主要用于解决图搜索和路径规划问题。它的主要特点是可以“飞跃”到棋盘上任何位置&#xff0c;从而大大减少了搜索的时间和空间复杂度。以下是一些飞行棋算法的实际应用场景&#xff1a; 1、路径规划&#xff1a;在机器人领…

利用Scala与Apache HttpClient实现网络音频流的抓取

概述 在当今数字化时代&#xff0c;网络数据的抓取和处理已成为许多应用程序和服务的重要组成部分。本文将介绍如何利用Scala编程语言结合Apache HttpClient工具库实现网络音频流的抓取。通过本文&#xff0c;读者将学习如何利用强大的Scala语言和Apache HttpClient库来抓取网…

1.6 学Python能干什么,Python的应用领域有哪些

Python能干什么&#xff0c;Python的应用领域 Python 作为一种功能强大的编程语言&#xff0c;因其简单易学而受到很多开发者的青睐。那么&#xff0c;Python 的应用领域有哪些呢&#xff1f; Python 有着非广泛的应用&#xff0c;几乎所有大中型互联网公司都在使用 Python&a…

java数据结构与算法刷题-----LeetCode215. 数组中的第K个最大元素

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路&#xff1a;时间复杂度O( n n n)&#xff0c;空间复杂度…

SQL Server 2008R2 日志文件大小设置及查询

SQL Server 2008R2 建立数据库存在日志无限增长问题&#xff0c;造成磁盘内存不足。本文解决这个问题&#xff0c;如下&#xff1a; 1.设置日志文件的最大大小 USE master; GO ALTER DATABASE [D_total] MODIFY FILE (NAME D_total_log, -- 日志文件的逻辑名称MAXSIZE 200…

精准防灾新篇章:GIS与Python机器学习技术在地质灾害风险评价与信息化建库中的前沿应用

结合项目实践案例和科研论文成果进行讲解。入门篇&#xff0c;ArcGIS软件的快速入门与GIS数据源的获取与理解&#xff1b;方法篇&#xff0c;致灾因子提取方法、灾害危险性因子分析指标体系的建立方法和灾害危险性评价模型构建方法&#xff1b;拓展篇&#xff0c;GIS在灾害重建…

idea 开发serlvet篮球秩序册管理系统idea开发mysql数据库web结构计算机java编程layUI框架开发

一、源码特点 idea开发 java servlet 篮球秩序册管理系统是一套完善的web设计系统mysql数据库 系统采用serlvetdaobean mvc 模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 servlet 篮…

机器学习-06-无监督算法-01-划分聚类Kmeans算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中无监督算法&#xff0c;包括划分聚类等。 参考 数据分析实战 | K-means算法——蛋白质消费特征分析 欧洲48国英文名称的来龙去脉及其国旗动画 Kmeans在线动态演示 本门课程的目标 完成一个特定行业的…

java算法第32天 | ● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 本题中理解利润拆分是关键点&#xff01; 不要整块的去看&#xff0c;而是把整体利润拆为每天的利润。假如第 0 天买入&#xff0c;第 3 天卖出&#xff0c;那么利润为&#xff1a;prices[3] - prices[0]。 相当于(prices[3] - prices[2]) (prices[…

Panasonic松下PLC如何数据采集?如何实现快速接入IIOT云平台?

在工业自动化领域&#xff0c;数据采集与远程控制是提升生产效率、优化资源配置的关键环节。对于使用Panasonic松下PLC的用户来说&#xff0c;如何实现高效、稳定的数据采集&#xff0c;并快速接入IIOT云平台&#xff0c;是摆在他们面前的重要课题。HiWoo Box工业物联网关以其强…

练习 12 Web [极客大挑战 2019]BabySQL

本题复习&#xff1a;1.常规的万能语句SQL查询&#xff0c;union联合查询&#xff0c;Extractvalue()报错注入 extractvalue(1,concat(‘0x7e’,select(database())))%23 我一开始挨着试&#xff0c;感觉都无效 直到报错注入&#xff0c;查到了库名‘geek’ 尝试查表名&…

【赠书第21期】游戏力:竞技游戏设计实战教程

文章目录 前言 1 竞技游戏设计的核心要素 1.1 游戏机制 1.2 角色与技能 1.3 地图与环境 2 竞技游戏设计的策略与方法 2.1 以玩家为中心 2.2 不断迭代与优化 2.3 营造竞技氛围与社区文化 3 实战案例分析 4 结语 5 推荐图书 6 粉丝福利 前言 在数字化时代的浪潮中&…

Linux文件 profile、bashrc、bash_profile区别

Linux系统中&#xff0c;有三种文件 出现的非常频繁&#xff0c;那就是 profile、bash_profile、bashrc 文件。 1、profile 作用 profile&#xff0c;路径&#xff1a;/etc/profile&#xff0c;用于设置系统级的环境变量和启动程序&#xff0c;在这个文件下配置会对所有用户…

O2OA红头文件流转与O2OA版式公文编辑器基本使用

O2OA开发平台在流程管理中&#xff0c;提供了符合国家党政机关公文格式标准&#xff08;GB/T 9704—2012&#xff09;的公文编辑组件&#xff0c;可以让用户在包含公文管理的项目实施过程中&#xff0c;轻松地实现标准化公文格式的在线编辑、痕迹保留、手写签批等功能。并且可以…

QT gridlayout 循环设置组件,表格也通用 已解决

在需求中。经常遇到&#xff0c;表格 展示需求。 几乎都是json格式的。 // 列表配置文件QJsonArray listJsonArray getCfgJsonData("details_tab_table_config.json");if (listJsonArray.isEmpty()){return;}ui->gridWidget->setMaximumSize(QSize(310, 180)…

【Mysql】面试题汇总

1. 存储引擎 1-1. MySQL 支持哪些存储引擎&#xff1f;默认使用哪个&#xff1f; 答&#xff1a; MySQL 支持的存储引擎包括 InnoDB、MyISAM、Memory 等。 Mysql 5.5 之前默认的是MyISAM&#xff0c;Mysql 5.5 之后默认的是InnoDB。 可以通过 show engines 查看 Mysql 支持…

SQL Server 文件组详解

数据文件组 SQL Server 数据库最常用的存储文件是数据文件和日志文件。 数据文件用于存储数据&#xff0c;由一个主要数据文件&#xff08;.mdf&#xff09;和若干个次要数据文件&#xff08;.ndf&#xff09;构成&#xff1b;日志文件用于存储事物日志&#xff0c;由.ldf文件…

创龙教仪基于瑞芯微3568的ARM Cortex A-55教学实验箱 适用于人工智能 传感器 物联网等领域

适用课程 Cortex-A55 ARM嵌入式实验箱主要用于《ARM 系统开发》、《ARM 应用开发》《物联网通信技术》、《嵌入式系统设计》、《移动互联网技术》、《无线传感器网络》、《物联网设计方法与应用》、《人工智能》等课程。 适用专业 Cortex-A55 ARM嵌入式实验箱主要面向电子信…