在开发环境中,对于实时数据流的需求非常常见,最常用的技术包括 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. SpringCloud入门(四)Ribbon负载均衡

    一.Ribbon负载均衡原理SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的. SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTempl ...

  2. VM Ware 安装mac OS xxx 系统

    1. unlock 文件破解Vm Ware 默认不能安装苹果系统 :https://drive.google.com/file/d/1_AUeYh5JYltqjnuztQh-5UTomAIVBcLZ/ ...

  3. 解决 WebSocketClient.js?5586:16 WebSocket connection to 'ws://192.168.13.25:8080/ws' failed:

    控制台报错: vue.config.js Vue的配置文件 const { defineConfig } = require('@vue/cli-service') module.exports = ...

  4. go~wasm插件的开发

    Go和TinyGo是两种不同的Go语言编译器,它们之间有以下几点区别: 目标平台: Go:Go语言编译器主要面向通用计算机平台,如Windows.Linux.macOS等. TinyGo:TinyGo ...

  5. KubeSphere 边缘节点 IP 冲突的分析和解决思路分享

    在上一篇监控问题排查的文章中,笔者分析了 KubeSphere 3.1.0 集成 KubeEdge 中的边缘监控原理和问题排查思路,在介绍 EdgeWatcher 组件时提到了"边缘节点的内 ...

  6. KubeSphere Cloud 月刊|灾备支持 K8s 1.22+,轻量集群支持安装灾备和巡检组件

    功能升级 备份容灾服务支持 K8s v1.22+ 版本集群 随着 Kubernetes 近一年频繁的发版.升级,越来越多的用户开始部署并使用高版本的 Kubernetes 集群.备份容灾服务支持 Ku ...

  7. KubeSphere DevOps 流水线入门指南

    作者:赵海亮,浙江大学计算机专业四年级在读博士生,研究方向为云计算.边缘计算.分布式系统等. 虽然 KubeSphere 能够将我们从 yaml 文件的编写中解放出来,但是项目上云仍然十分繁琐. 此外 ...

  8. python接口自动化框架-case依赖与SQL依赖以及SQL断言实现

    框架目录: 整体思路: 采用excle数据驱动维护用例,数据依赖:两种实现方法case依赖与sql依赖,优先使用case依赖, 最终运行的用例是: 维护用例在datacase目录下: 涉及到用例信息新 ...

  9. 在react中使用阿里图标库

    参考教程:https://blog.csdn.net/qq_41977441/article/details/110352463 阿里图标库:https://www.iconfont.cn/ 成果展示 ...

  10. PhpStorm 2024.2.4 最新安装教程(附2099年~亲测有效)

    下载安装 下载补丁https://pan.quark.cn/s/fcc23ab8cadf 检查 免责声明:本文中的资源均来自互联网,仅供个人学习和交流使用,严禁用于商业行为,下载后请在24小时内从电脑 ...