Framework4.0连接AspNetCore SignalR Hub

最近有项目要使用AspNetCore重构,其中包含了AspNetCore SignalR Hub服务,而我们的桌面端程序使用的还是Framework4.0版本开发的(因为依然有客户在使用XP的系统,而XP系统最高支持到Framework4.0版本),微软的Microsoft.AspNet.SignalR.Client在Framework4.0下只是1x版本,连2x版本都连接不上,再加上Microsoft.AspNet.SignalR.Client和Microsoft.AspNetCore.SignalR.Client差异较大,Microsoft.AspNet.SignalR.Client无法连接AspNetCore版的SignalR服务。

经过各种查询资料,折腾了一天,始终没找到答案。于是换个思路,既然SignalR服务支持WebSocket模式,那么使用WebSocket客户端应该可以连接吧。换个思路继续搜索资料,结果依然非常非常的少,最终仅找到一篇使用微软ClientWebsocket类来连接AspNetCore SignalR Hub服务的文章,链接 https://www.cnblogs.com/turingguo/p/9072865.html 这里非常感谢这位老兄。

然而当我打开微软官方关于ClientWebsocket类的资料页面并看到底部,心里真的是万马奔腾,因为最低只支持到Framework4.5,4.0下不能用。继续寻找Framework4.0下可用的Websocket Client,最终找到了websocket-sharp组件,链接 https://github.com/sta/websocket-sharp ,通过Nuget添加时需注意,要添加名称为WebSocketSharpFork的包。

  • 1、首先是服务端实现,这里使用的是.NET Core 3.1版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class MsgHub : Hub
{
private static IDictionary<string, string> clientDic = new Dictionary<string, string>();

private readonly ILogger<MsgHub> _logger;

public MsgHub(ILogger<MsgHub> logger)
{
_logger = logger;
}

[HubMethodName("SendMessage")]
public async Task SendMessageAsync(string message)
{
_logger.LogDebug($"客户端说:{message}");
clientDic.TryGetValue(Context.ConnectionId, out var userId);

await Clients.All.SendAsync("ReceiveMessage", $"来自用户{userId}的消息:{message}");
}

public override async Task OnConnectedAsync()
{
string userId = Context.GetHttpContext().Request.Query["uid"].FirstOrDefault();

if (string.IsNullOrEmpty(userId))
throw new HubException("连接参数错误");

_logger.LogDebug($"用户[{userId}]连接成功,当前客户ID:[{userId}],当前连接ID:[{Context.ConnectionId}]");

clientDic.Add(Context.ConnectionId, userId);

await base.OnConnectedAsync();
}

public override Task OnDisconnectedAsync(Exception exception)
{
if (exception != null)
_logger.LogError(new EventId(exception.HResult), exception, exception.Message);

clientDic.Remove(Context.ConnectionId);

return base.OnDisconnectedAsync(exception);
}
}
  • 2、然后是在Startup中添加配置
1
2
3
4
5
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(option => option.EnableDetailedErrors = true)
.AddJsonProtocol();
}
  • 3、在Startup.Configure中配置SignalR路由
1
2
3
4
5
6
7
8
9
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});

endpoints.MapHub<MsgHub>("/msgHub");
});
  • 4、客户端代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
internal class Program
{
static void Main(string[] args)
{
try
{
using (var ws = new WebSocket("ws://localhost:5000/msgHub?uid=567"))
{
ws.OnOpen += (sender, e) =>
Console.WriteLine("连接成功。");

ws.OnError += (sender, e) =>
Console.WriteLine("Error: " + e.Message);

ws.OnClose += (sender, e) =>
Console.WriteLine($"连接关闭, Code:{e.Code};Reason:{e.Reason}");

ws.OnMessage += (sender, e) =>
{
string message = GetSignalrMessage(e.RawData);
if (!string.IsNullOrEmpty(message))
{
Console.WriteLine("收到消息: " + message);
}
};

ws.Connect();
ws.Send(AddSeparator(Encoding.UTF8.GetBytes(@"{""protocol"":""json"", ""version"":1}")));

//向服务器发送消息,调用服务器SendMessage方法
SignalrMessage msg = new SignalrMessage
{
type = 1,
target = "SendMessage",
arguments = new List<object> { "test send message" }
};
string strMsg = JsonConvert.SerializeObject(msg);
ws.Send(AddSeparator(Encoding.UTF8.GetBytes(strMsg)));

Console.ReadKey(true);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Console.Read();
}

/// <summary>
/// 添加消息分隔符
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static byte[] AddSeparator(byte[] data)
{
List<byte> t = new List<byte>(data) { 0x1e };//0x1e record separator
return t.ToArray();
}

/// <summary>
/// 移除消息分隔符
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static byte[] RemoveSeparator(byte[] data)
{
List<byte> t = new List<byte>(data);
t.Remove(0x1e);
return t.ToArray();
}

private static string GetSignalrMessage(byte[] data)
{
string strMessage = Encoding.UTF8.GetString(RemoveSeparator(data));
SignalrMessage message = JsonConvert.DeserializeObject<SignalrMessage>(strMessage);
if (message.type.Equals(1) && "ReceiveMessage".Equals(message.target))
{
return message.arguments[0].ToString();
}

return string.Empty;
}
}

internal class SignalrMessage
{
public ushort type { get; set; }
public string target { get; set; }
public List<object> arguments { get; set; }
}

运行结果: