在工业生产监控、系统实时告警、大屏数据展示等场景中,我们需要服务端主动向前端推送实时数据 。常见方案有轮询、WebSocket、SSE,其中 SSE(Server-Sent Events) 是轻量、简单、稳定 的单向推送方案,基于 HTTP 协议,无需额外依赖,自动重连,完美适配服务端→客户端的单向实时推送需求。
一、什么是 SSE?
SSE 全称 Server-Sent Events(服务器发送事件),是 HTML5 标准协议,核心特点:
- 单向通信:仅服务端 → 前端推送数据(适合监控、告警、数据更新)
- 基于 HTTP:无需升级协议,兼容所有现代浏览器
- 原生自动重连:网络波动 / 服务重启后,浏览器自动重连
- 轻量高效:比 WebSocket 更轻量,比轮询性能高 10 倍以上
- 长连接保活:一次连接,持续推送,无频繁请求开销
适用场景:生产车间监控、系统告警、大屏看板、任务进度推送、日志实时输出。
二、开发环境
- ASP.NET Core 6/7/8(本文以 .NET 8 为例)
- 无需安装任何 NuGet 包(原生内置支持)
- 前端:纯 H5 + 原生 JS(无任何框架依赖)
三、C# 后端实现 SSE 核心代码
创建 SSEController.cs,完整可直接运行,适配生产监控场景:
cs
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace ProductionSchedulingService.Controllers
{
[ApiController]
// ABP 框架默认路由:api/app/[controller]
// 普通 WebAPI 可写:api/[controller]
[Route("api/app/[controller]")]
public class SSEController : ControllerBase
{
/// <summary>
/// SSE 实时推送接口
/// 访问地址:http://IP:端口/api/app/SSE/push
/// </summary>
[HttpGet("push")]
public async Task PushMonitorData(CancellationToken cancellationToken)
{
try
{
// ====================== SSE 固定响应头(必须配置)======================
// 禁用缓存
Response.Headers.CacheControl = "no-cache";
// SSE 标准类型
Response.Headers.ContentType = "text/event-stream";
// 长连接保活
Response.Headers.Connection = "keep-alive";
// 禁用 Nginx 缓冲(生产必备)
Response.Headers["X-Accel-Buffering"] = "no";
// ====================== 模拟生产车间实时数据 ======================
var productionLines = new[] { "纺丝1号线", "纺丝2号线", "捻线3号线" };
var random = new Random();
// 持续推送,客户端断开自动退出
while (!cancellationToken.IsCancellationRequested)
{
// 构建推送数据
var data = new
{
PushTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
LineName = productionLines[random.Next(productionLines.Length)],
Speed = random.Next(800, 1500), // 设备转速
Yield = random.Next(50, 100), // 产量
Status = random.Next(100) > 10 ? "正常运行" : "异常告警"
};
// SSE 标准格式:data: JSON字符串\n\n
string jsonData = JsonSerializer.Serialize(data);
await Response.WriteAsync($"data: {jsonData}\n\n");
// 立即刷新到前端(必须调用)
await Response.Body.FlushAsync(cancellationToken);
// 推送间隔:1秒(可根据业务调整)
await Task.Delay(1000, cancellationToken);
}
}
catch
{
// 客户端断开连接,释放资源
Response.Body.Close();
}
}
}
}
四、端 H5 代码
cs
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>生产车间实时监控(SSE)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei";
}
body {
padding: 20px;
background: #f5f7fa;
}
.title {
text-align: center;
margin-bottom: 20px;
color: #333;
}
/* 生产线卡片容器 */
.line-container {
display: flex;
gap: 20px;
flex-wrap: wrap;
justify-content: center;
}
/* 生产线卡片 */
.line-card {
width: 350px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.line-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #2f4050;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.line-item {
margin: 10px 0;
font-size: 14px;
color: #666;
}
.label {
display: inline-block;
width: 80px;
font-weight: bold;
}
.normal {
color: #00b42a;
font-weight: bold;
}
.warn {
color: #f53f3f;
font-weight: bold;
}
</style>
</head>
<body>
<h1 class="title">📊 生产线实时状态监控(SSE实时推送)</h1>
<!-- 固定生产线卡片,只更新数值,不新增DOM -->
<div class="line-container">
<!-- 纺丝1号线 -->
<div class="line-card" data-line="纺丝1号线">
<div class="line-title">纺丝1号线</div>
<div class="line-item"><span class="label">推送时间:</span><span class="time">--</span></div>
<div class="line-item"><span class="label">运行转速:</span><span class="speed">--</span> r/min</div>
<div class="line-item"><span class="label">当前产量:</span><span class="yield">--</span> kg</div>
<div class="line-item"><span class="label">运行状态:</span><span class="status">--</span></div>
</div>
<!-- 纺丝2号线 -->
<div class="line-card" data-line="纺丝2号线">
<div class="line-title">纺丝2号线</div>
<div class="line-item"><span class="label">推送时间:</span><span class="time">--</span></div>
<div class="line-item"><span class="label">运行转速:</span><span class="speed">--</span> r/min</div>
<div class="line-item"><span class="label">当前产量:</span><span class="yield">--</span> kg</div>
<div class="line-item"><span class="label">运行状态:</span><span class="status">--</span></div>
</div>
<!-- 捻线3号线 -->
<div class="line-card" data-line="捻线3号线">
<div class="line-title">捻线3号线</div>
<div class="line-item"><span class="label">推送时间:</span><span class="time">--</span></div>
<div class="line-item"><span class="label">运行转速:</span><span class="speed">--</span> r/min</div>
<div class="line-item"><span class="label">当前产量:</span><span class="yield">--</span> kg</div>
<div class="line-item"><span class="label">运行状态:</span><span class="status">--</span></div>
</div>
</div>
<script>
// SSE连接地址
const sse = new EventSource('http://172.16.34.22:55021/api/app/SSE/push');
// 连接成功
sse.onopen = function () {
console.log("✅ SSE 连接成功");
};
// 接收服务端推送 → 精准更新对应生产线数据
sse.onmessage = function (event) {
const data = JSON.parse(event.data);
// 1. 找到对应生产线的DOM
const lineCard = document.querySelector(`[data-line="${data.LineName}"]`);
if (!lineCard) return;
// 2. 只更新数值,不新增节点(核心优化)
lineCard.querySelector('.time').textContent = data.PushTime;
lineCard.querySelector('.speed').textContent = data.Speed;
lineCard.querySelector('.yield').textContent = data.Yield;
// 3. 更新状态样式
const statusDom = lineCard.querySelector('.status');
statusDom.textContent = data.Status;
statusDom.className = data.Status === "正常运行" ? "normal" : "warn";
};
// 连接错误(自动重连)
sse.onerror = function (err) {
console.log("🔄 连接断开,浏览器自动重连中...");
};
</script>
</body>
</html>
五、生产环境最佳实践
- 推送频率:1~3 秒一次,兼顾实时性与性能
- 连接数:单服务器支持 10000+ SSE 连接,无压力
- 异常处理:服务端捕获断开异常,释放资源
- 监控:对接日志系统,监控 SSE 连接状态
- 集群部署:多实例部署,Nginx 负载均衡,支持大规模客户端
六、总结
SSE 是 ASP.NET Core 实现单向实时推送的最优方案:
- ✅ 零依赖、原生支持
- ✅ 自动重连、长连接保活
- ✅ 代码简洁、生产稳定
- ✅ 适配工业监控、大屏、告警等场景
生产环境 Nginx 部署配置
SSE 是长连接,Nginx 默认会缓冲数据、60 秒自动断开,必须专用配置:
cs
server {
listen 80;
server_name 172.16.34.22;
# 前端静态页面部署
root html;
index index.html;
# SSE 专用代理(核心配置)
location /api/app/SSE/push {
proxy_pass http://127.0.0.1:55021; # 转发到后端 .NET 服务
proxy_buffering off; # 关闭缓冲,实时推送
proxy_http_version 1.1;
proxy_set_header Connection "";
gzip off; # 关闭压缩
# 24小时超时(保活不断连)
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
# 普通接口代理
location /api {
proxy_pass http://127.0.0.1:55021;
}
}
效果图:
