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>
相关推荐
xcLeigh6 分钟前
HTML5实现好看的视频播放器(三种风格,附源码)
前端·音视频·html5
TE-茶叶蛋8 分钟前
html5-qrcode扫码功能
前端·html·html5
2501_906467639 分钟前
HTML5结合Vue3实现百万文件分块上传的思路是什么?
前端·html·html5·vue上传解决方案·vue断点续传·vue分片上传下载·vue分块上传下载
San30.9 分钟前
现代前端工程化实战:从 Vite 到 React Router demo的构建之旅
前端·react.js·前端框架
kirinlau11 分钟前
vue3+vite+scss项目使用tailwindcss
前端·css·scss
阿贾克斯的黎明13 分钟前
现代前端的魔法标签:HTML5 语义化标签全解析
前端·html·html5
菠菜盼娣16 分钟前
vue3知识点
前端·vue.js
JIngJaneIL20 分钟前
基于java+ vue建筑材料管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
土豆125022 分钟前
终端自治时代的 AI 开发范式:Claude Code CLI 全方位实操指南
前端·人工智能·程序员