最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot WebSocket多消息推送過程

 更新時(shí)間:2026年01月13日 14:35:28   作者:掐指一算乀缺錢  
介紹了如何實(shí)現(xiàn)多種消息推送功能,包括給分組、用戶推送消息,處理多設(shè)備登錄,監(jiān)控連接心跳,并使用公平鎖支持并發(fā),詳細(xì)描述了WebSocket配置類、攔截器、處理器和工具類的實(shí)現(xiàn),并提供了測(cè)試地址

實(shí)現(xiàn)功能

  • 1. 給某個(gè)分組推送消息
  • 2. 給所有分組推送消息
  • 3. 給所有用戶推送消息
  • 4. 給某一個(gè)用戶單獨(dú)推送消息(在分組中)
  • 5. 給某一個(gè)用戶推送消息(不在在分組中)
  • 6. 用戶可能存在多個(gè)分組
  • 7. 用戶多設(shè)備登錄
  • 8. 監(jiān)控連接心跳(后端實(shí)現(xiàn)不需要前端實(shí)現(xiàn)配合)
  • 9. 失敗超過5次關(guān)閉連接

使用公平鎖支持并發(fā)。

添加依賴

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

websocket配置類

/**
 * @Description websocket配置類
 * @Author WangKun
 * @Date 2025/8/10 19:02
 * @Version
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    /**
     * @Description websocket攔截器配置端點(diǎn)
      * @param registry WebSocket處理程序注冊(cè)表,用于注冊(cè)WebSocket處理器和攔截器
     * @Throws 可能拋出WebSocket相關(guān)的異常
     * @Return void 無返回值
     * @Date 2025-08-10 19:14:42
     * @Author WangKun
     **/
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 注冊(cè)WebSocket處理器,并設(shè)置URL路徑為"/ws/{id}"
        registry.addHandler(webSocketHandler(), "/ws/{id}")
                // 允許所有來源的跨域請(qǐng)求
                .setAllowedOrigins("*");
    }

    /**
     * @Description websocket核心處理器
     * @Throws 可能拋出Bean創(chuàng)建相關(guān)的異常
     * @Return JWebSocketManager 返回WebSocket管理器的實(shí)例
     * @Date 2025-08-10 19:13:48
     * @Author WangKun
     **/
    @Bean
    public WebSocketManager webSocketManager() {
        return new WebSocketManager();
    }

    /**
     * @Description websocket攔截處理
     * @Throws 可能拋出Bean創(chuàng)建相關(guān)的異常
     * @Return JWebSocketHandler 返回WebSocket處理器的實(shí)例
     * @Date 2025-08-10 19:15:20
     * @Author WangKun
     **/
    @Bean // 將此方法返回的對(duì)象注冊(cè)為Spring容器中的Bean
    public WebSocketHandler webSocketHandler() {
        // 創(chuàng)建WebSocket處理器實(shí)例,并注入WebSocket管理器
        return new WebSocketHandler(webSocketManager());
    }
}

websocket攔截器

/**
 * @Description websocket攔截器
 * @Author WangKun
 * @Date 2025/8/10 19:14
 * @Version
 */
@Slf4j
public class WebSocketHandler extends TextWebSocketHandler {

    private final WebSocketManager manager; // WebSocket管理器,用于管理WebSocket會(huì)話

    /**
     * @Description 構(gòu)造器注入核心
      * @param manager WebSocket管理器實(shí)例
     * @Throws
     * @Return
     * @Date 2025-08-21 09:27:35
     * @Author WangKun
     **/
    public WebSocketHandler(WebSocketManager manager) {
        this.manager = manager;
    }

