SignalR 底层原理详解
1. 整体架构概览
客户端 (JavaScript) ←→ 传输层 ←→ SignalR Hub ←→ 业务逻辑↓ ↓ ↓WebSocket HTTP/WebSocket C# 方法长轮询 JSON 序列化 RPC 调用Server-Sent Events 二进制协议
2. 传输层协议
2.1 WebSocket (首选)
// 客户端建立 WebSocket 连接
const connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();// 底层实际是:
const ws = new WebSocket("ws://localhost:5000/chathub");
WebSocket 握手过程:
1. 客户端发送 HTTP Upgrade 请求GET /chathub HTTP/1.1Upgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==2. 服务器响应 101 Switching ProtocolsHTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=3. 建立双向通信通道
2.2 长轮询 (Fallback)
// 当 WebSocket 不可用时,自动降级到长轮询
// 客户端定期发送请求,服务器保持连接打开
长轮询机制:
1. 客户端发送 POST 请求到 /chathub/negotiate
2. 服务器返回连接信息
3. 客户端发送 POST 请求到 /chathub/poll
4. 服务器保持连接打开,等待消息
5. 有消息时立即返回,无消息时超时返回
6. 客户端立即发起下一个请求
3. RPC 调用机制
3.1 客户端到服务器 (Client → Server)
// JavaScript 调用
connection.invoke("SendMessage", "用户名", "消息内容");
底层实现:
// 发送的 JSON 消息
{"protocol": "json","version": 1,"type": 1, // 1 = Invocation"target": "SendMessage","arguments": ["用户名", "消息内容"],"invocationId": "12345"
}
服务器端处理:
// SignalR 自动路由到对应方法
public class ChatHub : Hub
{public async Task SendMessage(string user, string message){// 这里就是 RPC 调用的目标方法await Clients.All.SendAsync("ReceiveMessage", user, message);}
}
3.2 服务器到客户端 (Server → Client)
// C# 服务器调用
await Clients.All.SendAsync("ReceiveMessage", user, message);
底层实现:
// 发送给客户端的 JSON 消息
{"protocol": "json","version": 1,"type": 1, // 1 = Invocation"target": "ReceiveMessage","arguments": ["用户名", "消息内容"]
}
客户端处理:
// JavaScript 接收处理
connection.on("ReceiveMessage", (user, message) => {// 这里就是 RPC 调用的目标函数addMessage(user, message);
});
4. 消息序列化
4.1 JSON 协议 (默认)
// 服务器配置
services.AddSignalR().AddJsonProtocol(options =>{options.PayloadSerializerOptions.PropertyNamingPolicy = null;});
序列化过程:
// C# 对象
var message = new { User = "张三", Content = "你好" };// 序列化为 JSON
{"User": "张三","Content": "你好"
}// 包装为 SignalR 消息
{"protocol": "json","version": 1,"type": 1,"target": "ReceiveMessage","arguments": [{"User": "张三", "Content": "你好"}]
}
4.2 二进制协议 (高性能)
// 使用 MessagePack 序列化
services.AddSignalR().AddMessagePackProtocol();
5. 连接生命周期
5.1 连接建立
1. 客户端发起连接请求
2. 服务器创建 Hub 实例
3. 调用 OnConnectedAsync()
4. 建立传输通道
5. 开始消息交换
5.2 消息路由
客户端消息 → SignalR 路由 → Hub 方法 → 业务逻辑
业务逻辑 → Hub 方法 → SignalR 路由 → 客户端回调
5.3 连接断开
1. 检测连接断开
2. 调用 OnDisconnectedAsync()
3. 清理资源
4. 通知其他客户端
6. 分组和用户管理
6.1 分组机制
// 服务器端
await Groups.AddToGroupAsync(Context.ConnectionId, "房间1");
await Clients.Group("房间1").SendAsync("ReceiveMessage", "房间消息");
底层实现:
// 内部消息格式
{"type": 7, // 7 = AddToGroup"groupName": "房间1"
}
6.2 用户管理
// 服务器端
await Clients.User("用户ID").SendAsync("ReceiveMessage", "私聊消息");
7. 错误处理和重连
7.1 自动重连
const connection = new signalR.HubConnectionBuilder().withUrl("/chathub").withAutomaticReconnect([0, 2000, 10000, 30000]) // 重连间隔.build();
7.2 错误处理
connection.onclose(error => {if (error) {console.log("连接错误:", error);} else {console.log("连接正常关闭");}
});
8. 性能优化
8.1 消息批处理
// 服务器端批量发送
var tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{tasks.Add(Clients.All.SendAsync("ReceiveMessage", $"消息{i}"));
}
await Task.WhenAll(tasks);
8.2 连接池管理
// 服务器端连接管理
public class ChatHub : Hub
{private static readonly ConcurrentDictionary<string, DateTime> _connections = new();public override async Task OnConnectedAsync(){_connections[Context.ConnectionId] = DateTime.UtcNow;await base.OnConnectedAsync();}
}
9. 安全机制
9.1 身份验证
[Authorize]
public class ChatHub : Hub
{public async Task SendMessage(string message){var user = Context.User.Identity.Name;await Clients.All.SendAsync("ReceiveMessage", user, message);}
}
9.2 授权策略
[Authorize(Policy = "ChatPolicy")]
public class ChatHub : Hub
{// 只有通过授权策略的用户才能访问
}
10. 调试和监控
10.1 日志记录
// 启用详细日志
services.AddSignalR().AddHubOptions<ChatHub>(options =>{options.EnableDetailedErrors = true;});
10.2 性能监控
// 连接统计
public class ConnectionStats
{public int TotalConnections { get; set; }public int ActiveConnections { get; set; }public DateTime LastActivity { get; set; }
}
总结
SignalR 的底层原理可以概括为:
- 传输层:WebSocket 为主,长轮询为备
- RPC 机制:双向方法调用,JSON 序列化
- 消息路由:自动路由到对应的方法/回调
- 连接管理:生命周期管理,自动重连
- 性能优化:批处理,连接池,缓存
- 安全机制:身份验证,授权策略
这就是为什么 JavaScript 能调用 C# 方法,C# 能调用 JavaScript 函数的根本原理!