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

Spring Boot+Vue实现Socket通知推送

目录

Spring Boot端

第一步,引入依赖

第二步,创建WebSocket配置类

第三步,创建WebSocket服务

第四步,创建Controller进行发送测试

Vue端

第一步,创建连接工具类

第二步,建立连接

​编辑

第三步,监听服务器发送过来的消息

第四步,关闭连接 


Spring Boot端

第一步,引入依赖

首先我们需要引入WebSocket所需的依赖,以及处理输出格式的依赖

<!--格式转换-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version>
</dependency>
<!--WebSocket依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

第二步,创建WebSocket配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author: tjp* @create: 2023-04-03 09:58* @Description: WebSocket配置*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

第三步,创建WebSocket服务

这一步我们通过userId作为标识符,区分系统中对应的用户,后续也可基于此,进行其他的操作步骤。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.excel.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
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.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;/*** @author: tjp* @create: 2023-04-03 13:55* @Description: WebSocket服务*/@ServerEndpoint("/websocket/{userId}")
@Slf4j
@Component
public class WebSocketServer {/*** 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/private static int onlineCount = 0;/*** concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();/*** 与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 接收userId*/private String userId = "";/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);//加入set中} else {webSocketMap.put(userId, this);//加入set中addOnlineCount();//在线数加1}log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());try {HashMap<Object, Object> map = new HashMap<>();map.put("key", "连接成功");sendMessage(JSON.toJSONString(map));} catch (IOException e) {log.error("用户:" + userId + ",网络异常!!!!!!");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);//从set中删除subOnlineCount();}log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {log.info("用户消息:" + userId + ",报文:" + message);//可以群发消息//消息保存到数据库、redisif (StringUtils.isNotBlank(message)) {try {//解析发送的报文JSONObject jsonObject = JSONObject.parseObject(message);//追加发送人(防止串改)jsonObject.put("fromUserId", this.userId);String fromUserId = jsonObject.getString("fromUserId");//传送给对应toUserId用户的websocketif (StringUtils.isNotBlank(fromUserId) && webSocketMap.containsKey(fromUserId)) {webSocketMap.get(fromUserId).sendMessage(jsonObject.toJSONString());//自定义-业务处理//                    DeviceLocalThread.paramData.put(jsonObject.getString("group"),jsonObject.toJSONString());} else {log.error("请求的userId:" + fromUserId + "不在该服务器上");//否则不在这个服务器上,发送到mysql或者redis}} catch (Exception e) {e.printStackTrace();}}}/*** 发生错误时候** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());error.printStackTrace();}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {//加入线程锁synchronized (session) {try {//同步发送信息this.session.getBasicRemote().sendText(message);} catch (IOException e) {log.error("服务器推送失败:" + e.getMessage());}}}/*** 发送自定义消息* *//*** 发送自定义消息** @param message  发送的信息* @param toUserId 如果为null默认发送所有* @throws IOException*/public static void sendInfo(String message, String toUserId) throws IOException {//如果userId为空,向所有群体发送if (StringUtils.isEmpty(toUserId)) {//向所有用户发送信息Iterator<String> itera = webSocketMap.keySet().iterator();while (itera.hasNext()) {String keys = itera.next();WebSocketServer item = webSocketMap.get(keys);item.sendMessage(message);}}//如果不为空,则发送指定用户信息else if (webSocketMap.containsKey(toUserId)) {WebSocketServer item = webSocketMap.get(toUserId);item.sendMessage(message);} else {log.error("请求的userId:" + toUserId + "不在该服务器上");}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount--;}public static synchronized ConcurrentHashMap<String, WebSocketServer> getWebSocketMap() {return WebSocketServer.webSocketMap;}}

第四步,创建Controller进行发送测试

获取当前在线人数

import com.......WebSocketServer;@ApiOperation(value = "获取当前在线人数")
@GetMapping("/getOnlineCount")
public Integer getOnlineCount() {return WebSocketServer.getOnlineCount();
}

通过接口,向前端用户推送消息

