SSE服务器推送:实时网络传输协议详解与实战
1223 字
6 分钟
SSE服务器推送:实时网络传输协议详解与实战
本文基于作者实际开发经验,已在项目中使用 SSE 实现实时数据推送,涵盖客户端监听与服务端事件流实现。
SSE(Server-Sent Events)是基于HTTP的一个轻量级单向传输协议,允许服务器主动向客户端推送实时数据,场景主要有:新闻推送、消息通知、股票行情、实时日志等。
核心特性如下:
- 单向通信
- 基于HTTP
- 长连接(替代轮询)
- 自动重连
1.客户端基本使用方法
这里简单画了个流程图表示生命周期:
- 先是连接成功触发open事件;
- 然后接收message消息是要配置监听的,建议用addEventListener,因为如果使用onMessage无法接收指定消息类型;
- 网络中断,服务器出错都会触发error事件;
- 关闭连接的close方法,通常离开页面就要关闭

//url为后端sse服务器地址,根据地址创建连接const eventSource = new EventSource(url);
// 建立连接触发open事件eventSource.onopen = () => { console.log('✅ 触发open事件,SSE连接已建立');};
// 方式1:使用onmessage属性eventSource.onmessage = function (event) { var data = event.data; console.log('收到数据:', data);};
// 方式2:使用addEventListener监听特定事件类型eventSource.addEventListener('message', function (event) { var data = event.data; console.log('收到数据(监听方式):', data);}, false);
// 异常触发error事件eventSource.onerror = (error) => { console.error('❌ 触发error事件,SSE连接错误:', error);};
// 主动关闭SSE连接eventSource.close();console.log('SSE连接已手动关闭');2.服务器端使用方法
先要了解服务端的实现规范,主要从三个方面入手:HTTP头信息要求、数据传输格式、核心字段。
2.1 HTTP头信息要求
Content-Type: text/event-stream // 必须,指定为事件流类型Cache-Control: no-cache // 必须,禁止缓存,确保数据实时性Connection: keep-alive // 必须,保持长连接2.2 数据传输格式
- 每行格式为
字段: 值\n(字段名后必须跟冒号和空格,结尾用换行符\n) - 多条消息之间用
\n\n(两个换行符)分隔。 - 此外,以
:开头的行是注释(服务器可定期发送注释保持连接)。
换行符必须是
\n(Unix格式),\r\n可能导致客户端解析错误。
2.3 核心字段说明
- data字段:消息内容
- event字段:指定事件类型
- id字段:消息标识,发给谁
- retry字段:重连间隔
3.服务端实现
SSE在SpringBoot项目中,spring-boot-starter-web提供了SSE核心类SseEmitter。
3.1 简单实现
下面是一个简单的实现方式,创建一个接口供前端访客建立SSE长连接,然后提供了广播接口和模拟进度通知接口。
import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.io.IOException;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
@RestControllerpublic class SseController {
private final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>(); private final ExecutorService executor = Executors.newCachedThreadPool();
@GetMapping(value = "/sse/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter subscribe() { SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); emitters.add(emitter);
emitter.onCompletion(() -> emitters.remove(emitter)); emitter.onTimeout(() -> emitters.remove(emitter)); emitter.onError((e) -> emitters.remove(emitter));
try { emitter.send(SseEmitter.event() .name("CONNECTED") .data("You are successfully connected to SSE server!") .reconnectTime(5000)); } catch (IOException e) { emitter.completeWithError(e); }
return emitter; }
@GetMapping("/sse/broadcast") public String broadcastMessage(@RequestParam String message) { executor.execute(() -> { for (SseEmitter emitter : emitters) { try { emitter.send(SseEmitter.event() .name("BROADCAST") .data(message) .id(String.valueOf(System.currentTimeMillis()))); } catch (IOException e) { emitters.remove(emitter); emitter.completeWithError(e); } } }); return "Broadcast message: " + message; }
@GetMapping("/sse/start-task") public String startTask() { executor.execute(() -> { try { for (int i = 0; i <= 100; i += 10) { Thread.sleep(1000); for (SseEmitter emitter : emitters) { try { emitter.send(SseEmitter.event() .name("PROGRESS") .data(i + "% completed") .id("task-progress")); } catch (IOException e) { emitters.remove(emitter); } } if (i == 100) { for (SseEmitter emitter : emitters) { try { emitter.send(SseEmitter.event() .name("COMPLETE") .data("Task completed successfully!")); } catch (IOException e) { emitters.remove(emitter); } } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); return "Task started!"; }}3.2 推荐实现
推荐设计MessageEventType、MessageEvent、SseEmitterManager三层架构:消息的事件类型枚举类、消息事件封装对象、SSE管理工具类。
限于篇幅,推荐实现的核心代码请参考原文中的
MessageEventType枚举、MessageEvent事件类和SseEmitterManager管理类。
4.客户端实现
客户端使用React + antd-mobile实现消息列表,通过SSE接收实时消息推送,分为已读和未读消息。
hook.js 核心逻辑
const createSSEConnection = (url, messageHandlers, setLoading) => { const eventSource = new EventSource(url);
eventSource.onopen = () => { console.log('✅ SSE连接已建立'); setLoading?.(false); };
eventSource.onmessage = (event) => handleSSEMessage(event, messageHandlers);
Object.keys(messageHandlers).forEach(eventType => { eventSource.addEventListener(eventType, (event) => { try { const data = JSON.parse(event.data); messageHandlers[eventType](data); } catch (error) { console.error(`解析${eventType}失败:`, error); } }); });
eventSource.onerror = (error) => { console.error('❌ SSE连接错误:', error); setLoading?.(false); };
return eventSource;};踩坑
- 一定要注意后端发送的事件类型,和客户端监听的要保持一致,要不然客户端收不到消息。


相关推荐
- RabbitMQ从入门到实战:消息队列核心用法详解 - 消息队列与实时通信对比
- SoftEther使用教程:搭建私有VPN服务器完整指南 - 网络安全和远程访问
- Metasploit渗透测试实战:白帽子从入门到精通 - 网络协议安全测试
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
SSE服务器推送:实时网络传输协议详解与实战
https://blog.wmovie.site/posts/sse-protocol-guide/ 相关文章 智能推荐
1
SoftEther使用教程:搭建私有VPN服务器完整指南
软路由与网络 SoftEther作为一款开源VPN软件的安装配置指南,包括Linux服务端安装、Windows管理客户端配置及Linux客户端连接。
2
Frp内网穿透实战:从零开始搭建远程访问
软路由与网络 Frp内网穿透完整教程,包括服务端和客户端配置、多种代理模式、安全设置和常见问题解决。
3
群晖虚拟机安装OpenWRT作旁路由:完整教程+避坑指南
软路由与网络 在群晖NAS虚拟机中安装OpenWRT作为旁路由,实现AdGuard去广告和网络加速功能。
4
ESXi虚拟化软路由:OpenWrt和爱快安装配置全攻略
软路由与网络 在ESXi虚拟化环境下安装OpenWrt和爱快路由系统,包括镜像转换、网卡直通、虚拟机创建和旁路由配置。
5
宝塔面板部署项目踩坑记录:PHP/Java/Node.js
运维自动化 宝塔面板部署各类项目的实战经验,包括PHP、Java、Node.js等框架的部署流程和常见问题解决。
随机文章 随机推荐