    /**
     * @param session WebSocket會(huì)話對(duì)象
     * @Description 初始化會(huì)話
     * @Throws 當(dāng)會(huì)話建立過程中出現(xiàn)異常時(shí)拋出
     * @Return void
     * @Date 2025-08-10 19:17:57
     * @Author WangKun
     **/
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        manager.addSession(extractId(session), session);
    }

    /**
     * @param session WebSocket會(huì)話對(duì)象
     * @param message 接收到的文本消息
     * @Description PONG消息處理
     * @Throws 當(dāng)消息處理過程中出現(xiàn)異常時(shí)拋出
     * @Return void
     * @Date 2025-08-10 19:18:01
     * @Author WangKun
     **/
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        String payload = message.getPayload();
        log.info("收到來自[{}]的PONG消息: {}", extractId(session), payload);
        if ("PONG".equals(payload)) {
            manager.handlePongMessage(session);
        }
    }

    /**
     * @param session WebSocket會(huì)話對(duì)象
     * @param message 接收到的PONG消息
     * @Description 消息處理
     * @Throws 當(dāng)消息處理過程中出現(xiàn)異常時(shí)拋出
     * @Return void
     * @Date 2025-08-10 19:18:08
     * @Author WangKun
     **/
    @Override
    public void handlePongMessage(WebSocketSession session, PongMessage message) {
        manager.handlePongMessage(session);
    }

    /**
     * @param session WebSocket會(huì)話對(duì)象
     * @param status 會(huì)話關(guān)閉狀態(tài)
     * @Description 會(huì)話關(guān)閉
     * @Throws 當(dāng)會(huì)話關(guān)閉過程中出現(xiàn)異常時(shí)拋出
     * @Return void
     * @Date 2025-08-10 19:18:13
     * @Author WangKun
     **/
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        manager.removeToSession(session);
    }

    /**
     * @param session WebSocket會(huì)話對(duì)象
     * @Description 攔截請(qǐng)求參數(shù)處理
     * @Throws 當(dāng)URI解析過程中出現(xiàn)異常時(shí)拋出
     * @Return java.lang.String 返回從URI中提取的ID
     * @Date 2025-08-10 19:18:22
     * @Author WangKun
     **/
    private String extractId(WebSocketSession session) {
        // 從URI路徑提取ID:/ws/{id}
        String path = session.getUri().getPath();
        return path.substring(path.lastIndexOf('/') + 1);
    }
}

websocket處理器

/**
 * @Description websocket處理器
 * @Author WangKun
 * @Date 2025/8/10 19:20
 * @Version
 */
@Slf4j
public class WebSocketManager {

    // ID -> 設(shè)備會(huì)話集合 (支持多設(shè)備)
    private final ConcurrentMap<String, Set<WebSocketSession>> SESSIONS = new ConcurrentHashMap<>();
    // 會(huì)話 -> ID
    private final ConcurrentMap<WebSocketSession, String> SESSION_MAP = new ConcurrentHashMap<>();
    // 分組ID -> ID集合
    private final ConcurrentMap<String, Set<String>> GROUPS = new ConcurrentHashMap<>();
    // 會(huì)話失敗計(jì)數(shù)器 (Session -> 失敗次數(shù))
    private final ConcurrentMap<WebSocketSession, Integer> FAILURE_COUNTS = new ConcurrentHashMap<>();
    // 心跳狀態(tài)監(jiān)控 (Session -> 最后活躍時(shí)間)
    private final ConcurrentMap<WebSocketSession, Long> LAST_ACTIVE_TIMES = new ConcurrentHashMap<>();
    // 公平鎖,確保所有操作的原子性
    private final ReentrantLock MAIN_LOCK = new ReentrantLock(true);
    // 心跳狀態(tài)監(jiān)控
    private final ConcurrentMap<WebSocketSession, Long> lastPongTimes = new ConcurrentHashMap<>();
    private final ConcurrentMap<WebSocketSession, ScheduledFuture<?>> pingTasks = new ConcurrentHashMap<>();
    // 心跳間隔25秒
    private static final long HEARTBEAT_INTERVAL = 25000;
    // 心跳超時(shí)40秒
    private static final long HEARTBEAT_TIMEOUT = 40000;
    // 心跳PING NIO包裝
    private static final ByteBuffer PING_PAYLOAD = ByteBuffer.wrap(new byte[]{0x1});