import com.......WebSocketServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;/*** @author: tjp* @create: 2023-04-03 13:57* @Description: 测试*/
@RestController
@RequestMapping("/news")
public class NewsController {@GetMapping("/send")public String send() {try {WebSocketServer.sendInfo("这是websocket发送过来的消息!", "需要推送的用户的编号");} catch (IOException e) {throw new RuntimeException(e);}return "发送消息成功";}}

Vue端

第一步,创建连接工具类

创建工具类websocket.js,这里的userId就是用来作为标识符的userId

/*** @author: tjp* @create: 2023-04-03 11:22* @Description: Socket客户端*/
export class WebSocketClient {constructor(userId) {this.userId = userId;this.websocket = null;this.timeout = 10000; // 心跳超时时间,单位msthis.timeoutObj = null; // 心跳定时器this.serverTimeoutObj = null; // 服务器超时定时器this.lockReconnect = false; // 避免重复连接this.timeoutnum = null; // 重连延迟定时器}// 初始化WebSocket连接initWebSocket() {let wsUrl = `ws://127.0.0.1:8080/websocket/${this.userId}`;this.websocket = new WebSocket(wsUrl);this.websocket.onopen = this.websocketonopen.bind(this);this.websocket.onerror = this.websocketonerror.bind(this);this.websocket.onmessage = this.setOnmessageMessage.bind(this);this.websocket.onclose = this.websocketclose.bind(this);// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = this.websocketclose.bind(this);}// 启动心跳start() {console.log('start');// 清除延时器this.timeoutObj && clearTimeout(this.timeoutObj);this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);/*// 向服务器发送心跳消息let actions = { "test": "12345" };this.websocket && this.websocket.readyState == 1 && this.websocket.send(JSON.stringify(actions));// 启动心跳定时器this.timeoutObj = setTimeout(() => {this.start();// 定义一个延时器等待服务器响应,若超时,则关闭连接,重新请求server建立socket连接this.serverTimeoutObj = setTimeout(() => {this.websocket.close();}, this.timeout)}, this.timeout)*/}// 重置心跳reset() {// 清除时间clearTimeout(this.timeoutObj);clearTimeout(this.serverTimeoutObj);// 重启心跳this.start();}// 重新连接reconnect() {if (this.lockReconnect) return;this.lockReconnect = true;// 没连接上会一直重连,设置延迟避免请求过多this.timeoutnum && clearTimeout(this.timeoutnum);this.timeoutnum = setTimeout(() => {this.initWebSocket();this.lockReconnect = false;}, 5000)}// 处理收到的消息async setOnmessageMessage(event) {console.log(event.data, '获得消息');// 重置心跳// this.reset();// 自定义全局监听事件window.dispatchEvent(new CustomEvent('onmessageWS', {detail: {data: event.data}}))// //发现消息进入    开始处理前端触发逻辑// if (event.data === 'success' || event.data === 'heartBath') return}// WebSocket连接成功回调websocketonopen() {// 开启心跳this.start();console.log("WebSocket连接成功!!!" + new Date() + "----" + this.websocket.readyState);clearInterval(this.otimer);//停止}// WebSocket连接错误回调websocketonerror(e) {console.log("WebSocket连接发生错误" + e);}// WebSocket连接关闭回调websocketclose(e) {this.websocket.close();clearTimeout(this.timeoutObj);clearTimeout(this.serverTimeoutObj);console.log("websocketcloe关闭连接")}// 关闭WebSocket连接closeWebSocket() {this.websocket.close();console.log("closeWebSocket关闭连接")}// 监听窗口关闭事件onbeforeunload() {this.closeWebSocket();}
}

第二步,建立连接

在任意你想建立连接的页面中建立Socket连接

比如,在用户点击登录按钮之后

在这里可以使用原型,创建连接对象,并启动连接

<script>
import Vue from "vue";
import {WebSocketClient} from "@/utils/websocket";
......
......
methods:{handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = truethis.$store.dispatch('user/login', this.loginForm).then(() => {this.$router.push({path: this.redirect || '/'})this.loading = false/*-----------在此处放入原型中------------*/Vue.prototype.$WebSocketClientInstance = new WebSocketClient('t');Vue.prototype.$WebSocketClientInstance.initWebSocket()/*-----------------end------------*/}).catch(() => {this.loading = false})} else {this.$message({message: '请填写正确格式的用户名或密码', type: 'error'})return false}})}    
}.....
.....
</script>

