在开发环境中,对于实时数据流的需求非常常见,最常用的技术包括 Server-Sent Events (SSE) 和 WebSocket。

什么是 Server-Sent Events (SSE)?

SSE (服务器发送事件)是一种基于 HTTP/1.1 协议的传达模型,允许服务器向浏览器不断发送数据更新。它直接使用 HTTP GET 请求,服务器送选用的字符串及内容。

举例: 让我们将一个服务器的实时状态传达给前端浏览器:

1. 添加服务器端 API

在 ASP.NET Core 中实现 SSE,示例是一个简单的项目实时监控。

项目结构如下:

Starup.cs文件新增如下代码:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace WebApplication
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{ services.AddControllers();
// 允许跨域请求
services.AddCors(options =>
{
options.AddPolicy("AllowLocalhost",
builder => builder.WithOrigins("https://localhost:5001") // 允许来自 https://localhost:5001 的请求
.AllowAnyHeader() // 允许任何头部
.AllowAnyMethod()); // 允许任何方法
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication", Version = "v1" });
});
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication v1"));
}
// 启用 CORS 中间件
app.UseCors("AllowLocalhost"); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization();
// 启用静态文件中间件
app.UseStaticFiles(); // 默认提供 wwwroot 下的静态文件 app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

控制器代码:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using System.Runtime.InteropServices; namespace WebApplication.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ServerStatusController : ControllerBase
{
// 定义性能计数器来获取 CPU 使用率
private readonly PerformanceCounter _cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); [HttpGet("status")]
public async Task GetServerStatus()
{
// 设置响应头,声明是 SSE 流
Response.ContentType = "text/event-stream";
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive"); // 获取当前进程的基本信息
var process = Process.GetCurrentProcess(); await using var writer = new StreamWriter(Response.Body, Encoding.UTF8, leaveOpen: true); while (!HttpContext.RequestAborted.IsCancellationRequested)
{
// 获取 CPU 使用率
var cpuUsage = _cpuCounter.NextValue(); // CPU 使用率百分比
var memoryUsage = process.WorkingSet64 / (1024 * 1024); // 内存使用(MB)
var uptime = (DateTime.Now - process.StartTime).ToString(@"hh\:mm\:ss"); // 服务器运行时间 // 获取系统的磁盘使用情况
var diskUsage = GetDiskUsage(); // 获取系统的网络使用情况(假设 Windows 上可用)
var networkUsage = new NetworkUsage().GetNetworkUsage(); // 构建状态信息
var status = new
{
CPU = $"{cpuUsage:F2}%",
Memory = $"{memoryUsage} MB",
Uptime = uptime,
DiskUsage = diskUsage,
NetworkUsage = networkUsage,
Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
}; // 将状态信息转化为 JSON 格式并发送
await writer.WriteLineAsync($"data: {System.Text.Json.JsonSerializer.Serialize(status)}\n");
await writer.FlushAsync(); // 确保立即推送数据
await Task.Delay(1000*2); // 每秒更新一次
}
} // 获取磁盘使用情况(Windows)
private string GetDiskUsage()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var drive = DriveInfo.GetDrives().FirstOrDefault(d => d.IsReady);
if (drive != null)
{
return $"{drive.TotalFreeSpace / (1024 * 1024 * 1024)} GB free of {drive.TotalSize / (1024 * 1024 * 1024)} GB";
}
}
return "N/A";
} }
}

