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;
    }
}

效果图:

相关推荐
酉鬼女又兒2 小时前
零基础快速入门前端ES6 核心特性详解:Set 数据结构与对象增强写法(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·职场和发展·蓝桥杯·es6
慧一居士2 小时前
Vue项目中,子组件调用父组件方法示例,以及如何传值示例,对比使用插槽和不使用插槽区别
前端·vue.js
KOYUELEC光与电子努力加油2 小时前
JAE日本航空电子推出满足汽车市场小型防水最新需求的MX80系列连接器
服务器·科技·单片机·汽车
我是伪码农3 小时前
HTML和CSS复习
前端·css·html
林恒smileZAZ3 小时前
前端实现进度条
前端
前端老石人3 小时前
邂逅前端开发:从基础到实践的全景指南
开发语言·前端·html
阿珊和她的猫3 小时前
以用户为中心的前端性能指标解析
前端·javascript·css
木心术13 小时前
OpenClaw网页前端开发与优化全流程指南
前端·人工智能
Amumu121383 小时前
HTML5的新特性
前端·html·html5