簡體   English   中英

客戶端調用函數時的 c# .NET Core SignalR 異常(並非總是發生)

[英]c# .NET Core SignalR exception when client calls a function (not always happens)

我有以下服務器SignalR集線器:

 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);
        }
    }

根據您捕獲用戶 ID 的方式,您似乎試圖將 SignalR 集線器視為單例。 根據 Microsoft 的文檔,集線器是一個瞬態類:

https://docs.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-3.1#create-and-use-hubs

集線器是暫時的:

不要將狀態存儲在集線器類的屬性中。 每個集線器方法調用都在新的集線器實例上執行。

因此,如果您想保留已注冊用戶 ID 的共享實例,您可能應該將其作為一個單獨的類進行管理,該類注冊為單例並注入到您的集線器類中,以便可以獨立於集線器來維護狀態。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM