当前位置: 首页 > news >正文

springboot集成websocket全全全!!!

一、界面展示

二、前置了解

1.什么是websocket

WebSocket是一种在单个TCP连接上进行全双工通信的持久化协议。
全双工协议就是客户端可以给我们服务器发数据 服务器也可以主动给客户端发数据。

2.为什么有了http协议 还要websocket 协议

http协议是一种无状态,非持久化的单全双工应用层协议。
主要用于一问一答的方式交付信息,即客户端发送请求,服务器返回响应。这种模式适合于获取数据或者提交数据的场景。

所以http协议中,服务器无法主动给客户端发送数据,导致出现服务器数据状态发生改变,客户端无法感知。

针对上面的问题,http 勉强可以通过 定时轮询 和 长轮询 解决问题。

定时轮询:客户端不断地定时请求服务器, 询问数据状态变更的情况。
定时轮询的弊端:存在延时,浪费服务器资源和带宽,存在大量无效请求。

长轮询:拉长请求时间,客户端发送请求后,服务器在没有新数据时不会立即响应,而是等到有新数据时才返回响应。这种方法可以减少无效的请求,
长轮询的弊端:仍然需要频繁地建立和断开连接,且服务器需要维护未完成的请求,这可能会占用大量的服务器资源。

承上启下 所有最后我们websocket应运而生,它就是为了解决这个问题而设计的。
WebSocket协议可以实现全双工通信,即客户端和服务器可以在任何时候 相互 主动发送数据。此外,一旦WebSocket连接建立,客户端和服务器之间的连接将保持活动状态,直到被任何一方关闭。

三、附集成代码

1.引入pom依赖
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
2.使用@ServerEndpoint创建WebSocket Endpoint
package com.ruoyi.framework.websocket;import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.WebSocketConfig;
import com.ruoyi.framework.web.service.TokenService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;import org.slf4j.LoggerFactory;/*** @author qujingye* @Classname WebSocketServer* @Description  虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean* @Date 2023/12/19 16:11*/
@Component
@ServerEndpoint(value = "/websocket/message", configurator = WebSocketConfig.class)
public class WebSocketServer {private static TokenService tokenService;@Autowiredprivate void setOriginMessageSender(TokenService tokenService) {WebSocketServer.tokenService = tokenService;}/*** WebSocketServer 日志控制器*/private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);private final static ConcurrentHashMap<Long, CopyOnWriteArrayList<Session>> sessionPool = new ConcurrentHashMap<>();private final static AtomicLong atomicLong = new AtomicLong(0L);/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session) throws Exception {Long userId = parseUserId(session);System.out.println(userId);LOGGER.info("[WebSocket] 有新的连接, 当前用户id: {}", userId);if (userId == null) {return;}CopyOnWriteArrayList<Session> sessions = sessionPool.get(userId);//不存在其他人登陆if (null == sessions) {sessions = new CopyOnWriteArrayList<>();}sessions.add(session);sessionPool.put(userId, sessions);atomicLong.getAndIncrement();LOGGER.info("[WebSocket] 有新的连接, 当前连接数: {}", atomicLong.get());}/*** 连接关闭时处理*/@OnClosepublic void onClose(Session session) {Long userId = parseUserId(session);if (userId == null) {return;}CopyOnWriteArrayList<Session> sessions = sessionPool.remove(userId);CopyOnWriteArrayList<Session> newSessions = new CopyOnWriteArrayList<>();for (Session s : sessions) {if (!s.getId().equals(session.getId())) {newSessions.add(s);}}sessionPool.put(userId, newSessions);atomicLong.getAndDecrement();LOGGER.info("[WebSocket] 连接断开, 当前连接数: {}", atomicLong.get());}/*** 抛出异常时处理*/@OnErrorpublic void onError(Session session, Throwable exception) throws Exception {LOGGER.error("用户错误:,原因:" + exception.getMessage());}/*** 服务器接收到客户端消息时调用的方法*/@OnMessagepublic void onMessage(String message, Session session) {//把收到的消息发回去session.getAsyncRemote().sendText(message);LOGGER.info("message: {}", message);}/*** 给该用户id的全部发送消息*/public void sendMessage(Long userId, String message) {CopyOnWriteArrayList<Session> sessions = sessionPool.get(userId);if (null == sessions || sessions.size() == 0) {return;}sessions.forEach(s -> s.getAsyncRemote().sendText(message));}/*** 获取用户id*/private Long parseUserId(Session session) {String token = (String) session.getUserProperties().get(WebSocketConfig.WEBSOCKET_PROTOCOL);if (StringUtils.isNotEmpty(token)) {LoginUser loginUser = tokenService.getLoginUserByToken(token);if (loginUser != null) {return loginUser.getUserId();}}return null;}
}
3.定义WebSocketConfig

