ASP.NET Core 实现 SSE 服务器推送|生产级实战教程(含跨域 / Nginx / 前端完整代码)

在工业生产监控、系统实时告警、大屏数据展示等场景中,我们需要服务端主动向前端推送实时数据 。常见方案有轮询、WebSocket、SSE,其中 SSE(Server-Sent Events)轻量、简单、稳定 的单向推送方案,基于 HTTP 协议,无需额外依赖,自动重连,完美适配服务端→客户端的单向实时推送需求。

一、什么是 SSE?

SSE 全称 Server-Sent Events(服务器发送事件),是 HTML5 标准协议,核心特点:

  1. 单向通信:仅服务端 → 前端推送数据(适合监控、告警、数据更新)
  2. 基于 HTTP:无需升级协议,兼容所有现代浏览器
  3. 原生自动重连:网络波动 / 服务重启后,浏览器自动重连
  4. 轻量高效:比 WebSocket 更轻量,比轮询性能高 10 倍以上
  5. 长连接保活:一次连接,持续推送,无频繁请求开销

适用场景:生产车间监控、系统告警、大屏看板、任务进度推送、日志实时输出。

二、开发环境

  • 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. 推送频率:1~3 秒一次,兼顾实时性与性能
  2. 连接数:单服务器支持 10000+ SSE 连接,无压力
  3. 异常处理:服务端捕获断开异常,释放资源
  4. 监控:对接日志系统,监控 SSE 连接状态
  5. 集群部署:多实例部署,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;
    }
}

效果图:

相关推荐
漂流瓶jz6 小时前
Webpack如何实现万物皆可import?loader的使用/配置/手写实践
前端·javascript·webpack
ZC跨境爬虫6 小时前
跟着 MDN 学CSS day_41:显式轨道、隐式网格与区域命名放置
前端·javascript·css·ui·交互
稳联技术老娜7 小时前
DeviceNet主站怎么连接西门子PLC,Profinet网关配置手册(那智机器人)
服务器·网络·数据库
9分钟带帽7 小时前
linux_系统开机自动执行shell脚本
linux·服务器
修己xj7 小时前
告别手动存图!这款叫 Fatkun 的浏览器插件,简直是素材收集神器
前端
袋鼠云数栈8 小时前
从前端到基础设施,ACOS 如何打通企业全链路可观测
运维·前端·人工智能·数据治理·数据智能
AskHarries8 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库
Moment8 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
消失在人海中8 小时前
oracle 数据库多表关联查询
服务器·数据库·oracle
qcx239 小时前
【系统学AI】25 论文导读 ①:两篇改变 AI 的开山之作——Attention Is All You Need & ReAct
前端·人工智能·react.js·transformer