C# WebSocket 实时应用入门
现代 Web 应用越来越需要客户端与服务器之间的即时数据传输。传统的 HTTP 协议采用"请求-响应"模式,对于需要低延迟和持续信息更新的场景(如在线聊天、股票行情、多人在线游戏、监控系统)效率不高。WebSocket 协议应运而生,它提供持久的双向连接。
在 C# (.NET) 生态系统中,既可以通过 System.Net.WebSockets 命名空间的内置类实现 WebSocket,也可以使用高级库 SignalR。本文将介绍这两种方法,编写可运行的示例,并讨论实时应用的架构特点。内容适合初学者和有经验的 .NET 开发者。
WebSocket 基础:在 C# 中的工作原理
WebSocket 是一种协议,在初始 HTTP 握手(upgrade request)后,将连接转换为持久的双向模式。在 C# 中,WebSocket 操作基于异步编程,使用 async 和 await 关键字。
用 C# 创建简单的 WebSocket 服务器
我们来看一个基本服务器示例,它接受 WebSocket 连接并向客户端发送回声响应。这里使用内置的 HttpListener 或 Kestrel(ASP.NET Core)。以下是基于 ASP.NET Core 的示例。
// Startup.cs 或 Program.cs(最小 API)var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.UseWebSockets();
app.Map("/ws", async (HttpContext context) =>{ if (context.WebSockets.IsWebSocketRequest) { using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); await Echo(webSocket); } else { context.Response.StatusCode = 400; }});
async Task Echo(WebSocket webSocket){ var buffer = new byte[1024 * 4]; while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync( new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { await webSocket.CloseAsync( WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); } else { // 将数据发送回客户端(回声) await webSocket.SendAsync( new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); } }}
app.Run();代码展示了基本机制:AcceptWebSocketAsync 将 HTTP 请求转换为 WebSocket,而 ReceiveAsync 和 SendAsync 方法实现异步数据交换。正确处理连接状态并优雅关闭连接非常重要。
C# WebSocket 客户端
客户端部分也很简单。使用 ClientWebSocket 类:
using System.Net.WebSockets;
async Task ConnectAndSend(){ using var client = new ClientWebSocket(); await client.ConnectAsync(new Uri("ws://localhost:5000/ws"), CancellationToken.None);
var message = "你好,WebSocket!"; var bytes = Encoding.UTF8.GetBytes(message); await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
var buffer = new byte[1024]; var result = await client.ReceiveAsync( new ArraySegment<byte>(buffer), CancellationToken.None); var response = Encoding.UTF8.GetString(buffer, 0, result.Count); Console.WriteLine($"收