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.1
   Upgrade: websocket
   Connection: Upgrade
   Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

2. 服务器响应 101 Switching Protocols
   HTTP/1.1 101 Switching Protocols
   Upgrade: websocket
   Connection: Upgrade
   Sec-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 的底层原理可以概括为:

  1. 传输层:WebSocket 为主,长轮询为备
  2. RPC 机制:双向方法调用,JSON 序列化
  3. 消息路由:自动路由到对应的方法/回调
  4. 连接管理:生命周期管理,自动重连
  5. 性能优化:批处理,连接池,缓存
  6. 安全机制:身份验证,授权策略

这就是为什么 JavaScript 能调用 C# 方法,C# 能调用 JavaScript 函数的根本原理!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helloworddm

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值