SSE服务端单向推送消息到前端

Spring Boot 2.x / 3.x 中不需要单独引入依赖。

  • 只需要引入web依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

后端代码

java 复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

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

@RestController
@RequestMapping("/sse")
@CrossOrigin("*")
public class SseController {

    // 保存每个客户端连接
    private final Map<String, SseEmitter> clients = new ConcurrentHashMap<>();

    /**
     * 客户端建立连接
     * 处理客户端的SSE连接请求,创建并保存连接对象,用于后续向客户端推送消息
     */
    @GetMapping("/connect/{clientId}")
    public SseEmitter connect(@PathVariable String clientId) {
        // 创建SseEmitter对象,参数0L表示连接永不超时(默认有超时时间,0L禁用超时)
        SseEmitter emitter = new SseEmitter(0L); // 永不超时
        // 将客户端ID与对应的SseEmitter存入clients集合(clients应为线程安全的Map,如ConcurrentHashMap,用于管理所有连接)
        clients.put(clientId, emitter);

        // 注册连接完成回调:当连接正常关闭时,从clients中移除该客户端(避免内存泄漏)
        emitter.onCompletion(() -> clients.remove(clientId));
        // 注册超时回调:当连接超时时,从clients中移除该客户端
        emitter.onTimeout(() -> clients.remove(clientId));
        // 注册错误回调:当连接发生错误时,从clients中移除该客户端
        emitter.onError(e -> clients.remove(clientId));

        // 控制台打印客户端连接成功的信息,用于日志跟踪
        System.out.println("Client connected: " + clientId);
        // 返回SseEmitter对象,Spring会自动维护该连接,后续可通过此对象向客户端推送消息
        return emitter;
    }

    /**
     * 群发消息
     * 向所有已连接的客户端推送相同的消息
     */
    @GetMapping("/sendAll")
    public String sendAll(@RequestParam String msg) {
        // 遍历clients集合中所有已连接的客户端(key为clientId,value为对应的SseEmitter)
        clients.forEach((id, emitter) -> {
            try {
                // 通过SseEmitter发送消息:使用event()构建事件,data()设置消息内容(这里添加"群发:"前缀标识消息类型)
                emitter.send(SseEmitter.event().data("群发:" + msg));
            } catch (IOException e) {
                // 若发送失败(如客户端已断开连接),调用complete()关闭该连接
                emitter.complete();
                // 从clients中移除该客户端,避免下次继续尝试发送
                clients.remove(id);
            }
        });
        return "OK";
    }

    /**
     * 单独推送给指定客户端
     * 向指定clientId的客户端推送消息
     */
    // 定义GET请求接口,路径为"/sendTo",用于接收目标客户端ID和消息内容
    @GetMapping("/sendTo")
    // 方法接收请求参数clientId(目标客户端ID)和msg(要发送的消息内容),返回"OK"表示处理成功
    public String sendTo(@RequestParam String clientId, @RequestParam String msg) {
        // 从clients集合中获取指定clientId对应的SseEmitter连接对象
        SseEmitter emitter = clients.get(clientId);
        // 判断连接对象是否存在(客户端是否在线)
        if (emitter != null) {
            try {
                // 通过SseEmitter发送消息:添加"单发:"前缀标识消息类型
                emitter.send(SseEmitter.event().data("单发:" + msg));
            } catch (IOException e) {
                // 发送失败时,从clients中移除该客户端
                clients.remove(clientId);
                // 关闭该连接
                emitter.complete();
            }
        }
        return "OK";
    }
}

前端html

html 复制代码
<!DOCTYPE html>
<html>
<body>
<h3>服务端推送测试 (SSE)</h3>
<h2 id="title"></h2>
<div id="msg"></div>
<script>
	
	const clientId = "client_" + Math.floor(Math.random() * 10000);
	
	document.getElementById("title").textContent=clientId;
	<!-- 创建EventSource对象,建立与服务端的SSE(Server-Sent Events)连接
         连接地址为本地服务的"/sse/connect/"+clientId,服务端可通过clientId识别当前客户端
         SSE是一种服务端向客户端单向推送消息的技术,连接会长期保持 -->
    const source = new EventSource("http://127.0.0.1:8080/sse/connect/"+clientId);
    <!-- 给EventSource对象绑定onmessage事件处理函数:
         当收到服务端发送的默认类型(无指定event字段)消息时触发
         函数逻辑:获取id为"msg"的div,将收到的消息(event.data)用<p>标签包裹后追加到div中,在页面显示 -->
    source.onmessage = function (event) {
        document.getElementById("msg").innerHTML += "<p>" + event.data + "</p>";
    };
    <!-- 另一种方式监听"message"事件(与onmessage作用相同,都是处理默认类型消息)
         这里的逻辑是在控制台打印收到的消息内容,用于调试 -->
    source.addEventListener("message", (e) => {
        console.log("Received:", e.data);
    });
    <!-- 绑定onerror事件处理函数:
         当SSE连接发生错误(如连接中断、服务端异常)时触发
         函数逻辑:在控制台打印错误信息,用于排查问题 -->
    source.onerror = (err) => {
        console.error("EventSource failed:", err);
    };
</script>
</body>
</html>
相关推荐
.又是新的一天.7 小时前
04-Fiddler详解+抓包定位问题
前端·测试工具·fiddler
克里斯蒂亚L7 小时前
OpenLayers - 画全国轨道线路图
前端
GISer_Jing7 小时前
小米前端面试
前端·面试·职场和发展
静西子7 小时前
Vue标签页切换时的异步更新问题
前端·javascript·vue.js
时间的情敌7 小时前
Vue 3.0 源码导读
前端·javascript·vue.js
自由日记7 小时前
css属性使用手册
前端·css·html
mapbar_front8 小时前
基层管理之“跪舔型”leader
前端·程序员
listhi5208 小时前
React Hooks 实现表单验证
前端·javascript·react.js
前端市界9 小时前
当20个并发请求拖垮你的应用:从TCP握手到HTTP/2的终极排错指南
前端