Long Polling vs WebSocket 在 C# 中:为实时应用该如何选择?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

长轮询还是持久通道:C#如何实现实时数据传输

\n\n

想象一下:您正在用C#编写一个聊天应用。或者一个股票交易终端。或者一个通知系统。客户端在等待数据——而且它不愿意久等。过去,开发者们想尽办法:设置定时器,每5秒轮询一次服务器,产生大量多余的请求。如今,我们有两种成熟的方案——Long Polling和WebSocket。两者都允许服务器在客户端没有明确请求的情况下主动发送数据。但它们的运作方式有着本质的不同。

\n\n

本文将详细解析如何在C#(ASP.NET Core)中实现这两种方案,从速度、负载和复杂度方面进行对比,并帮助您理解如何为您的项目做出选择。我们开始吧。

\n\n

什么是Long Polling?(以及为什么它不是魔法)

\n\n

Long Polling是一种在普通HTTP协议之上模拟实时通信的技术。客户端向服务器发送一个请求,但服务器不会立即响应。它会保持连接打开,直到有新的事件发生(或超时)。一旦数据准备就绪——服务器发送响应,客户端处理该响应并立即发起一个新的“长”请求。

\n\n

这是一个巧妙的技巧,可以在任何HTTP服务器和任何浏览器上工作。但它是有代价的:每个连接都会占用服务器内存,如果客户端数量达到数千个,这就会成为一个问题。

\n\n

C# (ASP.NET Core) 中的Long Polling示例

\n\n

我们来编写一个简单的控制器,模拟一个通知队列。客户端调用端点,服务器等待新数据(例如,来自Channel<string>)并将其发送出去。

\n\n
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\n
async 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\n

WebSocket:C#中的双向隧道

\n\n

WebSocket是一个基于TCP的全双工通信协议。在握手(一个带有升级头的普通HTTP GET请求)之后,客户端和服务器直接通信,没有HTTP头部的额外开销。服务器可以随时发送消息,客户端会立即收到。

\n\n

在ASP.NET Core中,WebSocket是内置支持的。不需要第三方库(

博客

书籍推荐