springboot下spring方式实现Websocket并设置session时间
概述
springboot实现websocket有4种方式
servlet,spring,netty,stomp
使用下来spring方式是最简单的.
springboot版本:3.1.2
jdk:17
当前依赖版本
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>3.1.2</version>
</dependency>
spring方式demo
demo借鉴了ruoyi-plus
spring实现websocket处理核心就是AbstractWebSocketHandler+HandshakeInterceptor
HandshakeInterceptor实现握手前后处理,比如握手前鉴权
AbstractWebSocketHandler实现消息的接收,监听连接的建立+关闭,顺序在HandshakeInterceptor之后.
拦截器
package org.xxx.xxx.websocket.interceptor;import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import org.xxx.common.core.domain.model.LoginUser;
import org.xxx.common.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;import java.util.List;
import java.util.Map;import static org.qps.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;/*** WebSocket握手请求的拦截器*/
@Slf4j
public class PlusWebSocketInterceptor implements HandshakeInterceptor {/*** 握手前** @param request request* @param response response* @param wsHandler wsHandler* @param attributes attributes* @return 是否握手成功*/@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {attributes.put(LOGIN_USER_KEY, getLoginUser(request));List<String> tokenList = request.getHeaders().get("Sec-Websocket-Protocol");//再塞回协议头里response.getHeaders().put("Sec-Websocket-Protocol", tokenList);return true;}private LoginUser getLoginUser(ServerHttpRequest request){List<String> tokenList = request.getHeaders().get("Sec-Websocket-Protocol");if(CollectionUtils.isEmpty(tokenList)){throw new ServiceException("用户未登录");}//请求头的子协议中获取tokenString token = tokenList.get(0);return loginUser;}/*** 握手后** @param request request* @param response response* @param wsHandler wsHandler* @param exception 异常*/@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {}
}
消息接收+连接建立+关闭处理
package org.xxx.common.websocket.handler;import org.xxx.common.core.domain.model.LoginUser;
import org.xxx.common.websocket.dto.WebSocketMessageDto;
import org.xxx.common.websocket.holder.WebSocketSessionHolder;
import org.xxx.common.websocket.utils.WebSocketUtils;
import lombok.extern.slf4j.Slf4j;
import org.xxx.common.websocket.constant.WebSocketConstants;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;import java.util.List;
import java.util.Objects;/*** WebSocketHandler 实现类*/
@Slf4j
public class PlusWebSocketHandler extends AbstractWebSocketHandler {/*** 连接成功后*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) {LoginUser loginUser = (LoginUser) session.getAttributes().get(WebSocketConstants.LOGIN_USER_KEY);WebSocketSessionHolder.addSession(loginUser.getUserId(), session);log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());}/*** 处理发送来的文本消息** @param session* @param message* @throws Exception*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {String msgPayload = message.getPayload();//todo 业务操作}@Overrideprotected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {super.handleBinaryMessage(session, message);}/*** 心跳监测的回复* 针对ping帧的恢复,非text形式的消息接收* @param session* @param message* @throws Exception*/@Overrideprotected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {WebSocketUtils.sendPongMessage(session);}/*** 连接出错时** @param session* @param exception* @throws Exception*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage());}/*** 连接关闭后** @param session* @param status*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) {//连接关闭}/*** 是否支持分片消息** @return*/@Overridepublic boolean supportsPartialMessages() {return false;}}
配置类
package org.xxx.common.websocket.config;import cn.hutool.core.util.StrUtil;
import org.xxx.common.websocket.config.properties.WebSocketProperties;
import org.xxx.common.websocket.handler.PlusWebSocketHandler;
import org.xxx.common.websocket.interceptor.PlusWebSocketInterceptor;
import org.xxx.common.websocket.listener.WebSocketTopicListener;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;/*** WebSocket 配置*/
@AutoConfiguration
//我是走的自定义配置文件是否允许开启websocket
@ConditionalOnProperty(value = "websocket.enabled", havingValue = "true")
@EnableConfigurationProperties(WebSocketProperties.class)
@EnableWebSocket
public class WebSocketConfig{@Beanpublic WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor,WebSocketHandler webSocketHandler,WebSocketProperties webSocketProperties) {if (StrUtil.isBlank(webSocketProperties.getPath())) {webSocketProperties.setPath("/websocket");}if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) {webSocketProperties.setAllowedOrigins("*");}return registry -> registry.addHandler(webSocketHandler, webSocketProperties.getPath()).addInterceptors(handshakeInterceptor).setAllowedOrigins(webSocketProperties.getAllowedOrigins());}@Beanpublic HandshakeInterceptor handshakeInterceptor() {return new PlusWebSocketInterceptor();}@Beanpublic WebSocketHandler webSocketHandler() {return new PlusWebSocketHandler();}/*** 自定义服务器属性容器配置** @return 自定义服务器容器属性配置*/@Beanpublic ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();//session超时时间25s,客户端25s没有交互就断开连接container.setMaxSessionIdleTimeout(25 * 1000L);container.setMaxTextMessageBufferSize(10 * 1024);container.setMaxBinaryMessageBufferSize(10 * 1024);return container;}
}
相关文章:
springboot下spring方式实现Websocket并设置session时间
概述 springboot实现websocket有4种方式 servlet,spring,netty,stomp 使用下来spring方式是最简单的. springboot版本:3.1.2 jdk:17 当前依赖版本 <dependency><groupId>org.springframework.boot<…...
LeetCode算法二叉树—相同的树
目录 100. 相同的树 - 力扣(LeetCode) 代码: 运行结果: 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是…...
搭建Flink集群、集群HA高可用以及配置历史服务器
Flink集群搭建 Flink集群搭建集群规划下载并解压安装包修改集群配置分发安装目录启动集群访问Web UI Flink集群HA高可用概述集群规划配置flink配置master、workers配置ZK分发安装目录启动HA集群测试 Flink参数配置配置历史服务器概述配置启动、停止历史服务器提交一个Job任务查…...
vscode终端中打不开conda虚拟包管理
今天,想着将之前鸽的Unet网络模型给实现一下,结果发现,在vscode中运行python脚本,显示没有这包,没有那包。但是在其他的ipynb中是有的,感觉很奇怪。我检查了一下python版本,发现不是我深度学习的…...
【音视频】MP4封装格式
基本概念 使用MP4box.js查看MP4内部组成结构 整体结构 数据索引(moov)数据流包(mdat) 各个包的位置,大小,信息,时间戳,编码方式等全在数据索引 数据流包只有纯二进制码流数据 数据…...
环境-使用vagrant快速创建linux虚拟机
1.下载软件 虚拟机 Oracle VM VirtualBox 镜像 Vagrant by HashiCorp (vagrantup.com) 如果下载慢,可以复制下载链接,使用迅雷下载 2.安装 根据提示点击下一步即可,建议安装到空间较大的非系统盘。 打开 window cmd 窗口,…...
10.1网站编写(Tomcat和servlet基础)
一.Tomcat: 1.Tomcat是java写的,运行时需要依赖jre,所以要装jdk. 2.建议配置好环境变量. 3.默认端口号8080(业务端口)可能会被占用,建议改一下(本人改成了9999). 4.另一个默认端口是8005(管理端口). 二Servlet基础(编写一个hello world代码): 整体分为7个步骤,分别是创建…...
10CQRS
本系列包含以下文章: DDD入门DDD概念大白话战略设计代码工程结构请求处理流程聚合根与资源库实体与值对象应用服务与领域服务领域事件CQRS(本文) 案例项目介绍 # 既然DDD是“领域”驱动,那么我们便不能抛开业务而只讲技术&…...
DAZ To UMA⭐一.DAZ简单使用教程
文章目录 🟥 DAZ快捷键🟧 DAZ界面介绍 🟥 DAZ快捷键 移动物体:ctrlalt鼠标左键 旋转物体:ctrlalt鼠标右键 导入模型:双击左侧模型UI 🟧 DAZ界面介绍 Files:显示全部文件 Products:显示全部产品 Figures:安装的全部人物 Wardrobe…...
面试题 —— Java集合篇(23题)
文章目录 1.Java中常见集合有哪些 ?2. 说说你对Java集合是怎么理解的?3.请你说一下List,Set,Map三者的特点是 ?4.在实际开发过程中如何更好的选择集合 ?5. ArrayList和Vector区别 ?6. ArrayList…...
SpringBoot2.7.14整合Swagger3.0的详细步骤及容易踩坑的地方
🧑💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 📖所属专栏:Sp…...
题解:ABC321D - Set Menu
题解:ABC321D - Set Menu 题目 链接:Atcoder。 链接:洛谷。 难度 算法难度:B。 思维难度:C。 调码难度:B。 综合评价:见洛谷链接。 算法 枚举二分查找。 思路 先对b升序排序&#x…...
什么是Progressive Web App(PWA)?它们有哪些特点?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 渐进式Web App简介⭐ PWAs的主要特点⭐ 总结⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入…...
MySQL的高级SQL语句
目录 一、高级SQL语句 1、select 查询表中一个或多个字段的数据 2、distinct 不显示重复的数据记录 3、where 有条件查询 4、and与or 且与或 5、in 显示在某个范围值内 的字段的信息 6、between 显示两个值范围内的数据记录 7、order by 对字…...
基于人脸5个关键点的人脸对齐(人脸纠正)
摘要:人脸检测模型输出人脸目标框坐标和5个人脸关键点,在进行人脸比对前,需要对检测得到的人脸框进行对齐(纠正),本文将通过5个人脸关键点信息对人脸就行对齐(纠正)。 一、输入图像…...
vue3中两个el-select下拉框选项相互影响
vue3中两个el-select下拉框选项相互影响 1、开发需求2、代码2.1 定义hooks文件2.2 在组件中使用 1、开发需求 如图所示,在项目开发过程中,遇到这样一个需求,常规时段中选中的月份在高峰时段中是禁止选择的状态,反之亦然。 2、代…...
博弈论——反应函数
反应函数 1 引言 谢老师的《经济博弈论》书中对反应函数并没有给出一般笼统的定义,而是将其应用与古诺模型并给出了相关解释:反应函数是指在无限策略的古诺博弈模型中,博弈方的策略有无限多种,因此各个博弈方的最佳对策也有无限…...
UE5读取json文件
一、下载插件 在工程中启用 二、定义读取外部json文件的函数,参考我之前的文章 ue5读取外部文件_艺菲的博客-CSDN博客 三、读取文件并解析为json对象 这里Load Text就是自己定义的函数,ResourceBundle为一个字符串常量,通常是读取的文件夹…...
Vue中的插槽--组件复用,内容自定义
插槽 文章目录 插槽插槽-默认插槽插槽-后备内容(设置默认值)插槽-具名插槽插槽–作用域插槽 插槽-默认插槽 作用:让组件内部的一些结构支持自定义 需求:要在页面中显示一个对话框,封装成一个组件(对话框有很多功能是类…...
完全指南:mv命令用法、示例和注意事项 | Linux文件移动与重命名
文章目录 mv命令使用指南1. 简介什么是mv命令?mv命令的作用和功能是什么? 2. 基本用法基本语法格式如何移动文件?如何重命名文件?如何移动和重命名目录? 3. 高级用法使用通配符进行批量移动和重命名使用选项进行文件移…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
