1. 握手协议 (Negotiation)
1.1 客户端发起握手
POST /chathub/negotiate HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Connection: keep-alive
{
"protocol": "json",
"version": 1
}
1.2 服务器响应
HTTP/1.1 200 OK
Content-Type: application/json
{
"connectionId": "abc123",
"availableTransports": [
{
"transport": "WebSockets",
"transferFormats": ["Text", "Binary"]
},
{
"transport": "ServerSentEvents",
"transferFormats": ["Text"]
},
{
"transport": "LongPolling",
"transferFormats": ["Text", "Binary"]
}
]
}
2. WebSocket 消息格式
2.1 消息类型枚举
public enum MessageType
{
Invocation = 1, // 方法调用
StreamItem = 2, // 流数据项
Completion = 3, // 完成
StreamInvocation = 4, // 流调用
CancelInvocation = 5, // 取消调用
Ping = 6, // 心跳
Pong = 7, // 心跳响应
Close = 8, // 关闭连接
Ack = 9 // 确认
}
2.2 实际消息示例
客户端调用服务器方法
{
"protocol": "json",
"version": 1,
"type": 1, // Invocation
"target": "SendMessage",
"arguments": ["张三", "你好世界"],
"invocationId": "12345"
}
服务器调用客户端方法
{
"protocol": "json",
"version": 1,
"type": 1, // Invocation
"target": "ReceiveMessage",
"arguments": ["张三", "你好世界"]
}
心跳消息
{
"protocol": "json",
"version": 1,
"type": 6 // Ping
}
3. 长轮询协议
3.1 轮询请求
POST /chathub/poll?connectionId=abc123 HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Connection: keep-alive
{
"protocol": "json",
"version": 1
}
3.2 轮询响应
HTTP/1.1 200 OK
Content-Type: application/json
Connection: keep-alive
{
"messages": [
{
"protocol": "json",
"version": 1,
"type": 1,
"target": "ReceiveMessage",
"arguments": ["张三", "你好世界"]
}
]
}
4. 二进制协议 (MessagePack)
4.1 消息结构
[消息类型][消息长度][消息内容]
4.2 序列化示例
// C# 对象
var message = new { User = "张三", Content = "你好" };
// MessagePack 序列化后的字节数组
[0x01, 0x00, 0x00, 0x00, 0x1A, 0x82, 0xA4, 0x55, 0x73, 0x65, 0x72, 0xA6, 0xE5, 0xBC, 0xA0, 0xE4, 0xB8, 0x89, 0xA7, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0xA6, 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD]
5. 连接管理
5.1 连接状态
public enum ConnectionState
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Reconnecting = 3
}
5.2 连接生命周期
1. 创建连接对象
2. 发起握手请求
3. 选择传输协议
4. 建立连接通道
5. 开始消息交换
6. 处理断开/重连
6. 错误处理
6.1 错误消息格式
{
"protocol": "json",
"version": 1,
"type": 3, // Completion
"invocationId": "12345",
"error": "方法 'SendMessage' 不存在",
"result": null
}
6.2 常见错误类型
- 连接错误: 网络中断、服务器不可用
- 协议错误: 消息格式不正确
- 方法错误: 调用的方法不存在
- 参数错误: 参数类型不匹配
- 授权错误: 没有权限访问方法
7. 性能优化
7.1 消息批处理
{
"protocol": "json",
"version": 1,
"type": 1,
"target": "ReceiveMessage",
"arguments": [
["用户1", "消息1"],
["用户2", "消息2"],
["用户3", "消息3"]
]
}
7.2 压缩传输
// 启用 GZIP 压缩
services.AddSignalR()
.AddHubOptions<ChatHub>(options =>
{
options.EnableDetailedErrors = true;
options.MaximumReceiveMessageSize = 32 * 1024;
});
8. 安全机制
8.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);
}
}
8.2 授权策略
[Authorize(Policy = "ChatPolicy")]
public class ChatHub : Hub
{
// 只有通过授权策略的用户才能访问
}
9. 调试技巧
9.1 启用详细日志
services.AddSignalR()
.AddHubOptions<ChatHub>(options =>
{
options.EnableDetailedErrors = true;
});
9.2 客户端调试
// 启用详细日志
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Debug)
.build();
10. 实际应用场景
10.1 聊天应用
- 实时消息传递
- 用户状态同步
- 群组管理
10.2 游戏服务器
- 实时游戏状态同步
- 玩家动作广播
- 游戏事件通知
10.3 IoT 设备
- 设备状态监控
- 远程控制指令
- 数据采集
总结
SignalR 的底层原理可以概括为:
- 协议层: WebSocket + 长轮询 + Server-Sent Events
- 消息层: JSON/MessagePack 序列化
- RPC层: 双向方法调用机制
- 传输层: HTTP/WebSocket 协议
- 安全层: 身份验证 + 授权策略
这就是为什么 JavaScript 能调用 C# 方法,C# 能调用 JavaScript 函数的根本原理!

1172

被折叠的 条评论
为什么被折叠?