注入ServerEndpointExporter来自动注册端点

package com.ruoyi.framework.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;/*** @author qujingye* @Classname WebSocketConfig* @Description 继承服务器断点配置类* @Date 2023/12/19 16:08*/
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {/*** WebSocket的协议头*/public final static String WEBSOCKET_PROTOCOL = "Sec-Websocket-Protocol";/*** 注入ServerEndpointExporter,这个Bean会自动注册使用了@ServerEndpoint注解声明的WebSocket Endpoint。*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}/*** 建立握手时,连接前的操作*/@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {// 这个用户属性userProperties 可以通过 session.getUserProperties()获取final Map<String, Object> userProperties = sec.getUserProperties();Map<String, List<String>> headers = request.getHeaders();List<String> protocol = headers.get(WEBSOCKET_PROTOCOL);// 存放自己想要的header信息if (protocol != null) {userProperties.put(WEBSOCKET_PROTOCOL, protocol.get(0));}}/*** 创建端点实例,也就是被@ServerEndpoint所标注的对象*/@Overridepublic <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {return super.getEndpointInstance(clazz);}}
4.定义过滤器设置响应头
package com.ruoyi.framework.security.filter;import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.WebSocketConfig;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** FileName:     com.admin.security.filter WebsocketFilter* Date:         2023/8/1 16:42** @author Messylee*/
@Order(1)
@Component
@WebFilter(filterName = "WebsocketFilter", urlPatterns = "/ws/**")
public class WebsocketFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) servletResponse;HttpServletRequest headers = (HttpServletRequest) servletRequest;String token = headers.getHeader(WebSocketConfig.WEBSOCKET_PROTOCOL);if (StringUtils.isNotEmpty(token)){response.setHeader(WebSocketConfig.WEBSOCKET_PROTOCOL, token);}filterChain.doFilter(servletRequest, servletResponse);}}
5.vue前端界面代码

