SpringBoot:websocket 实现后端主动前端推送数据
简单说明下websocket实用场景。
- 实时通信领域:
- 社交聊天弹幕
- 多玩家游戏
- 协同编辑
- 股票基金实时报价
- 体育实况更新
- 视频会议/聊天
- 基于位置的应用
- 在线教育
- 智能家居等需要高实时性的场景
一、服务端代码
pom.xml:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.1.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.22</version></dependency></dependencies>
config:
package com.king.websocket.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/**
* <b>Function: </b> todo
* @program: WebSocketConfig
* @Package: com.king.websocket.config
* @author: dingcho
* @date: 2025/01/20
* @version: 1.0
* @Copyright: 2025 www.kingbal.com Inc. All rights reserved.
*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}
socket核心代码 - WebSocketServer:
package com.king.websocket.server;import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.stereotype.Component;import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;/*** <b>Function: </b> todo** @program: WebSocketServer* @Package: com.king.websocket.server* @author: dingcho* @date: 2025/01/20* @version: 1.0* @Copyright: 2025 www.kingbal.com Inc. All rights reserved.*/
@ServerEndpoint("/dev-api/websocket/{userId}")
@Component
public class WebSocketServer {static Log log = LogFactory.get(WebSocketServer.class);// 静态变量,用来记录当前在线连接数private static int onlineCount = 0;// 存放每个客户端对应的MyWebSocket对象private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();// 与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;// 接收userIdprivate String userId = "";/*** 连接建立成功调用的方法* @param session* @param userId*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);webSocketMap.put(userId, this);} else {webSocketMap.put(userId, this);addOnlineCount();}log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());try {sendMessage("连接成功");} catch (IOException e) {log.error("用户:" + userId + ",网络异常!!!!!!");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);//从set中删除subOnlineCount();}log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());}/*** 收到客户端消息后调用的方法* @param message 客户端发送过来的消息* @param session*/@OnMessagepublic void onMessage(String message, Session session) {log.info("用户消息:" + userId + ",报文:" + message);//可以群发消息//消息保存到数据库redisif (!StrUtil.isEmpty(message)) {try {//解析发送的报文JSONObject jsonObject = JSON.parseObject(message);} catch (Exception e) {log.error("用户:" + userId + ", 接收报文异常!!!!!!");}}}/*** 会话异常* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}/*** 实现服务器主动推送*/public static void sendAllMessage(String message) throws IOException {ConcurrentHashMap.KeySetView<String, WebSocketServer> userIds = webSocketMap.keySet();for (String userId : userIds) {WebSocketServer webSocketServer = webSocketMap.get(userId);webSocketServer.session.getBasicRemote().sendText(message);System.out.println("webSocket实现服务器主动推送成功 userId >> " + userId);}}/*** 发送自定义消息*/public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {log.info("发送消息到:" + userId + ",报文:" + message);if (!StrUtil.isEmpty(message) && webSocketMap.containsKey(userId)) {webSocketMap.get(userId).sendMessage(message);} else {log.error("用户" + userId + ",不在线!");}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount--;}}
定时器 - WebSocketController:
package com.king.websocket.controller;import com.alibaba.fastjson2.JSONObject;
import com.king.websocket.server.WebSocketServer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;/*** <b>Function: </b> todo** @program: WebSocketController* @Package: com.king.websocket.controller* @author: dingcho* @date: 2025/01/20* @version: 1.0* @Copyright: 2025 www.kingbal.com Inc. All rights reserved.*/
@RestController
@RequestMapping("/message")
public class WebSocketController {// 设置定时十秒一次@Scheduled(cron = "0/10 * * * * ?")@PostMapping("/send")public String sendMessage() throws Exception {Map<String, Object> map = new HashMap<>();// 获取当前日期和时间LocalDateTime nowDateTime = LocalDateTime.now();DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");System.out.println(dateTimeFormatter.format(nowDateTime));map.put("server_time", dateTimeFormatter.format(nowDateTime));map.put("server_code", "200");map.put("server_message", "服务器消息来了!!!");JSONObject jsonObject = new JSONObject(map);WebSocketServer.sendAllMessage(jsonObject.toString());return jsonObject.toString();}}
启动项 - WebsocketApplication:
package com.king.websocket;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;/*** <b>Function: </b> todo** @program: WebsocketApplication* @Package: com.king.websocket* @author: dingcho* @date: 2025/01/20* @version: 1.0* @Copyright: 2025 www.kingbal.com Inc. All rights reserved.*/
@EnableScheduling
@ServletComponentScan
@SpringBootApplication
public class WebsocketApplication {public static void main(String[] args) {SpringApplication.run(WebsocketApplication.class, args);}}
然后启动项目使用 在线websocket测试-在线工具-postjson 进行测试

直接发送信息服务端,服务端也会接收到信息

二、前端
公共js:WebsocketTool.js
//在JavaScript中实现WebSocket连接失败后3分钟内尝试重连3次/*** @param {string} url Url to connect* @param {number} maxReconnectAttempts Maximum number of times* @param {number} reconnect Timeout* @param {number} reconnectTimeout Timeout**/
class WebSocketReconnect {constructor(url, maxReconnectAttempts = 3, reconnectInterval = 20000, maxReconnectTime = 180000) {this.url = urlthis.maxReconnectAttempts = maxReconnectAttemptsthis.reconnectInterval = reconnectIntervalthis.maxReconnectTime = maxReconnectTimethis.reconnectCount = 0this.reconnectTimeout = nullthis.startTime = nullthis.socket = nullthis.connect()}// 连接操作connect() {console.log('connecting...')this.socket = new WebSocket(this.url)// 连接成功建立的回调方法this.socket.onopen = () => {console.log('WebSocket Connection Opened!')this.clearReconnectTimeout()this.reconnectCount = 0}// 连接关闭的回调方法this.socket.onclose = (event) => {console.log('WebSocket Connection Closed:', event)this.handleClose()}// 连接发生错误的回调方法this.socket.onerror = (error) => {console.error('WebSocket Connection Error:', error)// 重连this.handleClose()}}//断线重连操作handleClose() {if (this.reconnectCount < this.maxReconnectAttempts && (this.startTime === null || Date.now() - this.startTime < this.maxReconnectTime)) {this.reconnectCount++console.log(`正在尝试重连 (${this.reconnectCount}/${this.maxReconnectAttempts})次...`)this.reconnectTimeout = setTimeout(() => {this.connect()}, this.reconnectInterval)if (this.startTime === null) {this.startTime = Date.now()}} else {console.log('超过最大重连次数或重连时间超时,已放弃连接')// 重置连接次数0this.reconnectCount = 0 // 重置开始时间this.startTime = null }}//清除重连定时器clearReconnectTimeout() {if (this.reconnectTimeout) {clearTimeout(this.reconnectTimeout)this.reconnectTimeout = null}}// 关闭连接close() {if (this.socket && this.socket.readyState === WebSocket.OPEN) {this.socket.close()}this.clearReconnectTimeout()this.reconnectCount = 0this.startTime = null}
}// WebSocketReconnect 类封装了WebSocket的连接、重连逻辑。
// maxReconnectAttempts 是最大重连尝试次数。
// reconnectInterval 是每次重连尝试之间的间隔时间。
// maxReconnectTime 是总的重连时间限制,超过这个时间后不再尝试重连。
// reconnectCount 用于记录已经尝试的重连次数。
// startTime 用于记录开始重连的时间。
// connect 方法用于建立WebSocket连接,并设置相应的事件监听器。
// handleClose 方法在WebSocket连接关闭或发生错误时被调用,根据条件决定是否尝试重连。
// clearReconnectTimeout 方法用于清除之前设置的重连定时器。
// close 方法用于关闭WebSocket连接,并清除重连相关的状态。// 使用示例
// const webSocketReconnect = new WebSocketReconnect('ws://your-websocket-url')
// 当不再需要WebSocket连接时,可以调用close方法
// webSocketReconnect.close();export default WebSocketReconnect
三、需要使用到websocket页面
<template><div><el-input v-model="textarea1" :rows="5" type="textarea" placeholder="请输入" /></div>
</template><script setup>
import { ref, reactive,, onMounted, onUnmounted } from 'vue'
import WebSocketReconnect from '@/util/WebsocketTool'// --------------------------------------------
let textarea1 = ref('【消息】---->')
let websocket = null
// 判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {// 连接WebSocket节点websocket = new WebSocketReconnect('ws://127.0.0.1:8080' + '/dev-api/websocket/123456')
} else {alert('浏览器不支持webSocket')
}// 接收到消息的回调方法
websocket.socket.onmessage = function (event) {let data = event.dataconsole.log('后端传递的数据:' + data)// 数据渲染至页面textarea1.value = textarea1.value + data + '
' + '【消息】---->'
}
// 当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {websocket.close()
}
// 关闭连接
function closeWebSocket() {websocket.close()
}
// 发送消息
function send() {websocket.socket.send({ myRes: 123 })
}//------------------------------------
</script><style scoped></style>
相关文章:
SpringBoot:websocket 实现后端主动前端推送数据
简单说明下websocket实用场景。 实时通信领域:社交聊天弹幕多玩家游戏协同编辑股票基金实时报价体育实况更新视频会议/聊天基于位置的应用在线教育智能家居等需要高实时性的场景 一、服务端代码 pom.xml: <dependencies><dependency><…...
嵌入式硬件篇---PID控制
文章目录 前言第一部分:连续PID1.比例(Proportional,P)控制2.积分(Integral,I)控制3.微分(Derivative,D)控制4.PID的工作原理5..实质6.分析7.各种PID控制器P控…...
小程序获取微信运动步数
1、用户点击按钮,在小程序中触发getuserinfo方法,获取用户信息 <scroll-view class"scrollarea" scroll-y type"list"><view class"container"><button bind:tap"getLogin">获取</button&…...
5G 核心网 相关概念快速入门
在我们开始阅读3GPP协议来学习5G核心网之前, 不妨来看看我之前整理的PPT,快速学习核心网相关概念, 以及5G转发面PFCP协议的相关核心知识。 涵盖了最精简的核心骨干内容,助你轻松上阵。 讲解目标 3GPP和相关协议 5G核心网架构模…...
【2024 年度总结】从小白慢慢成长
【2024 年度总结】从小白慢慢成长 1. 加入 CSDN 的契机2. 学习过程2.1 万事开头难2.2 下定决心开始学习2.3 融入技术圈2.4 完成万粉的目标 3. 经验分享3.1 工具的选择3.2 如何提升文章质量3.3 学会善用 AI 工具 4. 保持初心,继续前行 1. 加入 CSDN 的契机 首次接触…...
SAP POC 项目完工进度 - 收入确认方式【工程制造行业】【新准则下工程项目收入确认】
1. SAP POC收入确认基础概念 1.1 定义与原则 SAP POC(Percentage of Completion)收入确认方式是一种基于项目完工进度来确认收入的方法。其核心原则是根据项目实际完成的工作量或成本投入占预计总工作量或总成本的比例,来确定当期应确认的收…...
vue3+three.js加载glb模型
<template><div><!-- 亮度调节滑块 --><div class"controls"><label for"brightness">背景光亮度:</label><inputtype"range"id"brightness"v-model"brightness"min&quo…...
Golang Gin系列-4:Gin Framework入门教程
在本章中,我们将深入研究Gin,一个强大的Go语言web框架。我们将揭示制作一个简单的Gin应用程序的过程,揭示处理路由和请求的复杂性。此外,我们将探索基本中间件的实现,揭示精确定义路由和路由参数的技术。此外ÿ…...
25西湖ctf
2025西湖冬季 图片不全去我blog找👇 25西湖 | DDLS BLOG 文章所有参考将在文末给出 web web1 ssti 太简单的不赘述,知道用就行 {{cycler.__init__.__globals__.__builtins__[__import__](os).popen($(printf "\150\145\141\144\40\57\146\1…...
AI Agent:AutoGPT的使用方法
AutoGPT的使用方法 准备工作: 安装Python:确保你的电脑上安装了Python 3.8或更高版本。获取OpenAI API密钥:访问https://platform.openai.com/account/api-keys获取API密钥,并保存备用。获取Google API及Google Search Engine ID(可选):若要使用谷歌搜索功能,需访问htt…...
2024年博客之星主题创作|Android 开发:前沿技术、跨领域融合与就业技能展望
目录 引言 一、推动 Android 应用创新的核心力量 1.1 人工智能与机器学习的崛起 1.2 增强现实(AR)与虚拟现实(VR)的应用扩展 1.3 5G技术的推动 1.4 跨平台开发技术的成熟 1.4.1 React Native 1.4.2 Flutter 1.4.3 Taro …...
蓝桥杯小白备考指南
一、了解蓝桥杯 蓝桥杯大赛是工业和信息化部人才交流中心举办的全国性专业信息技术赛事 ,旨在促进软件和信息领域专业技术人才培养,提升高校毕业生的就业竞争力。比赛涵盖多个编程语言组别,如 Java、C/C、Python 等。不同组别和参赛类别&…...
面向对象的程序设计:以对象的方式进行思考
1 理解接口与实现的区别 以上一篇文章的电视机需要插电使用的例子继续来讲解: 对电视而言,插电使用,只需要标准的插座即可,具体的电从哪里来,是火力发电厂,或是太阳能发电,亦或是畜电池逆变供电,电视机是不需要关心的。 发电厂或供电设备属于实现,220V交流电插座属于…...
酵母三杂交实验全解析:从技术到应用【泰克生物】
酵母三杂交实验(Yeast Three-Hybrid, Y3H)是酵母双杂交(Y2H)技术的扩展,专门用于研究更复杂的分子相互作用,尤其是小分子与蛋白质间的相互作用。通过引入小分子作为第三方调节因子,酵母三杂交技…...
Git 分支合并
Merge(合并) Merge 是 Git 中最常用的分支合并方式之一。当你想要将一个分支的更改合并到另一个分支时,你可以使用 Merge 操作。 合并步骤: 通常是从开发分支往主分支上合并代码的时候用 merge 1、git checkout master&#x…...
C# 以管理员方式启动程序全解析
引言 在 Windows 应用程序开发的领域中,C# 语言凭借其强大的功能和广泛的适用性,被众多开发者所青睐。然而,在实际的开发过程里,我们常常会遭遇这样的情况:程序需要访问特定的系统资源,像是系统文件夹、注…...
CSS:语法、样式表、选择器
目录 一、语法 二、创建 外部样式表 内部样式表 内联样式 三、选择器 ID选择器 类选择器 伪类选择器 :hover a:link a:active a:visited 属性选择器 伪元素选择器 ::first-letter ::first-line ::selection ::placeholder ::before 和::after 通配选择器 标…...
python轻量级框架-flask
简述 Flask 是 Python 生态圈中一个基于 Python 的Web 框架。其轻量、模块化和易于扩展的特点导致其被广泛使用,适合快速开发 Web 应用以及构建小型到中型项目。它提供了开发 Web 应用最基础的工具和组件。之所以称为微框架,是因为它与一些大型 Web 框架…...
SQL和MySQL以及DAX的日期表生成?数字型日期?将生成的日期表插入到临时表或者实体表中
几种生成日期表的方法 如何用SQL语句生成日期表呢? 如何用MySQL语句生成日期表呢? 如何用DAX语句生成日期表呢? 1. MySQL生成日期表 1.1 日期格式:yyyy-MM-dd 字符型 2024-01-02 -- 生成日期表 WITH RECURSIVE temp_dateTable …...
文件下载时利用redis的队列模式顺序下载文件,防止多文件任务下载导致OOM
1、controller层控制 Resourceprivate RedissonClient redissonClient;Slf4j Service public class CustomerSettlementExportServiceImpl implements ICustomerSettlementExportService { /*** 文件加入队列顺序导出** param pubFileExportList 参数* return 结果*/public Aja…...
当Windows 10的OneDrive无法彻底卸载时,这个批处理脚本是你的终极解决方案
当Windows 10的OneDrive无法彻底卸载时,这个批处理脚本是你的终极解决方案 【免费下载链接】OneDrive-Uninstaller Batch script to completely uninstall OneDrive in Windows 10 项目地址: https://gitcode.com/gh_mirrors/on/OneDrive-Uninstaller 你是否…...
Reportr部署实战:如何在Heroku和自有服务器上快速搭建个人数据仪表板
Reportr部署实战:如何在Heroku和自有服务器上快速搭建个人数据仪表板 【免费下载链接】dashboard Your lifes personal dashboard. 项目地址: https://gitcode.com/gh_mirrors/das/dashboard Reportr是一个功能强大的开源个人数据仪表板应用,能够…...
2001-2023年各省农产品进出口额数据(无缺失)
2001-2023年各省农产品进出口额数据 1、时间:2001-2023年 2、来源:商务BU、农业年鉴、海关总署 3、指标:年份、省份、农产品出口额(亿元)、农产品进口额(亿元)、农产品出口额(万美…...
OpenClaw任务监控:Qwen3-14b_int4_awq模型执行日志分析
OpenClaw任务监控:Qwen3-14b_int4_awq模型执行日志分析 1. 为什么需要关注OpenClaw任务日志 上周我在用OpenClaw自动处理一批技术文档时,遇到了一个奇怪的现象:任务明明显示"已完成",但输出的文件内容却是空的。这个问…...
OpenClaw错误处理机制:Phi-3-vision识别失败自动重试方案
OpenClaw错误处理机制:Phi-3-vision识别失败自动重试方案 1. 为什么需要错误处理机制 上周我在用OpenClaw对接Phi-3-vision模型时,遇到了一个典型问题:当模型识别图片中的文字内容时,偶尔会出现识别失败或结果不准确的情况。这直…...
JMS, ActiveMQ 学习一则偌
开发个什么Skill呢? 通过 Skill,我们可以将某些能力进行模块化封装,从而实现特定的工作流编排、专家领域知识沉淀以及各类工具的集成。 这里我打算来一次“套娃式”的实践:创建一个用于自动生成 Skill 的 Skill,一是用…...
大模型之Linux服务器部署大模型头
一、各自优势和对比 这是检索出来的数据,据说是根据第三方评测与企业数据,三款产品在代码生成质量上各有侧重: 产品 语言优势 场景亮点 核心差异 百度 Comate C核心代码质量第一;Python首生成率达92.3% SQL生成准确率提升35%&…...
Phi-4-reasoning-vision-15B作品集:15类真实办公截图(邮件/PPT/数据库/IDE等)理解效果
Phi-4-reasoning-vision-15B作品集:15类真实办公截图理解效果展示 1. 模型能力概览 Phi-4-reasoning-vision-15B是微软最新发布的视觉多模态推理模型,专门针对办公场景的视觉理解需求进行了优化。这个模型不仅能看懂图片内容,还能像专业人士…...
打字不如说话,说话不如截图——AI 代码助手的多模态输入实践嚎
整体排查思路 我们的目标是验证以下三个环节是否正常: 登录成功时:服务器是否正确生成了Session并返回了包含正确 JSESSIONID的Cookie给浏览器。 浏览器端:浏览器是否成功接收并存储了该Cookie。 后续请求:浏览器在执行查询等…...
稀缺资源!农业农村部试点项目PHP可视化配置规范白皮书(内部解密版·仅限本期订阅用户获取)
第一章:农业农村部试点项目PHP可视化配置规范白皮书概述 本白皮书面向农业农村部“数字乡村基础设施能力提升”试点项目,聚焦PHP后端服务在农业物联网平台、基层农情填报系统及涉农数据中台等场景中的可视化配置实践。其核心目标是统一配置管理范式&…...
