轮询、WebSocket 和 SSE:实时通信技术全面指南(含C#实现)
技术概览与对比
1. 轮询 (Polling)
简介:
轮询是一种客户端定期向服务器发送请求以检查更新的伪推送技术。
C# 服务端示例:
// ASP.NET Core 轮询接口示例
[ApiController]
[Route("api/poll")]
public class PollingController : ControllerBase
{
private static DateTime _lastUpdate = DateTime.MinValue;
[HttpGet]
public IActionResult GetUpdates([FromQuery] DateTime lastClientUpdate)
{
// 检查是否有新数据
if (_lastUpdate > lastClientUpdate)
{
return Ok(new {
timestamp = _lastUpdate,
data = "新的轮询数据"
});
}
return NoContent(); // 304也可以
}
// 更新数据的方法(通常由其他服务调用)
public static void UpdateData()
{
_lastUpdate = DateTime.Now;
}
}
客户端使用:
// 前端轮询示例
function startPolling() {
setInterval(async () => {
const response = await fetch('/api/poll?lastClientUpdate=' + lastUpdateTime);
if (response.ok) {
const data = await response.json();
// 处理数据...
lastUpdateTime = data.timestamp;
}
}, 5000); // 每5秒轮询一次
}
2. WebSocket
简介:
WebSocket 提供了全双工、持久的连接,支持真正的实时双向通信。
C# 服务端实现:
// ASP.NET Core WebSocket 示例
public class WebSocketHandler
{
private readonly RequestDelegate _next;
public WebSocketHandler(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await HandleWebSocketConnection(webSocket);
}
else
{
await _next(context);
}
}
private async Task HandleWebSocketConnection(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
// 处理接收到的消息
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {message}");
// 发送响应
string response = $"Echo: {message} at {DateTime.Now}";
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
await webSocket.SendAsync(new ArraySegment<byte>(responseBytes),
WebSocketMessageType.Text, true, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
}
客户端使用:
// 前端WebSocket示例
const socket = new WebSocket('ws://yourserver.com/ws');
socket.onopen = function(e) {
console.log("WebSocket连接已建立");
socket.send("Hello Server!");
};
socket.onmessage = function(event) {
console.log(`收到消息: ${event.data}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`连接正常关闭,code=${event.code} reason=${event.reason}`);
} else {
console.log('连接中断');
}
};
socket.onerror = function(error) {
console.log(`错误: ${error.message}`);
};
3. 服务器发送事件 (SSE)
简介:
SSE 是一种允许服务器向客户端推送数据的单向通信技术。
C# 服务端实现:
// ASP.NET Core SSE 示例
[ApiController]
[Route("api/sse")]
public class SSEController : ControllerBase
{
[HttpGet]
public async Task Get()
{
Response.Headers.Add("Content-Type", "text/event-stream");
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
for (int i = 0; i < 10; i++)
{
var data = new {
message = $"Message {i}",
time = DateTime.Now.ToString("HH:mm:ss")
};
string eventData = $"data: {JsonSerializer.Serialize(data)}\n\n";
await Response.WriteAsync(eventData);
await Response.Body.FlushAsync();
await Task.Delay(1000);
}
Response.Body.Close();
}
}
客户端使用:
// 前端SSE示例
const eventSource = new EventSource('/api/sse');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
eventSource.onopen = function() {
console.log('SSE连接已建立');
};
eventSource.onerror = function() {
console.log('SSE连接错误');
// 自动重连
};
技术对比总结
特性 | 轮询 | WebSocket | SSE |
---|---|---|---|
通信方向 | 客户端→服务器 | 双向 | 服务器→客户端 |
协议 | HTTP | WS/WSS | HTTP |
实现复杂度 | 简单 | 中等 | 简单 |
实时性 | 低(取决于轮询间隔) | 高 | 高 |
资源消耗 | 高(频繁请求) | 低(持久连接) | 中等 |
兼容性 | 所有浏览器 | IE10+, 现代浏览器 | 除IE外的现代浏览器 |
C#支持 | 简单HTTP接口 | 需要中间件处理 | 流式响应 |
适用场景 | 简单更新,兼容性要求高 | 聊天、游戏、实时协作 | 实时通知、数据推送 |
生产环境建议
- WebSocket最佳实践:
- 使用SignalR库(.NET官方实时通信库)
- 处理连接中断和重连
- 考虑消息压缩(特别是高频小消息)
// SignalR 服务端示例
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
- SSE优化建议:
- 设置适当的重试时间(retry字段)
- 使用事件类型区分不同消息
- 考虑心跳机制保持连接
// 带心跳的SSE
await Response.WriteAsync("retry: 5000\n\n"); // 5秒重试
while (true)
{
if (hasData)
{
await Response.WriteAsync($"data: {data}\n\n");
}
else
{
// 心跳
await Response.WriteAsync(": heartbeat\n\n");
}
await Task.Delay(1000);
}
- 降级策略:
// 检测支持情况并提供降级方案
if (HttpContext.WebSockets.IsWebSocketRequest)
{
// WebSocket处理
}
else if (Request.Headers["Accept"] == "text/event-stream")
{
// SSE处理
}
else
{
// 降级到轮询
return PollingEndpoint();
}
总结
- 选择WebSocket:当需要双向实时通信时(如聊天应用、实时游戏)
- 选择SSE:当只需服务器推送且希望简单实现时(如通知、行情更新)
- 选择轮询:仅作为兼容性后备方案或在极简单场景中使用
.NET开发者特别推荐使用SignalR库,它自动选择最佳传输方式(WebSocket、SSE、长轮询等)并处理连接管理、重连等复杂问题。
这只是开胃菜,SignalR 是下一篇要讲的,敬请期待…
【SignalR 完全指南:.NET 实时通信的终极解决方案】飞机票 直达