- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
在开发环境中,对于实时数据流的需求非常常见,最常用的技术包括 Server-Sent Events (SSE) 和 WebSocket.
SSE (服务器发送事件)是一种基于 HTTP/1.1 协议的传达模型,允许服务器向浏览器不断发送数据更新。它直接使用 HTTP GET 请求,服务器送选用的字符串及内容.
举例: 让我们将一个服务器的实时状态传达给前端浏览器:
在 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 ); } }
在浏览器中使用 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 |
---|---|---|
通讯方式 | 服务器 -> 客户端 | 双向通信 |
使用协议 | HTTP/1.1 | TCP/HTTP/1.1 or HTTP/2 |
解析方式 | 浏览器内置,无需额外应用 | 需要设计应用协议 |
应用场景 | 更新速度不高,如实时通知 | 高频发送,如游戏体验和客制游戏 |
应用端支持 | 原生支持,不需额外学习 | 需要客户端实现 |
考虑问题 | 支持 HTTP 跨域,比 WebSocket 更简单 | 需要第三方应用支持,解决处理诡机 |
SSE 适合于不高频、安全性优先的场景,如通知信息。它具有以下优点:
单向通信的效率:服务器可以在需要时直接推送更新,无需客户端不断轮询,减少资源消耗.
基于 HTTP/1.1 的简单性:由于 SSE 使用标准 HTTP 请求和响应机制,无需额外的协议支持.
与现有 HTTP 基础设施的兼容性:例如,代理服务器、负载均衡器等无需特殊配置即可支持 SSE.
WebSocket 是一种全双工通信协议,基于 TCP 连接。它允许客户端和服务器之间实时双向通信,特别适用于高频、低延迟的应用场景,如在线游戏、实时协作编辑、股票交易和聊天应用.
WebSocket 的特点包括:
支持子协议:例如用于消息格式的 STOMP 和用于加密传输的 WAMP.
自定义消息格式的能力:可以选择 JSON、Protobuf 或二进制数据来优化通信效率.
实时交互场景的处理:WebSocket 的低延迟特性使其能够快速响应用户的实时交互需求.
高效的资源利用:相比于轮询或长轮询,WebSocket 使用单一持久连接,减少了频繁的 HTTP 开销.
此外,WebSocket 在需要多客户端实时同步状态的场景中表现优异,如协作工具(文档编辑、白板)和物联网设备管理.
最后此篇关于ASP.NETCoreEventStream(SSE)使用以及WebSocket比较的文章就讲到这里了,如果你想了解更多关于ASP.NETCoreEventStream(SSE)使用以及WebSocket比较的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想使用 NetworkX Graph 对象作为 Python dict 中的键。但是,我不希望默认的比较行为(即通过对象的地址)。相反,我希望同构图是 dict 中相同元素的键。 此行为是否已在某处
这个问题已经有答案了: What is the most effective way for float and double comparison? (33 个回答) 已关闭 7 年前。 在您认为我
我正在学习 C 编程,为了练习,我找到了一个需要解决的任务。这有点像一个游戏,有人选择一个单词,其他人猜测字母。我必须检查有多少给定的单词可能是所选单词的正确答案。 输入: 3 3//数字 n 和 m
我两天前开始学习C,在做作业时遇到了问题。我们的目的是从字符数组中获取字符列表,并通过计算连续字符并将其替换为数字来缩短它。对“a4b5c5”说“aaaabbbbbccccc”。这是我到目前为止的代码
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
为什么我在 if 中的比较不起作用?答案应该是 8 但它返回 0。 function findMissing(missingArray){ var getArray = missing
我想知道为什么以下 JavaScript 比较会给出不同的结果。 (1==true==1) true (2==true==2) false (0==false==0) false (0==false)
我想知道是否有人可以帮助我完成这个程序。编写一个接受两个字符串的函数。该函数应该将这两个字符串与字典顺序上排在第一位的字符串组合起来。两个字符串之间应该有一个空格。在一行上打印结果字符串。在一行上打印
有谁知道一个免费的开源库(实用程序类),它允许您比较一个 Java bean 的两个实例并返回一个属性列表/数组,这两个实例的值不同?请发布一个小样本。 干杯 托马斯 最佳答案 BeanCompara
我是java新手。任何人都可以给出以下类声明的含义 public class ListNode, V> { K key; V value; ListNode next;
我需要用 C 语言计算和比较 3 种不同大小(100 * 100、1000 * 1000 和 10000 * 10000)的 2 个矩阵相乘的执行时间。我编写了以下简单代码来为 1000 * 1000
当我在 ACCESS 2007 中运行以下 SQL 时 Select Location, COUNT(ApartmentBuildings) AS TotalIBuildingsManaged Fro
根据我对互斥锁的了解——它们通常提供对共享资源的锁定功能。因此,如果一个新线程想要访问这个锁定的共享资源——它要么退出,要么必须不断轮询锁(并在等待锁时浪费处理器周期)。 但是,监视器具有条件变量,它
通常在编程中,不应该比较浮点数据类型是否相等,因为存储的值通常是近似值。 由于两个非整数 Oracle NUMBER 值的存储方式不同(以 10 为基数),是否可以可靠地比较它们是否相等? 最佳答案
使用 PowerShell 时,我们偶尔会比较不同类型的对象。一个常见的场景是 $int -eq $bool (即其中 0 -eq $false 、 0 -ne $true 和任何非零值仅等于真,但不
#include #define MAX 1000 void any(char s1[], char s2[], char s3[]); int main() { char string1[
我想比较两个日期。 从这两个日期中,我只使用 ToShortDateString() 获取日期组件, 如下所示。现在的问题是当我比较两个日期时。它的 throw 错误—— "Operator >= c
用户输入一个数字( float 或整数),并且它必须大于下限。 这是从 UITextField 获取数字的代码: NSNumberFormatter * f = [[NSNumberFormatter
我已经摆弄这段代码大约一个小时了,它让我难以置信。我认为解决方案相当简单,但我似乎无法弄清楚。无论如何,这里去。我制作了一个 javascript 函数来检查用户输入的字符,以便它只能接受 7 个字符
我不太明白为什么当我们在不覆盖 equals 的情况下比较具有相同类属性的两个实例时方法,它将给出 false .但它会给出 true当我们比较一个案例类的两个实例时。例如 class A(val
我是一名优秀的程序员,十分优秀!