diff --git a/pom.xml b/pom.xml
index ae3899b..fb34276 100644
--- a/pom.xml
+++ b/pom.xml
@@ -274,6 +274,12 @@
ruoyi-minio
${ruoyi.version}
+
+
+ com.ruoyi
+ ruoyi-websocket
+ ${ruoyi.version}
+
@@ -289,6 +295,7 @@
ruoyi-online
ruoyi-mybatis-jpa
ruoyi-middleware
+ ruoyi-websocket
pom
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index f06c417..c108030 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -81,6 +81,12 @@
ruoyi-middleware-starter
+
+
+ com.ruoyi
+ ruoyi-websocket
+
+
com.github.xiaoymin
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index d0958d9..3f7cd64 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -120,6 +120,7 @@ public class SecurityConfig {
.requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs",
"/druid/**", "/*/api-docs/**")
.permitAll()
+ .requestMatchers("/websocket/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
diff --git a/ruoyi-websocket/pom.xml b/ruoyi-websocket/pom.xml
new file mode 100644
index 0000000..46dcf9f
--- /dev/null
+++ b/ruoyi-websocket/pom.xml
@@ -0,0 +1,31 @@
+
+
+
+ ruoyi
+ com.ruoyi
+ 3.8.7.3.2
+
+ 4.0.0
+
+ ruoyi-websocket
+
+
+ websocket系统模块
+
+
+
+
+ com.ruoyi
+ ruoyi-common
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+
+
+
diff --git a/ruoyi-websocket/src/main/java/com/ruoyi/websocket/SemaphoreUtils.java b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/SemaphoreUtils.java
new file mode 100644
index 0000000..85f1074
--- /dev/null
+++ b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/SemaphoreUtils.java
@@ -0,0 +1,59 @@
+package com.ruoyi.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/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketConfig.java b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketConfig.java
new file mode 100644
index 0000000..6dad3cf
--- /dev/null
+++ b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketConfig.java
@@ -0,0 +1,20 @@
+package com.ruoyi.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/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketServer.java b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketServer.java
new file mode 100644
index 0000000..2378837
--- /dev/null
+++ b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketServer.java
@@ -0,0 +1,105 @@
+package com.ruoyi.websocket;
+
+import java.util.concurrent.Semaphore;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import jakarta.websocket.OnClose;
+import jakarta.websocket.OnError;
+import jakarta.websocket.OnMessage;
+import jakarta.websocket.OnOpen;
+import jakarta.websocket.Session;
+import jakarta.websocket.server.ServerEndpoint;
+
+/**
+ * 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);
+ // 移除用户
+ WebSocketUsers.remove(session.getId());
+ // 获取到信号量则需释放
+ 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/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketUsers.java b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketUsers.java
new file mode 100644
index 0000000..75fd661
--- /dev/null
+++ b/ruoyi-websocket/src/main/java/com/ruoyi/websocket/WebSocketUsers.java
@@ -0,0 +1,142 @@
+package com.ruoyi.websocket;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.websocket.Session;
+
+/**
+ * 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[你已离线]");
+ }
+ }
+}