客户端调用函数时c#.NET核心信号器异常(并非总是发生)
我有以下服务器客户端调用函数时c#.NET核心信号器异常(并非总是发生),c#,signalr,signalr-hub,asp.net-core-signalr,C#,Signalr,Signalr Hub,Asp.net Core Signalr,我有以下服务器信号器集线器: public class AlertHub : Hub { static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>(); public Task SendMessage(string user, string message) {
信号器集线器:
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;
}
}
这是完美的,也不例外
然后,在我的客户机上,当我尝试发送消息时,我会执行以下操作:(这通常有效,异常是间歇性的)
这通常可以正常工作,但有时它会开始崩溃,并在服务器中显示以下错误:
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);
然后该类注册为Singleton DI:
services.AddSingleton<AlertHubDictionary>();
services.AddSingleton();
最后对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);
}
}
公共类AlertHub:Hub
{
专用只读ILogger\u记录器;
私人AlertHubDictionary\u hubDictionary;
静态ConcurrentDictionary_用户=新ConcurrentDictionary();
公共AlertHub(ILogger记录器、AlertHub字典)
{
_记录器=记录器;
_hubDictionary=hubDictionary;
}
公共任务发送消息(字符串用户,字符串消息)
{
//Clients.Client(“”).sendsync(“广播消息”,用户,消息);
返回Clients.All.sendsync(“BroadcastMessage”,用户,消息);
}
公共任务SendMessageToUser(字符串userUid、字符串用户、字符串消息、字符串totalMessages)
{
//var clientId=\u users.FirstOrDefault(x=>x.Value==userUid.Key;
var clientId=\u hubDictionary.UsersDictionary.FirstOrDefault(x=>x.Value==userUid.Key);
使用(var scope=\u logger.BeginScope(“SCOPED\u值”))
{
_logger.LogInformation($“使用clientId{clientId}向userUid{userUid}-{user}发送消息”);
}
if(clientId==null)
{
//用户未连接
返回Task.FromResult(0);
}
其他的
{
返回Clients.Client(clientId.SendAsync(“BroadcastMessage”、user、message、totalMessages);
}
}
公屋
{
_hubDictionary.ClearUsersDictionary();
//_user.Clear();
使用(var scope=\u logger.BeginScope(“SCOPED\u值”))
{
_logger.LogInformation(“_用户已清除”);
}
}
公共覆盖任务OnConnectedAsync()
{
_hubDictionary.UsersDictionary.TryAdd(Context.ConnectionId,Context.ConnectionId);
//_users.TryAdd(Context.ConnectionId,Context.ConnectionId);
使用(var scope=\u logger.BeginScope(“SCOPED\u值”))
{
_logger.LogInformation($“ConnectionId{Context.ConnectionId}connected.”);
}
返回base.OnConnectedAsync();
}
公共覆盖任务OnDisconnectedAsync(异常)
{
字符串userUid;
_hubDictionary.UsersDictionary.TryRemove(Context.ConnectionId,out userUid);
使用(var scope=\u logger.BeginScope(“SCOPED\u值”))
{
_logger.LogInformation($“UserUid{UserUid}已断开连接。”);
}
返回base.OnDisconnectedAsync(异常);
}
公共void SetUserUid(字符串userUid)
{
_hubDictionary.UsersDictionary[Context.ConnectionId]=userUid;
使用(var scope=\u logger.BeginScope(“SCOPED\u值”))
{
_logger.LogInformation($“连接{Context.ConnectionId}链接到userUid{userUid}”);
}
//ClientNameChanged?.Invoke(Context.ConnectionId,用户名);
}
}
根据您捕获用户ID的方式,您似乎试图将SignalR hub视为一个单独的组件。根据Microsoft文档,hub是一个临时类:
集线器是暂时的:
不要在中心类的属性中存储状态。每个集线器方法
调用在新的中心实例上执行
因此,如果您想保留已注册用户ID的共享实例,您可能应该将其作为一个单独的类进行管理,该类注册为单例并注入到hub类中,以便可以独立于hub维护状态。将日志记录放入文件中。从添加/清空\用户的方法进行日志记录。你可能会时不时地失去连接=这就是为什么它会被清除。太棒了。是的,这完全有道理。任何我可以查看如何实现它的链接?@VAAA-您只需将字典移动到它自己的类中,在启动时注册它就可以了service.AddSingleton()
,然后使用构造函数注入将其注入到hub的构造函数中,如中所述,这里有一个来自Microsoft的非常类似的独立类示例,尽管标记为internal,但您可以使用public:非常感谢,我将尝试一下刚刚更新了我所做的更改,我将尝试一下。
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();
}
}
services.AddSingleton<AlertHubDictionary>();
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);
}
}