SpringBoot:websocket 实现后端主动前端推送数据
简单说明下websocket实用场景。
- 实时通信领域:
- 社交聊天弹幕
- 多玩家游戏
- 协同编辑
- 股票基金实时报价
- 体育实况更新
- 视频会议/聊天
- 基于位置的应用
- 在线教育
- 智能家居等需要高实时性的场景
一、服务端代码
pom.xml:
org.springframework.boot spring-boot-starter-websocket cn.hutool hutool-all 5.1.0 com.alibaba fastjson 2.0.22
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; /** * Function: 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 { @Bean public 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; /** * Function: 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 webSocketMap = new ConcurrentHashMap(); // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; // 接收userId private String userId = ""; /** * 连接建立成功调用的方法 * @param session * @param userId */ @OnOpen public 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 + ",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if (webSocketMap.containsKey(userId)) { webSocketMap.remove(userId); //从set中删除 subOnlineCount(); } log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息 * @param session */ @OnMessage public void onMessage(String message, Session session) { log.info("用户消息:" + userId + ",报文:" + message); //可以群发消息 //消息保存到数据库redis if (!StrUtil.isEmpty(message)) { try { //解析发送的报文 JSONObject jsonObject = JSON.parseObject(message); } catch (Exception e) { log.error("用户:" + userId + ", 接收报文异常!!!!!!"); } } } /** * 会话异常 * @param session * @param error */ @OnError public 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 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; /** * Function: 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 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; /** * Function: 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 = url this.maxReconnectAttempts = maxReconnectAttempts this.reconnectInterval = reconnectInterval this.maxReconnectTime = maxReconnectTime this.reconnectCount = 0 this.reconnectTimeout = null this.startTime = null this.socket = null this.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.connect() }, this.reconnectInterval) if (this.startTime === null) { this.startTime = Date.now() } } else { console.log('超过最大重连次数或重连时间超时,已放弃连接') // 重置连接次数0 this.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 = 0 this.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页面
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.data console.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 }) } //------------------------------------
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。