长轮询还是持久通道:C#如何实现实时数据传输
\n\n想象一下:您正在用C#编写一个聊天应用。或者一个股票交易终端。或者一个通知系统。客户端在等待数据——而且它不愿意久等。过去,开发者们想尽办法:设置定时器,每5秒轮询一次服务器,产生大量多余的请求。如今,我们有两种成熟的方案——Long Polling和WebSocket。两者都允许服务器在客户端没有明确请求的情况下主动发送数据。但它们的运作方式有着本质的不同。
\n\n本文将详细解析如何在C#(ASP.NET Core)中实现这两种方案,从速度、负载和复杂度方面进行对比,并帮助您理解如何为您的项目做出选择。我们开始吧。
\n\n什么是Long Polling?(以及为什么它不是魔法)
\n\nLong Polling是一种在普通HTTP协议之上模拟实时通信的技术。客户端向服务器发送一个请求,但服务器不会立即响应。它会保持连接打开,直到有新的事件发生(或超时)。一旦数据准备就绪——服务器发送响应,客户端处理该响应并立即发起一个新的“长”请求。
\n\n这是一个巧妙的技巧,可以在任何HTTP服务器和任何浏览器上工作。但它是有代价的:每个连接都会占用服务器内存,如果客户端数量达到数千个,这就会成为一个问题。
\n\nC# (ASP.NET Core) 中的Long Polling示例
\n\n我们来编写一个简单的控制器,模拟一个通知队列。客户端调用端点,服务器等待新数据(例如,来自Channel<string>)并将其发送出去。
using Microsoft.AspNetCore.Mvc;\nusing System.Threading.Channels;\n\n[ApiController]\n[Route("api/[controller]")]\npublic class NotificationsController : ControllerBase\n{\n private static readonly Channel<string> _channel = Channel.CreateUnbounded<string>();\n\n // 用于发布通知的端点(例如,来自其他服务)\n [HttpPost("publish")]\n public async Task<IActionResult> Publish([FromBody] string message)\n {\n await _channel.Writer.WriteAsync(message);\n return Ok("通知已发送");\n }\n\n // Long Polling:客户端等待,直到有新消息出现\n [HttpGet("long-poll")]\n public async Task<IActionResult> LongPoll(CancellationToken cancellationToken)\n {\n // 等待新消息;如果没有,则最多等待30秒\n using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n cts.CancelAfter(TimeSpan.FromSeconds(30));\n\n try\n {\n var message = await _channel.Reader.ReadAsync(cts.Token);\n return Ok(new { data = message });\n }\n catch (OperationCanceledException)\n {\n // 超时——返回空响应,客户端将重试请求\n return Ok(new { data = (string?)null });\n }\n }\n}\n\n\n客户端(JavaScript)代码看起来像这样:
\n\nasync function startLongPoll() {\n while (true) {\n const response = await fetch('/api/notifications/long-poll');\n const result = await response.json();\n if (result.data) {\n console.log('新通知:', result.data);\n }\n // 立即开始新的轮询\n }\n}\nstartLongPoll();\n\n\n一切都很简单。但有一个问题:如果服务器重启或客户端断开连接,数据将会丢失。Long Polling不保证消息送达。
\n\nWebSocket:C#中的双向隧道
\n\nWebSocket是一个基于TCP的全双工通信协议。在握手(一个带有升级头的普通HTTP GET请求)之后,客户端和服务器直接通信,没有HTTP头部的额外开销。服务器可以随时发送消息,客户端会立即收到。
\n\n在ASP.NET Core中,WebSocket是内置支持的。不需要第三方库(