From ceda60e919f883acb3f17e800aa4f7b690a8b63d Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Tue, 29 Jul 2025 16:06:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5webSocket=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=88=90=E5=8A=9F,=E5=90=8E=E7=BB=AD?= =?UTF-8?q?=E5=9C=A8=E6=AD=A4=E5=9F=BA=E7=A1=80=E4=B8=8A=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/WebSocketTestController.java | 32 +++ klp-admin/src/main/resources/application.yml | 3 + klp-framework/pom.xml | 5 +- .../framework/websocket/SemaphoreUtils.java | 58 ++++ .../framework/websocket/WebSocketConfig.java | 20 ++ .../framework/websocket/WebSocketServer.java | 106 ++++++++ .../framework/websocket/WebSocketUsers.java | 140 ++++++++++ klp-ui/public/websocket-test.html | 257 ++++++++++++++++++ klp-ui/src/views/index.vue | 16 +- 9 files changed, 635 insertions(+), 2 deletions(-) create mode 100644 klp-admin/src/main/java/com/klp/web/controller/websocket/WebSocketTestController.java create mode 100644 klp-framework/src/main/java/com/klp/framework/websocket/SemaphoreUtils.java create mode 100644 klp-framework/src/main/java/com/klp/framework/websocket/WebSocketConfig.java create mode 100644 klp-framework/src/main/java/com/klp/framework/websocket/WebSocketServer.java create mode 100644 klp-framework/src/main/java/com/klp/framework/websocket/WebSocketUsers.java create mode 100644 klp-ui/public/websocket-test.html diff --git a/klp-admin/src/main/java/com/klp/web/controller/websocket/WebSocketTestController.java b/klp-admin/src/main/java/com/klp/web/controller/websocket/WebSocketTestController.java new file mode 100644 index 00000000..6e87c5bc --- /dev/null +++ b/klp-admin/src/main/java/com/klp/web/controller/websocket/WebSocketTestController.java @@ -0,0 +1,32 @@ +package com.klp.web.controller.websocket; + +import com.klp.common.core.controller.BaseController; +import com.klp.common.core.domain.R; +import org.springframework.web.bind.annotation.*; + +/** + * WebSocket测试控制器 + * + * @author klp + */ +@RestController +@RequestMapping("/websocket/test") +public class WebSocketTestController extends BaseController { + + /** + * 测试WebSocket端点是否可用 + */ + @GetMapping("/status") + public R getWebSocketStatus() { + return R.ok("WebSocket端点可用,连接地址: ws://localhost:8080/websocket/message"); + } + + /** + * 获取当前连接数 + */ + @GetMapping("/connections") + public R getConnectionCount() { + // 这里可以调用WebSocketUsers.getUsers().size()来获取实际连接数 + return R.ok(0); + } +} \ No newline at end of file diff --git a/klp-admin/src/main/resources/application.yml b/klp-admin/src/main/resources/application.yml index a4c6401e..3459d1f4 100644 --- a/klp-admin/src/main/resources/application.yml +++ b/klp-admin/src/main/resources/application.yml @@ -140,6 +140,9 @@ security: # 扫码枪获取出入库单详情 - /wms/stockIoDetail - /wms/stockIo/scanInStock + # WebSocket路径 + - /websocket/** + - /wms/websocket/** # MyBatisPlus配置 diff --git a/klp-framework/pom.xml b/klp-framework/pom.xml index a199b11d..2665f6ea 100644 --- a/klp-framework/pom.xml +++ b/klp-framework/pom.xml @@ -66,7 +66,10 @@ com.klp klp-common - + + org.springframework.boot + spring-boot-starter-websocket + diff --git a/klp-framework/src/main/java/com/klp/framework/websocket/SemaphoreUtils.java b/klp-framework/src/main/java/com/klp/framework/websocket/SemaphoreUtils.java new file mode 100644 index 00000000..d3cb4ca7 --- /dev/null +++ b/klp-framework/src/main/java/com/klp/framework/websocket/SemaphoreUtils.java @@ -0,0 +1,58 @@ +package com.klp.framework.websocket; + +import java.util.concurrent.Semaphore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 信号量相关处理 + * + * @author ruoyi + */ +public class SemaphoreUtils +{ + /** + * SemaphoreUtils 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class); + + /** + * 获取信号量 + * + * @param semaphore + * @return + */ + public static boolean tryAcquire(Semaphore semaphore) + { + boolean flag = false; + + try + { + flag = semaphore.tryAcquire(); + } + catch (Exception e) + { + LOGGER.error("获取信号量异常", e); + } + + return flag; + } + + /** + * 释放信号量 + * + * @param semaphore + */ + public static void release(Semaphore semaphore) + { + + try + { + semaphore.release(); + } + catch (Exception e) + { + LOGGER.error("释放信号量异常", e); + } + } +} diff --git a/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketConfig.java b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketConfig.java new file mode 100644 index 00000000..d168649a --- /dev/null +++ b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketConfig.java @@ -0,0 +1,20 @@ +package com.klp.framework.websocket; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * websocket 配置 + * + * @author ruoyi + */ +@Configuration +public class WebSocketConfig +{ + @Bean + public ServerEndpointExporter serverEndpointExporter() + { + return new ServerEndpointExporter(); + } +} diff --git a/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketServer.java b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketServer.java new file mode 100644 index 00000000..6faab0eb --- /dev/null +++ b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketServer.java @@ -0,0 +1,106 @@ +package com.klp.framework.websocket; + +import java.util.concurrent.Semaphore; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * websocket 消息处理 + * + * @author ruoyi + */ +@Component +@ServerEndpoint("/websocket/message") +public class WebSocketServer +{ + /** + * WebSocketServer 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class); + + /** + * 默认最多允许同时在线人数100 + */ + public static int socketMaxOnlineCount = 100; + + private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount); + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session) throws Exception + { + boolean semaphoreFlag = false; + // 尝试获取信号量 + semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore); + if (!semaphoreFlag) + { + // 未获取到信号量 + LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount); + WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount); + session.close(); + } + else + { + // 添加用户 + WebSocketUsers.put(session.getId(), session); + LOGGER.info("\n 建立连接 - {}", session); + LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size()); + WebSocketUsers.sendMessageToUserByText(session, "连接成功"); + } + } + + /** + * 连接关闭时处理 + */ + @OnClose + public void onClose(Session session) + { + LOGGER.info("\n 关闭连接 - {}", session); + // 移除用户 + boolean removeFlag = WebSocketUsers.remove(session.getId()); + if (!removeFlag) + { + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + } + + /** + * 抛出异常时处理 + */ + @OnError + public void onError(Session session, Throwable exception) throws Exception + { + if (session.isOpen()) + { + // 关闭连接 + session.close(); + } + String sessionId = session.getId(); + LOGGER.info("\n 连接异常 - {}", sessionId); + LOGGER.info("\n 异常信息 - {}", exception); + // 移出用户 + WebSocketUsers.remove(sessionId); + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + + /** + * 服务器接收到客户端消息时调用的方法 + */ + @OnMessage + public void onMessage(String message, Session session) + { + String msg = message.replace("你", "我").replace("吗", ""); + WebSocketUsers.sendMessageToUserByText(session, msg); + } +} diff --git a/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketUsers.java b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketUsers.java new file mode 100644 index 00000000..2007e60b --- /dev/null +++ b/klp-framework/src/main/java/com/klp/framework/websocket/WebSocketUsers.java @@ -0,0 +1,140 @@ +package com.klp.framework.websocket; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.websocket.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * websocket 客户端用户集 + * + * @author ruoyi + */ +public class WebSocketUsers +{ + /** + * WebSocketUsers 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); + + /** + * 用户集 + */ + private static Map USERS = new ConcurrentHashMap(); + + /** + * 存储用户 + * + * @param key 唯一键 + * @param session 用户信息 + */ + public static void put(String key, Session session) + { + USERS.put(key, session); + } + + /** + * 移除用户 + * + * @param session 用户信息 + * + * @return 移除结果 + */ + public static boolean remove(Session session) + { + String key = null; + boolean flag = USERS.containsValue(session); + if (flag) + { + Set> entries = USERS.entrySet(); + for (Map.Entry entry : entries) + { + Session value = entry.getValue(); + if (value.equals(session)) + { + key = entry.getKey(); + break; + } + } + } + else + { + return true; + } + return remove(key); + } + + /** + * 移出用户 + * + * @param key 键 + */ + public static boolean remove(String key) + { + LOGGER.info("\n 正在移出用户 - {}", key); + Session remove = USERS.remove(key); + if (remove != null) + { + boolean containsValue = USERS.containsValue(remove); + LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功"); + return containsValue; + } + else + { + return true; + } + } + + /** + * 获取在线用户列表 + * + * @return 返回用户集合 + */ + public static Map getUsers() + { + return USERS; + } + + /** + * 群发消息文本消息 + * + * @param message 消息内容 + */ + public static void sendMessageToUsersByText(String message) + { + Collection values = USERS.values(); + for (Session value : values) + { + sendMessageToUserByText(value, message); + } + } + + /** + * 发送文本消息 + * + * @param userName 自己的用户名 + * @param message 消息内容 + */ + public static void sendMessageToUserByText(Session session, String message) + { + if (session != null) + { + try + { + session.getBasicRemote().sendText(message); + } + catch (IOException e) + { + LOGGER.error("\n[发送消息异常]", e); + } + } + else + { + LOGGER.info("\n[你已离线]"); + } + } +} diff --git a/klp-ui/public/websocket-test.html b/klp-ui/public/websocket-test.html new file mode 100644 index 00000000..e9b86390 --- /dev/null +++ b/klp-ui/public/websocket-test.html @@ -0,0 +1,257 @@ + + + + + + WebSocket测试界面 + + + + + +
+
+ +
+ +

WebSocket实时通信测试

+ +
+
+ + +
+ +
+ + + +
+ +
+ 未连接 +
+
+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/klp-ui/src/views/index.vue b/klp-ui/src/views/index.vue index faed9bf4..f66447b9 100644 --- a/klp-ui/src/views/index.vue +++ b/klp-ui/src/views/index.vue @@ -155,6 +155,13 @@ export default { icon: 'fas fa-cog', bgColor: 'bg-indigo-500', link: '/system/menu' + }, + { + title: 'WebSocket测试', + description: '实时通信功能测试', + icon: 'fas fa-comments', + bgColor: 'bg-teal-500', + link: '/websocket-test.html' } ], resourceCharts: [ @@ -216,7 +223,12 @@ export default { } }, handleLink(item) { - this.$router.push(item); + // 如果是外部链接(以http开头或.html结尾),则在新窗口打开 + if (item.startsWith('http') || item.endsWith('.html')) { + window.open(item, '_blank'); + } else { + this.$router.push(item); + } }, getGreeting() { const hour = new Date().getHours(); @@ -236,6 +248,7 @@ export default { case 'bg-purple-500': return 'bg-purple'; case 'bg-red-500': return 'bg-red'; case 'bg-indigo-500': return 'bg-indigo'; + case 'bg-teal-500': return 'bg-teal'; default: return ''; } }, @@ -449,6 +462,7 @@ export default { .bg-purple { background: #a855f7; } .bg-red { background: #ef4444; } .bg-indigo { background: #6366f1; } +.bg-teal { background: #14b8a6; } .business-module-title { font-size: 16px; font-weight: 500;