SpringBoot2.0集成WebSocket
<!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
新建配置类
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @Description 开启springboot对websocket的支持* @Author WangKun* @Date 2023/8/14 17:21* @Version*/
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
@Configuration
public class WebSocketConfig{/*** @Description 注入一个ServerEndpointExporter, 会自动注册使用@ServerEndpoint注解* @param* @Throws* @Return org.springframework.web.socket.server.standard.ServerEndpointExporter* @Date 2023-08-14 17:26:31* @Author WangKun*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
这个注解需要打上声明是开发环境,否则在tomcat部署中会报错
新建服务类
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;/*** @Description websocket服务,不考虑分组* @Author WangKun* @Date 2023/8/14 17:29* @Version*/
@ConditionalOnClass(value = WebSocketConfig.class)
@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocket {//在线计数器private static int onlineCount = 0;//存放每个客户端对应的WebSocket对象。private static final ConcurrentHashMap<String, WebSocket> WEB_SOCKET_MAP = new ConcurrentHashMap<>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;private String userId;/*** @param* @Description 在线数* @Throws* @Return int* @Date 2023-08-14 17:47:19* @Author WangKun*/public static synchronized int getOnlineCount() {return onlineCount;}/*** @param* @Description 在线数加1* @Throws* @Return void* @Date 2023-08-14 17:47:32* @Author WangKun*/public static synchronized void addOnlineCount() {WebSocket.onlineCount++;}/*** @param* @Description 在线数减1* @Throws* @Return void* @Date 2023-08-14 17:47:47* @Author WangKun*/public static synchronized void subOnlineCount() {WebSocket.onlineCount--;}/*** @param session* @param userId* @Description 建立连接* @Throws* @Return void* @Date 2023-08-14 17:52:08* @Author WangKun*/@OnOpenpublic void onOpen(final Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;// 防止前端刷新重连用户重复,存在计数器不累加if (WEB_SOCKET_MAP.containsKey(userId)) {WEB_SOCKET_MAP.remove(userId);WEB_SOCKET_MAP.put(userId, this);} else {WEB_SOCKET_MAP.put(userId, this);addOnlineCount();}sendMessage(String.valueOf(ResponseCode.CONNECT_SUCCESS.getCode())); //自定义成功返回码log.info("用户--->{} 连接成功,当前在线人数为--->{}", userId, getOnlineCount());}/*** @param message* @Description 向客户端发送消息 session.getBasicRemote()与session.getAsyncRemote()的区别* @Throws* @Return void* @Date 2023-08-14 17:51:07* @Author WangKun*/public synchronized void sendMessage(String message) {try {// 加锁避免阻塞this.session.getBasicRemote().sendText(message);} catch (IOException e) {log.error("向客户端发送消息--->{}", e.getMessage(), e);throw new RuntimeException(e);}}/*** @param* @Description 关闭连接* @Throws* @Return void* @Date 2023-08-14 17:52:30* @Author WangKun*/@OnClosepublic void onClose(Session session) {if (!WEB_SOCKET_MAP.isEmpty() && WEB_SOCKET_MAP.containsKey(userId)) {this.session = session;WEB_SOCKET_MAP.remove(userId);subOnlineCount();log.info("用户--->{} 关闭连接!当前在线人数为--->{}", userId, getOnlineCount());}}/*** @param message* @param session* @Description 收到客户端消息* @Throws* @Return void* @Date 2023-08-15 10:54:55* @Author WangKun*/@OnMessagepublic void onMessage(String message, Session session) {//这一块可以操作数据,比如存到数据//判断心跳是否存活, 防止心跳自动断开,再重连if ("ping".equalsIgnoreCase(message) && !WEB_SOCKET_MAP.isEmpty() && WEB_SOCKET_MAP.containsKey(userId)) {WEB_SOCKET_MAP.get(userId).sendMessage("pong");}log.info("收到来自客户端用户:{} 消息:--->{}", userId, message);}/*** @param session* @param error* @Description 发生错误时* @Throws* @Return void* @Date 2023-08-15 10:55:27* @Author WangKun*/@OnErrorpublic void onError(Session session, Throwable error) {if (!WEB_SOCKET_MAP.isEmpty() && WEB_SOCKET_MAP.containsKey(userId)) {WEB_SOCKET_MAP.remove(userId);subOnlineCount();log.error("用户--->{} 错误!" + userId, "原因--->{}" + error.getMessage());error.printStackTrace();}}/*** @param userId* @param message* @Description 通过userId向客户端发送消息(指定用户发送)* @Throws* @Return void* @Date 2023-08-14 18:01:35* @Author WangKun*/public static void sendTextMessageByUserId(String userId, String message) {log.info("服务端发送消息到用户{},消息:{}", userId, message);if (!WEB_SOCKET_MAP.isEmpty() && StringUtils.isNotBlank(userId) && WEB_SOCKET_MAP.containsKey(userId)) {WEB_SOCKET_MAP.get(userId).sendMessage(message);} else {log.error("用户{}不在线", userId);}}/*** @param message* @Description 群发自定义消息* @Throws* @Return void* @Date 2023-08-14 18:03:38* @Author WangKun*/public static void sendTextMessage(String message) {// 如果在线一个就广播log.info("广播数据到当前在线人,人数:{}", getOnlineCount());if (getOnlineCount() > 0 && !WEB_SOCKET_MAP.isEmpty()) {for (String item : WEB_SOCKET_MAP.keySet()) {WEB_SOCKET_MAP.get(item).sendMessage(message);log.info("服务端发送消息到用户{},消息:{}", item, message);}}}
}
@ConditionalOnClass(value = WebSocketConfig.class)
指定使用自定义配置文件
相关文章:
SpringBoot2.0集成WebSocket
<!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 新建配置类 import org.springframework.boot.autoconfigure.condition.Cond…...
Vue的Ajax请求-axios、前后端分离练习
Vue的Ajax请求 axios简介 Axios,是Web数据交互方式,是一个基于promise [5]的网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.js http模块, 而在…...
Spring源码深度解析三 (MVC)
书接上回 10.MVC 流程&源码剖析 * 问题1:Spring和SpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?) * 问题2:DispatcherServlet初始化过程中做了什么? * 问题3:请求…...
API接口漏洞利用及防御
API是不同软件系统之间进行数据交互和通信的一种方式。API接口漏洞指的是在API的设计、开发或实现过程中存在的安全漏洞,可能导致恶意攻击者利用这些漏洞来获取未授权的访问、篡改数据、拒绝服务等恶意行为。 1.API接口漏洞简介 API(Application Progr…...
解决Spring mvc + JDK17@Resource无法使用的情况
问题描述 我在使用jdk17进行Spring mvc开发时发现 Resource用不了了。 原因 因为JDK版本升级的改动,在Jdk9~17环境下,搭建Springboot项目,会出现原有Resource(javax.annotation.Resource)不存在的问题,导…...
页面禁用鼠标右键,禁用F12打开开发者工具!!!
文章目录 问题分析方法一方法二方法二问题 今天在浏览博主文章时发现无法复制页面上的内容,也无法F12打开开发者工具,更用不了鼠标右键,于是上网找了原因并亲测可用 分析 方法一 将 <body> 改成 <body oncontextmenu=self.event.returnValue=false>方法二 …...
Android中使用JT808协议进行车载终端通信的实现和优化
JT808是一种在中国广泛应用的车载终端通信协议,用于车辆与监控中心之间的数据通信。下面是关于Android平台上使用JT808协议进行通信的一般步骤和注意事项: 协议了解:首先,您需要详细了解JT808协议的规范和定义。该协议包含了通信消…...
导出pdf
该方法导出的pdf大小是A4纸的尺寸,如果大于1页需要根据元素高度进行截断的话,页面元素需要加 class ergodic-dom,方法里面会获取ergodic-dom元素,对元素高度和A4高度做比较,如果大于A4高度,会塞一个空白元素…...
【考研数学】线形代数第三章——向量 | 基本概念、向量组的相关性与线性表示
文章目录 引言一、向量的概念与运算1.1 基本概念1.2 向量运算的性质 二、向量组的相关性与线性表示2.1 理论背景2.2 相关性与线性表示基本概念2.3 向量组相关性与线性表示的性质 引言 向量是线性代数的重点和难点。向量是矩阵,同时矩阵又是由向量构成的,…...
温故知新之:接口和抽象类有什么区别?
本文以下内容基于 JDK 8 版本。 1、接口介绍 接口是 Java 语言中的一个抽象类型,用于定义对象的公共行为。它的创建关键字是 interface,在接口的实现中可以定义方法和常量,其普通方法是不能有具体的代码实现的,而在 JDK 8 之后&…...
回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测(多指标,多图)
回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测(多指标,多图)…...
文旅景区vr体验馆游乐场vr项目是什么
我们知道现在很多的景区或者游玩的地方,以及学校、科技馆、科普馆、商场或公园或街镇,都会建一些关于游玩以及科普学习的项目。从而增加学习氛围或者带动人流量等等。这样的形式,还是有很好的效果呈现。 普乐蛙VR体验馆案例 下面是普乐蛙做的…...
Vue Element upload组件和Iview upload 组件上传文件
今天要分享的是使用这俩个UI组件库的upload组件分别实现调用组件本身的上传方法实现和后台交互。接下来就是开车的时间,请坐稳扶好~ 一、element upload组件传送门 1、html文件 <el-upload ref"uploadRef" :action"uploadUrl" :data"…...
无涯教程-PHP - File 函数
文件系统功能用于访问和操纵文件系统,PHP为您提供了操纵文件的所有功能。 运行时配置 这些功能的行为受php.ini中的设置影响。 NameDefaultChangeableChangelogallow_url_fopen"1"PHP_INI_ALLPHP_INI_ALL in PHP < 4.3.4. PHP_INI_SYSTEM in PHP &l…...
elasticsearch 常用查询 7.4 版本
Elasticsearch 常用查询 match:全文查询exists:查询存在的字段must_not:查询不存在的字段ids:跟据id查询prefix:前缀查询range: 查询范围term:精准查询terms:多术语查询 本文基于es 7.4版本文档…...
ChatGpt 从入门到精通
相关资源下载地址: 基于ChatGPT的国际中文语法教学辅助应用的探讨.pdf 生成式人工智能技术对教育领域的影响-关于ChatGPT的专访.pdf 电子-从ChatGPT热议看大模型潜力.pdf 从图灵测试到ChatGPT——人机对话的里程碑及启示.pdf 正文 ChatGPT 是一种强大的自然语言处理模型&…...
vscode远程调试
安装ssh 在vscode扩展插件搜索remote-ssh安装 如果连接失败,出现 Resolver error: Error: XHR failedscode 报错,可以看这篇帖子vscode ssh: Resolver error: Error: XHR failedscode错误_阿伟跑呀的博客-CSDN博客 添加好后点击左上角的加号࿰…...
Vue3 数据响应式原理
核心: 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等… 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作 const userData {name: "John",age: 12 };let proxyUser new Proxy(use…...
2023.08.20 学习周报
文章目录 摘要文献阅读1.题目2.现有问题3.解决方案4.本文贡献5.方法5.1 利用长短期记忆网络学习时空演化特征5.2 构建用于气象辅助信息编码的堆叠自编码器5.3 使用多任务学习发现全市通用模式5.4 模型 6. 实验6.1 数据集6.2 实验设置6.3 实验结果 7.结论8.展望 大气污染物传输总…...
软件测试技术之单元测试—工程师 Style 的测试方法(2)
怎么写单元测试? JUnit 简介 基本上每种语言和框架都有不错的单元测试框架和工具,例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的,我们下面就简单介绍下 JUnit。 JUnit…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