<template><div class="app-container home"><el-row :gutter="20"><el-col :sm="24" :lg="24"><h1>集成websocket测试</h1></el-col></el-row><el-row :gutter="20"><el-col :sm="24" :lg="24"><div><el-input v-model="url" type="text" style="width: 20%" /> &nbsp;&nbsp;<el-button @click="join" type="primary">连接</el-button><el-button @click="exit" type="danger">断开</el-button><el-button @click="resetForm" type="success">重置</el-button><br /><br /><el-input type="textarea" v-model="message" :rows="9" /><br /><br /><el-button type="success" @click="send">发送消息</el-button><br /><br />返回内容<el-input type="textarea" v-model="text_content" :rows="9" /><br /><br /></div></el-col></el-row></div>
</template><script>
import { getToken } from "@/utils/auth";export default {name: "Index",data() {return {url: "ws://127.0.0.1:8080/websocket/message",message: "",text_content: "",ws: null,headers: {Authorization: "Bearer " + getToken(),},};},methods: {join() {const wsuri = this.url;// this.ws = new WebSocket(wsuri);this.ws = new WebSocket(wsuri, [getToken()]);const self = this;// 连接成功后调用this.ws.onopen = function (event) {self.text_content = self.text_content + "WebSocket连接成功!" + "\n";};this.ws.onerror = function (event) {self.text_content = self.text_content + "WebSocket连接发生错误!" + "\n";};// 接收后端消息this.ws.onmessage = function (event) {self.text_content = self.text_content + event.data + "\n";};// 关闭连接时调用this.ws.onclose = function (event) {self.text_content = self.text_content + "已经关闭连接!" + "\n";};},exit() {if (this.ws) {this.ws.close();this.ws = null;}},send() {if (this.ws) {this.ws.send(this.message);} else {alert("未连接到服务器");}},//重置resetForm() {this.message = "";this.text_content = "";},},
};
</script>

相关文章:

springboot集成websocket全全全!!!

一、界面展示 二、前置了解 1.什么是websocket WebSocket是一种在单个TCP连接上进行全双工通信的持久化协议。 全双工协议就是客户端可以给我们服务器发数据 服务器也可以主动给客户端发数据。 2.为什么有了http协议 还要websocket 协议 http协议是一种无状态&#xff0c;非…...

SpringMVC:整合 SSM 中篇

文章目录 SpringMVC - 04整合 SSM 中篇一、优化二、总结三、说明注意&#xff1a; SpringMVC - 04 整合 SSM 中篇 一、优化 在 spring-dao.xml 中配置 dao 接口扫描&#xff0c;可以动态地实现 dao 接口注入到 Spring 容器中。 优化前&#xff1a;手动创建 SqlSessionTempl…...

oracle即时客户端(Instant Client)安装与配置

之前的文章记录了oracle客户端和服务端的下载与安装&#xff0c;内容参见&#xff1a; 在Windows中安装Oracle_windows安装oracle 如果不想安装oracle客户端&#xff08;或者是电脑因为某些原因无法安装oracle客户端&#xff09;&#xff0c;还想能够连接oracle远程服务&#…...

POP3协议详解

基本介绍 POP3是一种用于从邮件服务器获取电子邮件的协议。它允许邮件客户端连接到邮件服务器&#xff0c;检索服务器上存储的邮件&#xff0c;并将邮件下载到客户端设备上。POP3的工作原理如下&#xff1a; 连接和身份验证&#xff1a; 邮件客户端通过TCP/IP连接到邮件服务器…...

电子病历编辑器源码,提供电子病历在线制作、管理和使用的一体化电子病历解决方案

概述&#xff1a; 电子病历是指医务人员在医疗活动过程中,使用医疗机构信息系统生成的文字、符号、图表、图形、数据、影像等数字化信息,并能实现存储、管理、传输和重现的医疗记录,是病历的一种记录形式。 医院通过电子病历以电子化方式记录患者就诊的信息&#xff0c;包括&…...

WT2605C高品质音频蓝牙语音芯片:外接功放实现双声道DAC输出的优势

在音频处理领域&#xff0c;双声道DAC输出能够提供更为清晰、逼真的音效&#xff0c;增强用户的听觉体验。针对这一需求&#xff0c;唯创知音的WT2605C高品质音频蓝牙语音芯片&#xff0c;通过外接功放实现双声道DAC输出&#xff0c;展现出独特的应用优势。 一、高品质音频处理…...

IntelliJ IDEA 2023.3 最新版如何如何配置?IntelliJ IDEA 2023.3 最新版试用方法

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

如何查看内存卡使用记录-查看的设备有:U盘、移动硬盘、MP3、SD卡等-供大家学习研究参考

主要功能 USB Viewer&#xff08;USB移动存储设备使用记录查看器&#xff09;可用于查看本机的USB移动存储设备使用记录。可查看的设备有&#xff1a;U盘、移动硬盘、MP3、SD卡……等。   可用于兵器、航空、航天、政府、军队等对保密要求较高的单位&#xff0c;可在计算机保…...

九、W5100S/W5500+RP2040之MicroPython开发<HTTPOneNET示例>

文章目录 1. 前言2. 平台操作流程2.1 创建设备2.2 创建数据流模板 3. WIZnet以太网芯片4. 示例讲解以及使用4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 烧录验证 5. 注意事项6. 相关链接 1. 前言 在这个智能硬件和物联网时代&#xff0c;MicroPython和树莓派PICO正…...

在 Laravel 中,清空缓存大全

在 Laravel 中&#xff0c;清空缓存通常涉及到清除应用程序中的缓存文件和数据库查询缓存。以下是一些常用的清空缓存方法&#xff1a; 1. 清除路由缓存&#xff1a; Laravel 的路由缓存可以提高应用程序的性能&#xff0c;但在开发过程中&#xff0c;你可能需要频繁地更改路…...

【贪心】单源最短路径Python实现

文章目录 [toc]问题描述Dijkstra算法Dijkstra算法的正确性贪心选择性质最优子结构性质 Dijkstra算法应用示例时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;贪心算法 问题描述 给定一个带权有向图 G ( V , E ) G (V , E) G(V,E)&#xff0c;其中每…...

Spark Shell的简单使用

简介 Spark shell是一个特别适合快速开发Spark原型程序的工具&#xff0c;可以帮助我们熟悉Scala语言。即使你对Scala不熟悉&#xff0c;仍然可以使用这个工具。Spark shell使得用户可以和Spark集群交互&#xff0c;提交查询&#xff0c;这便于调试&#xff0c;也便于初学者使用…...

Springsecurty【2】认证连接MySQL

1.前期准备 基于Spring Initializr创建SpringBoot项目&#xff08;基于SpringBoot 2.7.12版本&#xff09;&#xff0c;实现与MyBatisPlus的项目整合。分别导入&#xff1a;CodeGenerator和MyBatisPlusConfig。 CodeGenerator&#xff1a;用于MybatisPlus代码生成&#xff1b;…...

.Net 访问电子邮箱-LumiSoft.Net,好用

序言&#xff1a; 网上找了很多关于.Net如何访问电子邮箱的方法&#xff0c;但是大多数都达不到想要的需求&#xff0c;只有一些 收发邮件。因此 花了很大功夫去看 LumiSoft.Net.dll 的源码&#xff0c;总算做出自己想要的结果了&#xff0c;果然学习诗人进步。 介绍&#xff…...

谷粒商城-商品服务-新增商品功能开发(商品图片无法展示问题没有解决)

在网关配置路由 - id: member_routeuri: lb://gulimemberpredicates:- Path/api/gulimember/**filters:- RewritePath/api/(?<segment>.*),/$\{segment}并将所有逆向生成的工程调式出来 获取分类关联的品牌 例如&#xff1a;手机&#xff08;分类&#xff09;-> 品…...

Open3D 点云数据处理基础(Python版)

Open3D 点云数据处理基础&#xff08;Python版&#xff09; 文章目录 1 概述 2 安装 2.1 PyCharm 与 Python 安装 2.3 Anaconda 安装 2.4 Open3D 0.13.0 安装 2.5 新建一个 Python 项目 3 点云读写 4 点云可视化 2.1 可视化单个点云 2.2 同一窗口可视化多个点云 2.3…...

使用vue-qr,报错in ./node_modules/vue-qr/dist/vue-qr.js

找到node_modules—>vue-qr/dist/vue-qr.js文件&#xff0c;搜…e,将…去掉&#xff0c;然后重新运行项目。...

百川2大模型微调问题解决

之前用https://github.com/FlagAlpha/Llama2-Chinese微调过几个模型&#xff0c;总体来说llama2的生态还是比较好的&#xff0c;过程很顺利。微调百川2就没那么顺利了&#xff0c;所以简单做个记录 1. 数据准备&#xff0c;我的数据是单轮对话&#xff0c;之前微调llama2已经按…...

MySQL的事务-原子性

MySQL的事务处理具有ACID的特性&#xff0c;即原子性&#xff08;Atomicity)、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09;。 1. 原子性指的是事务中所有操作都是原子性的&#xff0c;要…...

D3839|完全背包

完全背包&#xff1a; 首先01背包的滚动数组中的解法是内嵌的循环是从大到小遍历&#xff0c;为了保证每个物品仅被添加一次。 for(int i 0; i < weight.size(); i) { // 遍历物品for(int j bagWeight; j > weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...