【发布时间】:2020-11-17 18:02:06
【问题描述】:
我有以下服务器SignalR Hub:
public class AlertHub : Hub
{
static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("BroadcastMessage", user, message);
}
public Task SendMessageToUser(string userUid, string user, string message, string totalMessages)
{
var clientId = _users.FirstOrDefault(x => x.Value == userUid).Key;
return Clients.Client(clientId).SendAsync("BroadcastMessage", user, message, totalMessages);
}
public static void ClearState()
{
_users.Clear();
}
public override Task OnConnectedAsync()
{
_users.TryAdd(Context.ConnectionId, Context.ConnectionId);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
string userUid;
_users.TryRemove(Context.ConnectionId, out userUid);
return base.OnDisconnectedAsync(exception);
}
public void SetUserUid(string userUid)
{
_users[Context.ConnectionId] = userUid;
}
}
在我的客户端上,就在await _signalRConnection.StartAsync(); 之前,我将userUid 设置为我的_users 字典:
//Send user name for this client, so we won't need to send it with every message
await _signalRConnection.InvokeAsync("SetUserUid", this.State.UserUID);
而且效果很好,没有例外。
然后在我的客户端上,当我尝试发送消息时,我会执行以下操作:(这通常有效,异常是间歇性的)
await _signalRConnection.InvokeAsync("SendMessageToUser", model.UserUid.ToString().ToLower(), this.State.UserName, $"{model.UserName} has sent you a message.", countMessagesString);
这正常工作,但有时它开始崩溃,在服务器中显示以下错误:
Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher.? [?] - MESSAGE: Failed to invoke hub method 'SendMessageToUser'.
System.ArgumentNullException: Value cannot be null. (Parameter 'connectionId')
那是因为当尝试获取 clientId 时,它会在此处返回 null:
var clientId = _users.FirstOrDefault(x => x.Value == userUid).Key; <-- HERE
return Clients.Client(clientId).SendAsync("BroadcastMessage", user, message, totalMessages);
所以我不知道为什么 _users 字典有时没有密钥。这很奇怪。
关于如何挖掘或解决它的任何线索?
** 更新 **
这就是我所做的:
public class AlertHubDictionary
{
static ConcurrentDictionary<string, string> _users;
public AlertHubDictionary()
{
_users = new ConcurrentDictionary<string, string>();
}
public ConcurrentDictionary<string, string> UsersDictionary { get { return _users; } }
public void ClearUsersDictionary()
{
_users.Clear();
}
}
然后这个类注册为Singleton DI:
services.AddSingleton<AlertHubDictionary>();
最后对 Hub 类进行替换:
public class AlertHub : Hub
{
private readonly ILogger<AlertHub> _logger;
private AlertHubDictionary _hubDictionary;
static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
public AlertHub(ILogger<AlertHub> logger, AlertHubDictionary hubDictionary)
{
_logger = logger;
_hubDictionary = hubDictionary;
}
public Task SendMessage(string user, string message)
{
//Clients.Client("").SendAsync("BroadcastMessage", user, message);
return Clients.All.SendAsync("BroadcastMessage", user, message);
}
public Task SendMessageToUser(string userUid, string user, string message, string totalMessages)
{
//var clientId = _users.FirstOrDefault(x => x.Value == userUid).Key;
var clientId = _hubDictionary.UsersDictionary.FirstOrDefault(x => x.Value == userUid).Key;
using (var scope = _logger.BeginScope("SCOPED_VALUE"))
{
_logger.LogInformation($"Send message to userUid {userUid} - {user} with clientId {clientId}.");
}
if (clientId == null)
{
// the user is not connected
return Task.FromResult(0);
}
else
{
return Clients.Client(clientId).SendAsync("BroadcastMessage", user, message, totalMessages);
}
}
public void ClearState()
{
_hubDictionary.ClearUsersDictionary();
//_users.Clear();
using (var scope = _logger.BeginScope("SCOPED_VALUE"))
{
_logger.LogInformation("_users cleared");
}
}
public override Task OnConnectedAsync()
{
_hubDictionary.UsersDictionary.TryAdd(Context.ConnectionId, Context.ConnectionId);
//_users.TryAdd(Context.ConnectionId, Context.ConnectionId);
using (var scope = _logger.BeginScope("SCOPED_VALUE"))
{
_logger.LogInformation($"ConnectionId {Context.ConnectionId} connected.");
}
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
string userUid;
_hubDictionary.UsersDictionary.TryRemove(Context.ConnectionId, out userUid);
using (var scope = _logger.BeginScope("SCOPED_VALUE"))
{
_logger.LogInformation($"UserUid {userUid} disconnected.");
}
return base.OnDisconnectedAsync(exception);
}
public void SetUserUid(string userUid)
{
_hubDictionary.UsersDictionary[Context.ConnectionId] = userUid;
using (var scope = _logger.BeginScope("SCOPED_VALUE"))
{
_logger.LogInformation($"Connection {Context.ConnectionId} linked to userUid {userUid}");
}
//ClientNameChanged?.Invoke(Context.ConnectionId, userName);
}
}
【问题讨论】:
-
将日志记录到文件中。从添加/清空
_users的方法记录。您可能会不时失去连接 = 这就是它被清除的原因。
标签: c# signalr signalr-hub asp.net-core-signalr