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>
相关推荐
杜子不疼.5 分钟前
【Linux】教你在 Linux 上搭建 Web 服务器,步骤清晰无门槛
linux·服务器·前端
程序员Agions1 小时前
useMemo、useCallback、React.memo,可能真的要删了
前端·react.js
滕青山1 小时前
Vue项目BMI计算器技术实现
前端·vue.js
子兮曰1 小时前
深入浏览器指纹:Canvas、WebGL、Audio是如何暴露你的身份的?
前端·浏览器·canvas
月亮补丁1 小时前
AntiGravity只能生成 1:1 图片?一招破解尺寸限制
前端
何中应1 小时前
MindMap部署
前端·node.js
NAGNIP1 小时前
程序员效率翻倍的快捷键大全!
前端·后端·程序员
一个网络学徒1 小时前
python5
java·服务器·前端
tiantian_cool1 小时前
Claude Opus 4.6 模型新特性(2026年2月5日发布)
前端
0思必得01 小时前
[Web自动化] Selenium获取元素的子元素
前端·爬虫·selenium·自动化·web自动化