    // 線程池
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);

    public WebSocketManager() {
        // 心跳檢測(cè)任務(wù)
        scheduler.scheduleAtFixedRate(this::checkHeartbeats, 10, 10, TimeUnit.SECONDS);
        // 會(huì)話清理任務(wù)
        scheduler.scheduleAtFixedRate(this::cleanExpiredSessions, 5, 5, TimeUnit.MINUTES);
    }

    /**
     * @param id
     * @param session
     * @Description 建立連接添加到session
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:37:14
     * @Author WangKun
     **/
    public void addSession(String id, WebSocketSession session) {
        MAIN_LOCK.lock();
        try {
            if (!SESSION_MAP.containsKey(session)) {
                // 會(huì)話管理
                SESSIONS.computeIfAbsent(id, k -> ConcurrentHashMap.newKeySet()).add(session);
                SESSION_MAP.put(session, id);
                // 初始化心跳狀態(tài)
                lastPongTimes.put(session, System.currentTimeMillis());
                startPingTask(session);
                log.info("[連接建立] {}: 會(huì)話:{}", id, session.getId());
            }
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param session
     * @Description 移除session中的
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:40:11
     * @Author WangKun
     **/
    public void removeToSession(WebSocketSession session) {
        MAIN_LOCK.lock();
        try {
            String id = SESSION_MAP.get(session);
            if (id != null) {
                // 清理會(huì)話
                Set<WebSocketSession> sessions = SESSIONS.get(id);
                if (sessions != null) {
                    sessions.remove(session);
                    if (sessions.isEmpty()) {
                        SESSIONS.remove(id);
                    }
                }
                // 清理心跳任務(wù)
                stopPingTask(session);
                lastPongTimes.remove(session);
                SESSION_MAP.remove(session);

                log.info("[連接關(guān)閉] {}:  會(huì)話{}: ", id, session.getId());
            }
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param session
     * @Description 開始心跳PING
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:41:06
     * @Author WangKun
     **/
    private void startPingTask(WebSocketSession session) {
        ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(() -> {
            MAIN_LOCK.lock();
            try {
                if (session.isOpen()) {
                    try {
                        // 標(biāo)準(zhǔn)WebSocket Ping發(fā)送
                        if (session instanceof StandardWebSocketSession) {
                            ((StandardWebSocketSession) session).getNativeSession()
                                    .getAsyncRemote().sendPing(PING_PAYLOAD);
                        } else {
                            session.sendMessage(new PingMessage(PING_PAYLOAD));
                        }
                        log.info("[Ping發(fā)送] 會(huì)話{}: ", session.getId());
                    } catch (Exception e) {
                        log.info("[Ping失敗] 會(huì)話{}: 錯(cuò)誤{}: {}", session.getId(), e.getMessage(), session.getUri());
                        closeSession(session);
                    }
                }
            } finally {
                MAIN_LOCK.unlock();
            }
        }, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL, TimeUnit.MILLISECONDS);
        pingTasks.put(session, task);
    }

    /**
     * @param session
     * @Description 心跳停止
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:41:55
     * @Author WangKun
     **/
    private void stopPingTask(WebSocketSession session) {
        ScheduledFuture<?> task = pingTasks.remove(session);
        if (task != null) {
            task.cancel(true);
        }
    }

    /**
     * @param session
     * @Description 心跳PONG接收
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:42:20
     * @Author WangKun
     **/
    public void handlePongMessage(WebSocketSession session) {
        MAIN_LOCK.lock();
        try {
            lastPongTimes.put(session, System.currentTimeMillis());
            log.info("[Pong接收] 會(huì)話{}: {}", session.getId(), session.getUri());
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param id
     * @param groupId
     * @Description 添加至分組
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:43:20
     * @Author WangKun
     **/
    public boolean addToGroup(String id, String groupId) {
        MAIN_LOCK.lock();
        if (StringUtils.isBlank(id) && StringUtils.isEmpty(id)) {
            log.info("加入分組失敗,id 為空");
            return false;
        }
        if (StringUtils.isBlank(groupId) && StringUtils.isEmpty(groupId)) {
            log.info("加入分組失敗,groupId 為空");
            return false;
        }
        try {
            boolean flag = GROUPS.computeIfAbsent(groupId, k -> ConcurrentHashMap.newKeySet()).add(id);
            log.info("[加入分組] {}: → 分組{}:  (當(dāng)前組內(nèi): {})", id, groupId, GROUPS.get(groupId).size());
            return flag;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param id
     * @param groupId
     * @Description 從分組移除
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:44:10
     * @Author WangKun
     **/
    public boolean removeToGroup(String id, String groupId) {
        MAIN_LOCK.lock();
        try {
            if (StringUtils.isBlank(id) && StringUtils.isEmpty(id)) {
                log.info("移除分組失敗,id 為空");
                return false;
            }
            if (StringUtils.isBlank(groupId) && StringUtils.isEmpty(groupId)) {
                log.info("移除分組失敗,groupId 為空");
                return false;
            }
            Set<String> groups = GROUPS.get(groupId);
            if (groups != null) {
                groups.remove(id);
                if (groups.isEmpty()) {
                    GROUPS.remove(groupId);
                }
                log.info("[離開分組] {}:  ← 分組{}: ", id, groupId);
                return true;
            }
            return false;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param id
     * @param message
     * @Description 消息發(fā)送,多設(shè)備
     * @Throws
     * @Return boolean
     * @Date 2025-08-10 19:44:48
     * @Author WangKun
     **/
    public boolean sendMessages(String id, String message) {
        MAIN_LOCK.lock();
        try {
            if (StringUtils.isBlank(id) && StringUtils.isEmpty(id)) {
                log.info("消息發(fā)送失敗,id 為空");
                return false;
            }
            Set<WebSocketSession> sessions = SESSIONS.get(id);
            if (sessions == null || sessions.isEmpty()) {
                log.info("[單發(fā)消息失敗] {}: 無活躍會(huì)話", id);
                return false;
            }
            // 創(chuàng)建副本防止并發(fā)修改
            List<WebSocketSession> sessionsCopy = new ArrayList<>(sessions);
            boolean allSuccess = true;
            for (WebSocketSession session : sessionsCopy) {
                if (validateSession(session)) {
                    allSuccess = false;
                    continue;
                }
                allSuccess &= sendMessage(session, message);
            }
            log.info("[單發(fā)消息完成] {}: 設(shè)備數(shù){}:成功{} ", id, sessionsCopy.size(), allSuccess ? "全部" : "部分");
            return allSuccess;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param groupId
     * @param message
     * @Description 按組發(fā)送
     * @Throws
     * @Return boolean
     * @Date 2025-08-10 19:46:15
     * @Author WangKun
     **/
    public boolean sendGroupMessages(String groupId, String message) {
        MAIN_LOCK.lock();
        try {
            if (StringUtils.isBlank(groupId)&& StringUtils.isEmpty(message)) {
                log.info("[按組發(fā)失敗] groudId為空");
                return false;
            }
            Set<String> ids = GROUPS.get(groupId);
            if (ids == null || ids.isEmpty()) {
                log.info("[按組發(fā)失敗] 分組{}: 無成員", groupId);
                return false;
            }
            boolean allSuccess = true;
            // 創(chuàng)建副本防止并發(fā)修改
            for (String id : new ArrayList<>(ids)) {
                allSuccess &= sendMessages(id, message);
            }
            log.info("[組發(fā)完成] 分組{}:  成員數(shù){}:  成功{}", groupId, ids.size(), allSuccess ? "全部" : "部分");
            return allSuccess;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param message
     * @Description 全局廣播無論在不在分組
     * @Throws
     * @Return boolean
     * @Date 2025-08-10 19:47:33
     * @Author WangKun
     **/
    public boolean sendGlobalMessages(String message) {
        MAIN_LOCK.lock();
        try {
            if (SESSIONS.isEmpty()) {
                log.info("[廣播失敗] 無活躍");
                return false;
            }
            boolean allSuccess = true;
            int totalSessions = 0;
            // 創(chuàng)建副本防止并發(fā)修改
            Set<String> ids = new HashSet<>(SESSIONS.keySet());
            for (String id : ids) {
                Set<WebSocketSession> sessions = SESSIONS.get(id);
                if (sessions != null) {
                    // 創(chuàng)建會(huì)話副本
                    List<WebSocketSession> sessionsCopy = new ArrayList<>(sessions);
                    totalSessions += sessionsCopy.size();
                    for (WebSocketSession session : sessionsCopy) {
                        if (validateSession(session)) {
                            allSuccess = false;
                            continue;
                        }
                        allSuccess &= sendMessage(session, message);
                    }
                }
            }
            log.info("[廣播完成] 總{}: 總設(shè)備{}: 成功{} ", ids.size(), totalSessions, allSuccess ? "全部" : "部分");
            return allSuccess;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param session
     * @param message
     * @Description 會(huì)話發(fā)送消息
     * @Throws
     * @Return boolean
     * @Date 2025-08-10 19:49:00
     * @Author WangKun
     **/
    private boolean sendMessage(WebSocketSession session, String message) {
        try {
            if (!session.isOpen()) {
                throw new IllegalStateException("會(huì)話已關(guān)閉");
            }
            // 會(huì)話級(jí)別的同步,鎖,防止亂發(fā)
            synchronized (session) {
                session.sendMessage(new TextMessage(message));
                recordSuccessfulSend(session);
                return true;
            }
        } catch (Exception e) {
            handleSendFailure(session, e);
            return false;
        }
    }

    /**
     * @param session
     * @Description 發(fā)送記錄 ,心跳更新狀態(tài)
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:50:41
     * @Author WangKun
     **/
    private void recordSuccessfulSend(WebSocketSession session) {
        FAILURE_COUNTS.put(session, 0);
        LAST_ACTIVE_TIMES.put(session, System.currentTimeMillis());
    }

    /**
     * @param session
     * @param e
     * @Description 失敗關(guān)閉連接
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:51:10
     * @Author WangKun
     **/
    private void handleSendFailure(WebSocketSession session, Exception e) {
        MAIN_LOCK.lock();
        try {
            int failures = FAILURE_COUNTS.getOrDefault(session, 0) + 1;
            FAILURE_COUNTS.put(session, failures);
            log.info("[發(fā)送失敗] 會(huì)話{}: 次數(shù){}:  原因{}: ", session.getId(), failures, e.getMessage());
            if (failures >= 5) {
                log.info("[自動(dòng)清理] 達(dá)到失敗上限 會(huì)話: {}", session.getId());
                closeSession(session);
            }
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param
     * @Description 心跳健康監(jiān)控
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:52:24
     * @Author WangKun
     **/
    private void checkHeartbeats() {
        MAIN_LOCK.lock();
        try {
            long currentTime = System.currentTimeMillis();
            new ArrayList<>(lastPongTimes.keySet()).forEach(session -> {
                Long lastPong = lastPongTimes.get(session);
                if (lastPong != null && currentTime - lastPong > HEARTBEAT_TIMEOUT) {
                    log.info("[心跳超時(shí)] 會(huì)話{}:  最后Pong{}:  前 ", session.getId(), currentTime - lastPong);
                    closeSession(session);
                }
            });
        } finally {
            MAIN_LOCK.unlock();
        }
    }

    /**
     * @param session
     * @Description 檢驗(yàn)session
     * @Throws
     * @Return boolean
     * @Date 2025-08-10 19:53:35
     * @Author WangKun
     **/
    private boolean validateSession(WebSocketSession session) {
        if (!session.isOpen()) {
            removeToSession(session);
            return true;
        }
        return false;
    }

    /**
     * @param session
     * @Description 關(guān)閉session, 會(huì)話結(jié)束
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:54:07
     * @Author WangKun
     **/
    private void closeSession(WebSocketSession session) {
        try {
            if (session.isOpen()) {
                session.close(CloseStatus.SESSION_NOT_RELIABLE);
            }
        } catch (Exception e) {
            log.info("[關(guān)閉異常] 會(huì)話{}:  原因{}: ", session.getId(), e.getMessage());
        } finally {
            removeToSession(session);
        }
    }

    /**
     * @param
     * @Description 清理無效會(huì)話
     * @Throws
     * @Return void
     * @Date 2025-08-10 19:55:08
     * @Author WangKun
     **/
    private void cleanExpiredSessions() {
        MAIN_LOCK.lock();
        try {
            new ArrayList<>(SESSION_MAP.keySet()).forEach(session -> {
                if (!session.isOpen()) {
                    removeToSession(session);
                }
            });
        } finally {
            MAIN_LOCK.unlock();
        }
    }

}

websocket工具類封裝

(SpringUtils就是一個(gè)自己封裝獲取spring上下文的工具)

/**
 * @Description websocket工具類
 * @Author WangKun
 * @Date 2025/8/11 10:08
 * @Version
 */
@Component
public class WebSocketUtils {

    /**
     * websocket處理器資源
     **/
    private static final WebSocketManager WEB_SOCKET_MANAGER = SpringUtils.getBean(WebSocketManager.class);


    /**
     * @param groupId
     * @param id
     * @Description 添加至分組
     * @Throws
     * @Return boolean
     * @Date 2025-08-11 10:14:27
     * @Author WangKun
     **/
    public static boolean addToGroup(String id, String groupId) {
        return WEB_SOCKET_MANAGER.addToGroup(id, groupId);
    }

    /**
     * @param id
     * @param groupId
     * @Description 移除分組
     * @Throws
     * @Return boolean
     * @Date 2025-08-11 10:44:47
     * @Author WangKun
     **/
    public static boolean removeToGroup(String id, String groupId) {
        return WEB_SOCKET_MANAGER.removeToGroup(id, groupId);
    }

    /**
     * @param id
     * @param message
     * @Description 指定單發(fā)消息
     * @Throws
     * @Return boolean
     * @Date 2025-08-11 11:03:14
     * @Author WangKun
     **/
    public static boolean sendMessage(String id, String message) {
        return WEB_SOCKET_MANAGER.sendMessages(id, message);
    }

    /**
     * @param groupId
     * @param message
     * @Description 指定分組發(fā)消息
     * @Throws
     * @Return boolean
     * @Date 2025-08-11 11:09:22
     * @Author WangKun
     **/
    public static boolean sendGroupMessages(String groupId, String message) {
        return WEB_SOCKET_MANAGER.sendGroupMessages(groupId, message);
    }

    /**
     * @param message
     * @Description 全局廣播發(fā)消息
     * @Throws
     * @Return boolean
     * @Date 2025-08-11 11:16:54
     * @Author WangKun
     **/
    public static boolean sendGlobalMessages(String message) {
        return WEB_SOCKET_MANAGER.sendGlobalMessages(message);
    }
}

websocket控制器

/**
 * @Description websocket控制器
 * @Author WangKun
 * @Date 2025/8/10 19:59
 * @Version
 */
@RestController
@RequestMapping("/api/websocket")
public class WebSocketController {

    private final WebSocketManager manager;

    /**
     * @param manager
     * @Description 構(gòu)造函數(shù),通過依賴注入方式初始化WebSocket管理器
     * @Throws
     * @Return
     * @Date 2025-08-21 09:25:44
     * @Author WangKun
     **/
    public WebSocketController(WebSocketManager manager) {
        this.manager = manager;
    }

    /**
     * @param
     * @Description 測(cè)試連接地址
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 19:59:26
     * @Author WangKun
     **/
    @GetMapping("/test") // 處理GET請(qǐng)求,用于測(cè)試WebSocket連接
    public String testConnection() {
        WebSocketUtils.addToGroup("test", "test");
        WebSocketUtils.sendGlobalMessages("壓測(cè)");
        return "ws://localhost:8099/ws/{id}";
    }

    /**
     * @param id      用戶ID
     * @param groupId 分組ID
     * @Description 添加到分組
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 19:59:42
     * @Author WangKun
     **/
    @PostMapping("/addToGroup")
    public String addToGroup(@RequestParam String id, @RequestParam String groupId) {
        boolean flag = manager.addToGroup(id, groupId);
        return "[" + id + "]添加到分組[" + groupId + "]:" + flag;
    }

    /**
     * @param id      用戶ID
     * @param groupId 分組ID
     * @Description 從分組移除
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 19:59:48
     * @Author WangKun
     **/
    @PostMapping("/removeToGroup")
    public String removeToGroup(@RequestParam String id, @RequestParam String groupId) {
        boolean flag = manager.removeToGroup(id, groupId);
        return "[" + id + "]從分組[" + groupId + "]移除:" + flag;
    }

    /**
     * @param id      用戶ID
     * @param message 要發(fā)送的消息內(nèi)容
     * @Description 發(fā)送消息
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 19:59:56
     * @Author WangKun
     **/
    @PostMapping("/sendMessages")
    public String sendMessages(@RequestParam String id, @RequestParam String message) {
        boolean success = manager.sendMessages(id, message);
        return success ? "發(fā)送成功" : "發(fā)送失敗(無活躍連接)";
    }

    /**
     * @param groupId 分組ID
     * @param message 要發(fā)送的消息內(nèi)容
     * @Description 給分組發(fā)送消息
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 20:00:06
     * @Author WangKun
     **/
    @PostMapping("/sendGroupMessages")
    public String sendToGroup(@RequestParam String groupId, @RequestParam String message) {
        boolean success = manager.sendGroupMessages(groupId, message);
        return success ? "發(fā)送成功" : "發(fā)送失?。ǚ纸M不存在或無成員)";
    }

    /**
     * @param message 要廣播的消息內(nèi)容
     * @Description 全局廣播
     * @Throws
     * @Return java.lang.String
     * @Date 2025-08-10 20:00:13
     * @Author WangKun
     **/
    @PostMapping("/sendGlobalMessages")
    public String broadcast(@RequestParam String message) {
        boolean success = manager.sendGlobalMessages(message);
        return success ? "廣播成功" : "部分發(fā)送失敗";
    }
}

測(cè)試地址

ws://localhost:8099/ws/user1

剩下的測(cè)試地址都在控制中。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot傳遞單一參數(shù)時(shí)@RequestParam和@RequestBody的區(qū)別小結(jié)

    SpringBoot傳遞單一參數(shù)時(shí)@RequestParam和@RequestBody的區(qū)別小結(jié)

    用SpringBoot框架做項(xiàng)目時(shí),經(jīng)常需要前端給后端傳遞參數(shù),本文主要介紹了SpringBoot傳遞單一參數(shù)時(shí)@RequestParam和@RequestBody的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • 完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導(dǎo)致的內(nèi)存溢出問題

    完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導(dǎo)致的內(nèi)存溢出問題

    今天小編就為大家分享一篇完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導(dǎo)致的內(nèi)存溢出問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • SpringBoot集成MaxCompute的示例代碼

    SpringBoot集成MaxCompute的示例代碼

    這篇文章主要介紹了SpringBoot集成MaxCompute的實(shí)例代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • Java中實(shí)例初始化和靜態(tài)初始化的過程詳解

    Java中實(shí)例初始化和靜態(tài)初始化的過程詳解

    Java代碼初始化塊是Java語言中的一個(gè)非常重要的概念。初始化塊負(fù)責(zé)在創(chuàng)建對(duì)象時(shí)進(jìn)行一些必要的操作,例如設(shè)置對(duì)象的初始狀態(tài)、初始化成員變量等。初始化塊被分為實(shí)例初始化塊和靜態(tài)初始化塊兩種類型。本文詳細(xì)介紹了初始化的過程,需要的朋友可以參考下
    2023-05-05
  • Java屬性嵌套獲取值的實(shí)現(xiàn)方式

    Java屬性嵌套獲取值的實(shí)現(xiàn)方式

    本文介紹了如何使用MybatisMetaObject類和FieldUtil工具類來簡化Java類嵌套獲取屬性值的過程,通過這種方式,可以避免逐層檢查和判空操作,直接從實(shí)例對(duì)象中獲取深層嵌套的屬性值,并且在任意一層為空時(shí)自動(dòng)返回null,不會(huì)報(bào)錯(cuò)
    2025-11-11
  • java多線程模擬搶紅包功能

    java多線程模擬搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了java多線程模擬搶紅包功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • SpringBoot的Bean類三種注入方式詳解

    SpringBoot的Bean類三種注入方式詳解

    在 Spring Boot 中,Bean 的注入方式主要包括構(gòu)造函數(shù)注入(Constructor Injection)、字段注入(Field Injection)以及 Setter 方法注入(Setter Injection),以下是這幾種注入方式的詳細(xì)介紹,并附上 Lombok 的注入方式,需要的朋友可以參考下
    2024-12-12
  • SpringBoot加載不出來application.yml文件的解決方法

    SpringBoot加載不出來application.yml文件的解決方法

    這篇文章主要介紹了SpringBoot加載不出來application.yml文件的解決方法,文中通過示例代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作有一定的幫助,需要的朋友跟著小編來一起來學(xué)習(xí)吧
    2023-12-12
  • 詳解Java編程中的策略模式

    詳解Java編程中的策略模式

    這篇文章主要介紹了詳解Java編程中的策略模式,以及用策略模式來分析源碼等內(nèi)容,需要的朋友可以參考下
    2015-08-08
  • spring-retry簡單使用方法

    spring-retry簡單使用方法

    這篇文章主要介紹了spring-retry簡單使用方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04

最新評(píng)論

青铜峡市| 新宾| 瑞安市| 陆河县| 彭阳县| 镇江市| 南乐县| 云安县| 信丰县| 营山县| 湖口县| 淮安市| 札达县| 和龙市| 桐乡市| 香河县| 屏山县| 河池市| 进贤县| 阜康市| 达州市| 永康市| 会泽县| 襄汾县| 三原县| 乳山市| 易门县| 曲周县| 古丈县| 河东区| 肥城市| 黎城县| 缙云县| 离岛区| 治县。| 犍为县| 夏河县| 长岭县| 浦江县| 仙游县| 抚州市|