网路获取类:

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text; public class NetworkUsage
{
public string GetNetworkUsage()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return GetWindowsNetworkUsage();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return GetLinuxNetworkUsage();
}
else
{
return "Unsupported operating system.";
}
} private string GetWindowsNetworkUsage()
{
try
{
// 获取 PerformanceCounter 支持的所有网络接口实例
var category = new PerformanceCounterCategory("Network Interface");
var validInstances = category.GetInstanceNames(); // 返回支持的实例名称 // 获取系统中活动的网络接口
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up
&& validInstances.Contains(ni.Description)) // 匹配实例名称
.ToList(); if (!interfaces.Any())
{
return "No valid network interfaces found.";
} var result = new StringBuilder(); foreach (var iface in interfaces)
{
try
{
var networkIn = new PerformanceCounter("Network Interface", "Bytes Received/sec", iface.Description);
var networkOut = new PerformanceCounter("Network Interface", "Bytes Sent/sec", iface.Description); var receivedBytes = networkIn.NextValue() / (1024 * 1024); // 转换为 MB
var sentBytes = networkOut.NextValue() / (1024 * 1024); // 转换为 MB result.AppendLine($"{iface.Name} ({iface.Description}): {receivedBytes:F2} MB received, {sentBytes:F2} MB sent per second");
}
catch (Exception ex)
{
result.AppendLine($"Error retrieving data for {iface.Name} ({iface.Description}): {ex.Message}");
}
} return result.ToString();
}
catch (Exception ex)
{
return $"Error retrieving network usage on Windows: {ex.Message}";
}
} private string GetLinuxNetworkUsage()
{
try
{
if (!File.Exists("/proc/net/dev"))
return "Unable to access network statistics (Linux only)"; string[] lines = File.ReadAllLines("/proc/net/dev"); var networkInterfaces = lines
.Skip(2) // 跳过前两行标题
.Select(line => line.Trim())
.Where(line => line.Contains(":"))
.Select(ParseNetworkLine)
.ToList(); return string.Join("\n", networkInterfaces.Select(ni =>
$"{ni.Interface}: {ni.ReceivedMB:F2} MB received, {ni.TransmittedMB:F2} MB sent"));
}
catch (Exception ex)
{
return $"Error retrieving network usage on Linux: {ex.Message}";
}
} private (string Interface, double ReceivedMB, double TransmittedMB) ParseNetworkLine(string line)
{
var parts = line.Split(new[] { ' ', ':' }, StringSplitOptions.RemoveEmptyEntries);
string interfaceName = parts[0]; long receivedBytes = long.Parse(parts[1]); // 接收字节
long transmittedBytes = long.Parse(parts[9]); // 发送字节 return (
Interface: interfaceName,
ReceivedMB: receivedBytes / (1024.0 * 1024.0), // 转换为 MB
TransmittedMB: transmittedBytes / (1024.0 * 1024.0) // 转换为 MB
);
}
}

2. 前端展示 SSE

在浏览器中使用 JavaScript 接收服务器数据:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Status</title>
</head>
<body>
<h1>Server Status</h1>
<div id="status">
<p>Loading...</p>
</div> <script>
const eventSource = new EventSource('/api/serverstatus/status'); eventSource.onmessage = function (event) {
const status = JSON.parse(event.data); document.getElementById('status').innerHTML = `
<p><strong>CPU Usage:</strong> ${status.CPU}</p>
<p><strong>Memory Usage:</strong> ${status.Memory}</p>
<p><strong>Uptime:</strong> ${status.Uptime}</p>
<p><strong>Disk Usage:</strong> ${status.DiskUsage}</p>
<p><strong>Network Usage:</strong> ${status.NetworkUsage}</p>
<p><strong>Timestamp:</strong> ${status.Timestamp}</p>
`;
}; eventSource.onerror = function (error) {
console.error("Error occurred: ", error);
};
</script>
</body>
</html>

运行网站后效果如下,2s刷新一次:

比较 SSE 和 WebSocket

特性 SSE WebSocket
通讯方式 服务器 -> 客户端 双向通信
使用协议 HTTP/1.1 TCP/HTTP/1.1 or HTTP/2
解析方式 浏览器内置,无需额外应用 需要设计应用协议
应用场景 更新速度不高,如实时通知 高频发送,如游戏体验和客制游戏
应用端支持 原生支持,不需额外学习 需要客户端实现
考虑问题 支持 HTTP 跨域,比 WebSocket 更简单 需要第三方应用支持,解决处理诡机

总结

    • SSE 适合于不高频、安全性优先的场景,如通知信息。它具有以下优点:

    1. 单向通信的效率:服务器可以在需要时直接推送更新,无需客户端不断轮询,减少资源消耗。

    2. 基于 HTTP/1.1 的简单性:由于 SSE 使用标准 HTTP 请求和响应机制,无需额外的协议支持。

    3. 与现有 HTTP 基础设施的兼容性:例如,代理服务器、负载均衡器等无需特殊配置即可支持 SSE。

    • WebSocket 是一种全双工通信协议,基于 TCP 连接。它允许客户端和服务器之间实时双向通信,特别适用于高频、低延迟的应用场景,如在线游戏、实时协作编辑、股票交易和聊天应用。

    WebSocket 的特点包括:

    1. 支持子协议:例如用于消息格式的 STOMP 和用于加密传输的 WAMP。

    2. 自定义消息格式的能力:可以选择 JSON、Protobuf 或二进制数据来优化通信效率。

    3. 实时交互场景的处理:WebSocket 的低延迟特性使其能够快速响应用户的实时交互需求。

    4. 高效的资源利用:相比于轮询或长轮询,WebSocket 使用单一持久连接,减少了频繁的 HTTP 开销。

    此外,WebSocket 在需要多客户端实时同步状态的场景中表现优异,如协作工具(文档编辑、白板)和物联网设备管理。

