SpringBoot使用WebSocket

SpringBoot使用WebSocket

mvn依赖

java 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

后端配置文件

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WS服务

java 复制代码
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ServerEndpoint("/ws/{userId}")
public class WebSocketServer {

    private static final ConcurrentHashMap<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    private String userId;

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.userId = userId;
        SESSION_MAP.put(userId, session);
        log.info("用户连接成功,userId={}, 当前在线人数={}", userId, SESSION_MAP.size());
    }

    @OnClose
    public void onClose() {
        if (userId != null) {
            SESSION_MAP.remove(userId);
            log.info("用户连接关闭,userId={}, 当前在线人数={}", userId, SESSION_MAP.size());
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到用户{}的消息:{}", userId, message);

        String response = "服务端已收到:" + message;

        try {
            session.getBasicRemote().sendText(response);
        } catch (IOException e) {
            log.error("发送消息失败,userId={}", userId, e);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket异常,userId={}", userId, error);
    }

    public static void sendMessageToAll(String message) {
        SESSION_MAP.forEach((uid, session) -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("广播消息失败,userId={}", uid, e);
            }
        });
    }

    public static void sendMessageToUser(String userId, String message) {
        Session session = SESSION_MAP.get(userId);
        if (session != null) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("发送消息给指定用户失败,userId={}", userId, e);
            }
        }
    }
}

前端测试文件

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 测试</title>
</head>
<body>

<h3>WebSocket 测试</h3>

用户ID:<input id="userId" value="user1"/>
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>

<br/><br/>

发送消息:<input id="message"/>
<button onclick="send()">发送</button>

<br/><br/>

<div>
    <strong>日志:</strong>
    <pre id="log"></pre>
</div>

<script>
    let ws;

    function log(text) {
        document.getElementById('log').innerText += text + '\n';
    }

    function connect() {
        const userId = document.getElementById('userId').value;
        ws = new WebSocket('ws://localhost:8080/ws/' + userId);

        ws.onopen = function () {
            log('连接成功');
        };

        ws.onmessage = function (event) {
            log('收到消息:' + event.data);
        };

        ws.onclose = function () {
            log('连接关闭');
        };

        ws.onerror = function (error) {
            log('连接异常');
            console.error(error);
        };
    }

    function disconnect() {
        if (ws) {
            ws.close();
        }
    }

    function send() {
        const message = document.getElementById('message').value;
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(message);
        } else {
            log('WebSocket 未连接');
        }
    }
</script>

</body>
</html>
相关推荐
回家路上绕了弯2 分钟前
为什么还是不能直接把需求丢给 AI?
后端
锋行天下40 分钟前
服务器 Python 2.7 装不了 langgraph-cli?用本地生成 Dockerfile 曲线救国
人工智能·后端·python
桦说编程1 小时前
TransmittableThreadLocal 解读:从 CRR 机制到上下文所有权
java·后端·源码
喵个咪1 小时前
GoWind 拆解:全链 ID 类型体系——int64 定版与前端字符串化契约
前端·后端·go
喵个咪2 小时前
GoWind CMS 拆解:发布审批——多级推进、逐级角色与全程留痕
前端·后端·go
喵个咪2 小时前
GoWind Flow 拆解:一个 Go 工作流引擎与它的双模式设计器
前端·后端·go
喵个咪2 小时前
GoWind CMS 拆解:搜索三板斧——高亮、联想、同义词
后端·搜索引擎·go
喵个咪2 小时前
GoWind 拆解:AI Agent 编排引擎——波次并行、子图递归与人工审批
人工智能·后端·go
喵个咪2 小时前
GoWind Admin 拆解:打印模板设计器——从画布到服务端 PDF 的完整打印链路
前端·后端·go
喵个咪3 小时前
GoWind Admin 拆解:BI 大屏设计器——schema 即 JSON、resolver 注入与三张快照表的口径谱系
大数据·后端·go