WebSocket 在 C# 中的实际应用:真实项目与高级技术

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

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

开始课程

WebSocket 在 C# 中的实际应用:从原型到生产环境——实时应用的高级技术

\n\n

使用 System.Net.WebSockets 或 SignalR 等库在 C# 中实现标准 WebSocket 可以解决聊天和通知等基本任务。然而,当需求超出“Hello, World”的范围时,开发者就会面临扩展、安全性、自定义路由和性能优化的需求。在本文中,我们将深入探讨在 .NET 中使用 WebSocket 的高级技术,帮助您构建可靠且高负载的实时系统。

\n\n

1. 可扩展 WebSocket 服务器的架构:模式与集群

\n\n

标准 WebSocket 的主要限制之一是连接绑定到特定的进程实例。在水平扩展时,发送到服务器 A 的消息无法到达连接到服务器 B 的客户端。为了解决这个问题,有两种方法:

\n\n
    \n
  • 集中式后端平面(Backplane): 使用 Redis Pub/Sub、RabbitMQ 或 Azure SignalR Service 在服务器之间交换消息。
  • \n
  • 粘性会话(Sticky Sessions): 负载均衡器始终将客户端请求定向到同一台服务器。这种方法可靠性较低,因为当节点宕机时,其所有客户端都会断开连接。
  • \n
\n\n

让我们看一个使用 StackExchange.Redis 和 System.Net.WebSockets 在 Redis 上实现自定义后端平面的示例。

\n\n
using StackExchange.Redis;\nusing System.Net.WebSockets;\nusing System.Text;\n\npublic class RedisBackplaneManager\n{\n    private readonly ConnectionMultiplexer _redis;\n    private readonly ISubscriber _subscriber;\n    private readonly ConcurrentDictionary<string, WebSocket> _connections;\n\n    public RedisBackplaneManager(string redisConnectionString)\n    {\n        _redis = ConnectionMultiplexer.Connect(redisConnectionString);\n        _subscriber = _redis.GetSubscriber();\n        _connections = new ConcurrentDictionary<string, WebSocket>();\n        \n        // 订阅频道 "websocket:messages"\n        _subscriber.Subscribe("websocket:messages", (channel, message) => {\n            var data = Encoding.UTF8.GetString(message);\n            // 解析消息(假设格式为:"connectionId:payload")\n            var parts = data.Split(':', 2);\n            if (parts.Length == 2 && _connections.TryGetValue(parts[0], out var ws))\n            {\n                ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(parts[1])), \n                    WebSocketMessageType.Text, true, CancellationToken.None);\n            }\n        });\n    }\n\n    public async Task SendToClientAsync(string connectionId, string message)\n    {\n        // 发布到 Redis,以便所有服务器都能收到消息\n        var payload = $"{connectionId}:{message}";\n        await _subscriber.PublishAsync("websocket:messages", payload);\n    }\n\n    public void AddConnection(string id, WebSocket ws) => _connections.TryAdd(id, ws);\n    public void RemoveConnection(string id) => _connections.TryRemove(id, out _);\n}
\n\n

2. 自定义协议与路由:将 WebSocket 用作 RPC 传输

\n\n

通常,我们不仅需要发送文本,还需要通过 WebSocket 调用远程过程(RPC)。与其手动解析 JSON 并检查命令,不如构建一个极简的消息路由器。SignalR 在底层使用了这种方法,但您可以为了实现完全控制而自行实现。

\n\n
public class WebSocketRouter\n{\n    private readonly Dictionary<string, Func<string, Task>> _routes = new();\n\n    public void On(string action, Func<string, Task> handler)\n    {\n        _routes[action] = handler;\n    }\n\n    public async Task RouteAsync(string rawMessage)\n    {\n        // 期望格式为                

博客

书籍推荐