Files
blog-press/docs/Web/SpringBoot/SpringBoot-WebSocket.md
2026-06-08 10:20:45 +08:00

4.3 KiB
Raw Blame History

title, date
title date
SpringBoot WebSocket使用 2026-06-08

一、简介

WebSocket API

二、安装

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

三、使用

3.1 定义WebSocketHandler

@Component
public class GenericWebSocketHandler extends TextWebSocketHandler {
    private static final Map<String, WebSocketSession> SESSION_MAP =
            new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String userId = getUserId(session);
        SESSION_MAP.put(userId, session);
        log("用户连接成功:" + userId);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String userId = getUserId(session);
        SESSION_MAP.remove(userId);
        log("用户断开连接:" + userId);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        String payload = message.getPayload();

        // 心跳处理
        if ("PING".equalsIgnoreCase(payload)) {
            session.sendMessage(new TextMessage("PONG"));
            return;
        }

        // 普通消息(可按 JSON 协议扩展)
        log("收到消息:" + payload);
        session.sendMessage(new TextMessage("服务端已收到:" + payload));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        log("传输异常:" + exception.getMessage());
    }

    /** 从 session 中获取用户标识(握手阶段放入) */
    private String getUserId(WebSocketSession session) {
        return (String) session.getAttributes().getOrDefault("userId", session.getId());
    }

    /** 单发 */
    public void sendToUser(String userId, String msg) throws IOException {
        WebSocketSession session = SESSION_MAP.get(userId);
        if (session != null && session.isOpen()) {
            session.sendMessage(new TextMessage(msg));
        }
    }

    /** 广播 */
    public void broadcast(String msg) throws IOException {
        for (WebSocketSession session : SESSION_MAP.values()) {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(msg));
            }
        }
    }

    /** 在线人数 */
    public int onlineCount() {
        return SESSION_MAP.size();
    }
}

3.2 定义HandshakeInterceptor

@Component
public class GenericHandshakeInterceptor implements HandshakeInterceptor {

    /**
     * 握手前执行
     */
    @Override
    public boolean beforeHandshake(
            ServerHttpRequest request,
            ServerHttpResponse response,
            WebSocketHandler wsHandler,
            Map<String, Object> attributes) {
        // 从请求中URL中获取id  
        // 例如ws://localhost:8080/ws?userId=123多个参数用&连接
        String userId = servletRequest.getServletRequest().getParameter("userId");

        if (userId == null) {
            // 返回 false → 握手失败 → 连接被拒绝
            return false;
        }

        // 放入 attributes 中,方便下次获取
        attributes.put("userId", userId);

        return true;
    }

    /**
     * 握手完成后执行(一般不用写)
     */
    @Override
    public void afterHandshake(
            ServerHttpRequest request,
            ServerHttpResponse response,
            WebSocketHandler wsHandler,
            Exception exception) {
    }
}

3.3 配置WebSocketConfigurer

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Resource
    private GenericWebSocketHandler genericWebSocketHandler;

    @Resource
    private GenericHandshakeInterceptor genericHandshakeInterceptor;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(genericWebSocketHandler, "/ws")
              .addInterceptors(genericHandshakeInterceptor)
              .setAllowedOrigins("*");
    }
}

WebSocket连接地址ip:port/ws?userId=123。

四、前端使用

  参考:useWebSocket