第三步,监听服务器发送过来的消息

在你想监听的页面,使用监听器进行监听

<script>
....
....
mounted() {// 添加socket通知监听window.addEventListener('onmessageWS', this.getSocketData)
},
methods: {// 收到消息处理getSocketData(res) {console.log(res.detail)console.log("llll")},
}....
....
</script>

这个时候,你就可以通过后端的接口进行发送了

 搞个测试

 

第四步,关闭连接 

搞个按钮

<template><div><button @click="closeConnect">关闭连接</button></div>
</template><script>
import {WebSocketClient} from "@/utils/websocket";
import Vue from "vue";export default {methods: {closeConnect() {console.dir(Vue.prototype)Vue.prototype.$WebSocketClientInstance.closeWebSocket();},}
}
</script>

 

相关文章:

Spring Boot+Vue实现Socket通知推送

目录 Spring Boot端 第一步&#xff0c;引入依赖 第二步&#xff0c;创建WebSocket配置类 第三步&#xff0c;创建WebSocket服务 第四步&#xff0c;创建Controller进行发送测试 Vue端 第一步&#xff0c;创建连接工具类 第二步&#xff0c;建立连接 ​编辑 第三步&a…...

python---python介绍

python介绍 1.1介绍 1.1.1为什么学习 1.1.2什么是python 优雅简单易学1.1.3在线2进制转换 在线二进制转文本工具 - 转换 1.1.4python的安装和配置 1.需要配置对应的环境变量。可以设置多个。 默认全选 设置安装的路径 最后安装完成即可。 验证&#xff1a;python 如何退出 1.1.…...

第十四届蓝桥杯大赛——真题训练第10天

目录 第一题&#xff1a;扫雷 题目描述 输入描述 输出描述 输入输出样例 运行限制 题目代码 第 2 题&#xff1a;完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 题目分析 题目代码 第三题&#xff1a;求阶乘 问题描述…...

3.29~3.30学习总结

刷题情况&#xff1a; 蓝桥杯刷题&#xff1a; Java学习情况: 抽象类&#xff1a;如果将子类中抽取的共性行为&#xff08;方法&#xff09;&#xff0c;子类的执行不一样的时候 &#xff08;通俗一点来说&#xff0c;就是无法找到一个万能的方法体供子类使用&#xff0c;但这…...

结构体详解 ——— C语言

目录 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体实现位段&#xff08;位段的填充&可移植性&#xff09; 位段的内存分配 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对…...

Java SE 基础(4) Java的语言特点

语言特点 Java是一门面向对象编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表&#xff0c;极好地实现了面向对象理论&a…...

都炸店了,拼多多还在坚持什么

子超这两天听说了拼多多被“炸店”事件&#xff0c;第一反应是震惊&#xff1a;这都什么年代了&#xff0c;还有这种不择手段的暴力行为&#xff1f;所谓的炸店&#xff0c;就是一些人员被煽动和组织起来&#xff0c;有预谋地对店铺发起打砸行动&#xff0c;这和线下去打砸商铺…...

vue尚品汇商城项目-day01【6.Footer组件的显示与隐藏】

文章目录6.Footer组件的显示与隐藏6.1我们可以根据组件身上的$route获取当前路由的信息&#xff0c;通过路由路径判断Footer显示与隐藏6.2配置路由的时候&#xff0c;可以给路由添加元信息[meta]&#xff0c;路由需要配置对象&#xff0c;它的key不能乱接、瞎写、胡写&#xff…...

命令行上的数据科学第二版 一、简介

原文&#xff1a;https://datascienceatthecommandline.com/2e/chapter-1-introduction.html 贡献者&#xff1a;Ting-xin 这本书是关于如何利用命令行做数据科学的。我的目标是通过教你使用命令行的力量&#xff0c;让自己成为一名更高效和多产的数据科学家。 在标题中同时使…...