ASP.NET Core EventStream (SSE) 使用以及 WebSocket 比较的更多相关文章

  1. ASP.NET Core Building chat room using WebSocket

    Creating “Login form” We use here simple form where user can insert his or her preferred nick name f ...

  2. WebSocket in ASP.NET Core

    一.WebSocket WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有1.1和1.0 ...

  3. 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件

    先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...

  4. 快速搭建CentOS+ASP.NET Core环境支持WebSocket

    环境:CentOS 7.x,.net core 2 以下.net core 2安装操作为官方方法.如果你使用Docker,那么更简单了,只需要docker pull microsoft/dotnet就 ...

  5. Real-time chart using ASP.NET Core and WebSocket

    Solution in glance The following diagram illustrates our solution where IoT device reports readings ...

  6. WebSocket In ASP.NET Core(一)

    .NET-Core Series Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error Handling in ...

  7. 在Asp.net Core中使用中间件来管理websocket

    介绍 ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理.但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容. ...

  8. ASP.NET Core 集成 WebSocket

    1. 环境 AspNetCore Web 2.0 (MVC) Windows 10 IIS 10 Express/IIS VS 2017 2.如何配置 在已有的或者新创建的 AspNet Core M ...

  9. ASP.NET Core 中的 WebSocket 支持(转自MSDN)

    本文介绍 ASP.NET Core 中 WebSocket 的入门方法. WebSocket (RFC 6455) 是一个协议,支持通过 TCP 连接建立持久的双向信道. 它用于从快速实时通信中获益的 ...

  10. asp.net core系列 70 即时通迅-WebSocket+Redis发布订阅

    一.概述 在asp.net core 中可以用WebSocket 或asp.net core SignalR来开发即时通迅.在项目中由于开发前后端分离,对于SignalR前端技术人员不想依赖juqer ...

随机推荐

  1. Linux_动态库与静态库(其一)

    1.动态库和静态库的定义 动态库(.so):动态库是编译后不嵌入目标文件中的共享库,在程序运行的时候才去链接动态库的代码,可以被多个程序共享使用,通常以 .so 结尾. 静态库(.a):静态库是将一组 ...

  2. Linux系统启动速度优化工具systemd-analyze

    systemd-analyze简介 systemd-analyze是Linux自带的分析系统启动性能的工具. systemd-analyze可使用的命令: systemd-analyze [OPTIO ...

  3. USB-A, Micro, lightning and USB-C

  4. 【Simpleperf】Android的CPU分析,性能优化利器

    很多时候,写代码是一件很爽的事情,但最后需要对APP进行瘦身.性能分析却是一件很棘手的事情.当需要对APP的性能进行分析时,Simpleperf是一个简单快捷的选择. 正文开始前,先奉上官方的资料: ...

  5. android 性能优化 -systrace

    简介: Systrace允许监视和跟踪Android系统的行为(trace).它会指明系统都在哪些工作上花费时间.CPU周期都用在哪里,甚至可以看到每个线程.进程在指定时间内都在干嘛.它同时还会突出观 ...

  6. Vue3的生命周期函数

        选项式 API 组合式API beforeCreate 不需要 created  不需要 beforeMount onBeforeMount mounted onMounted beforeU ...

  7. 008 Python、Anaconda、pip、Pycharm、Jupyter 的下载

    下述所有软件的具体做法:https://www.cnblogs.com/nickchen121/p/10718112.html python 下载 搜一下 python:https://www.pyt ...

  8. keycloak~token配置相关说明

    会话有效期 在 Keycloak 中,"SSO Session Idle" 和 "SSO Session Max" 是用于配置单点登录(SSO)会话的两个参数. ...

  9. Python3 编程面试题

    Python global 语句的作用 lambda 匿名函数好处 Python 错误处理 Python 内置错误类型 简述 any() 和 all() 方法 Python 中什么元素为假? 提高 P ...

  10. TXT文本Log日志分割工具(附工具链接)

    前言 相信大家也会像我一样,生产出现了问题,拿下来的日志,用文本编辑器打开直接卡死,甚至说非常卡,查起来非常麻烦且费时间 当当当当 ~~~~~~ 又小,免费非安装的TXT文件分割器就此诞生 链接地址: ...