utf-8转换到utf-16的转换过程你懂吗?

人生自是有情痴&#xff0c;此恨不关风与月。——唐代元稹《离思》 从UTF-8编码的文件中读取文本并将其存储到Java的String对象中&#xff0c;涉及到从字节序列到Unicode码点&#xff0c;再到UTF-16编码的转换。以下是详细的步骤&#xff1a; 从文件读取字节序列&#xff1a;首…...

C++编程大师之路:从入门到精通--持续更新中~

文章目录前言主要内容C基础入门初识C第一个C程序注释变量常量关键字标识符命名规则数据类型整型sizeof关键字实型&#xff08;浮点型&#xff09;字符型转义字符字符串型布尔类型 bool数据的输入运算符算术运算符赋值运算符比较运算符逻辑运算符程序流程结构选择结构if语句三目…...

面试阿里软件测试岗,收到offer后我却毫不犹豫拒绝了....

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是2年。我想说的是&#xff0c;但凡有点机会&#xff0c;千万别去外包&#xff01; 在深思熟虑过后&am…...

【c语言多线程编程】关于pthread_create()和pthread_join()的多线程详解

关于pthread_create()和pthread_join()的多线程详解 一、首先说一下pthread_create() 函数的用法&#xff1a; int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);各参数的含义&#xff1a; 1、pthread_t *thre…...

抖音seo矩阵系统源码搭建技术+二开开源代码定制部署

抖音已经成为了当今最为流行的短视频平台之一&#xff0c;拥有着庞大的用户群体和海量的视频资源。对于一些商家或者运营者来说&#xff0c;如何从这些视频资源中挖掘出有效的信息&#xff0c;进而提升自己的品牌、产品或者内容的曝光度&#xff0c;就成为了一个非常重要的问题…...

【周赛刷题】平衡树+图中最短环

2612. 最少翻转操作数&#xff08;平衡树&#xff09; 题目的难度有一部分在于数学推导。对于某个点 iii 进行反转是有一个范围的&#xff0c;这个范围需要考虑到边界的情况。可以的得到的一个结论是。对于窗口反转&#xff0c;KaTeX parse error: Expected group after ^ at p…...

C++笔记——第十篇 继承 的解析,详细易懂哦

目录 一、继承的概念及定义 1.继承的概念 2. 继承定义 2.1定义格式 2.2继承关系和访问限定符 2.3继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承…...

SQL Server中的全文搜索

SQL Server中的全文搜索一、概述二、全文搜索查询三、将全文搜索查询与 LIKE 谓词进行比较四、全文搜索体系结构4.1、SQL Server 进程4.2、过滤器守护程序主机进程五、全文搜索处理5.1、全文索引过程5.2、全文查询流程六、全文索引体系结构6.1、全文索引结构6.2、全文索引片段6…...

自适应平移混音方法

一、简介&#xff1a; 自适应平移混音方法是一种常见的音频混音技术&#xff0c;它利用自适应滤波器对不同音频信号进行平移和加权&#xff0c;从而实现混音。 二、该方法的基本步骤如下&#xff1a; 采集和存储需要混音的音频信号。 对其中一个音频信号进行预处理&#xff0c…...

炼钢厂VR职业技能实训软件,提高员工学习效率和掌握技能速度

炼钢作业是一个高危、高压、高温的行业&#xff0c;在实际操作中需要严格遵守安全规范和操作规程&#xff0c;一旦出现差错可能造成巨大的经济损失和人员伤亡。 利用广州华锐互动开发的炼钢厂VR职业技能实训软件&#xff0c;可以有效帮助员工更好地理解和掌握炼钢作业中的相关…...

MySQL数据库范式

文章目录MySQL数据库范式1、范式的优缺点2、第一范式3、第二范式4、第三范式5、BC范式6、第四范式MySQL数据库范式 1、范式的优缺点 应用数据库范式的好处&#xff1a; 减少数据冗余&#xff08;这是最主要的好处&#xff0c;其他好处都是由此而附带的&#xff09;消除异常&…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...