public virtual void CleanConnection(string connectionId) { Log.SignalR(LogTag.TextChatHub_CleaningConnection, new { connectionId }); // Remove Message Queue for this connection. Queues are created only when needed, so they might not always exists if (MessageQueues.ContainsKey(connectionId)) { MessageQueues[connectionId].Reset(); MessageQueues.Remove(connectionId); } // Remove all connections to rooms RoomsConnections.RemoveConnectionFromAllRooms(connectionId); // Remove User's Connection, and leave chat if this was the last connection if (UsersConnections.ContainsValue(connectionId)) { UserId userId = UsersConnections.GetFromValue(connectionId); UsersConnections.Remove(connectionId); if (!UsersConnections.ContainsKey(userId)) { ChatCtrl.LeaveChat(userId); } } else { Log.Info(LogTag.TextChatHub_AlreadyCleaned); } // Also remove LastSeen Info for this connection if (LastSeenConnections.ContainsKey(connectionId)) { LastSeenConnections.Remove(connectionId); } }
//========== Connection Methods ====================================================================================== public int Ping(string connectionId, OrderId orderIdToAck) { // Update Last Seen record if (LastSeenConnections.ContainsKey(connectionId)) { LastSeenConnections[connectionId] = DateTime.Now; } else { LastSeenConnections.Add(connectionId, DateTime.Now); } MessageQueues[connectionId].Ack(orderIdToAck); // Ack messages return(MessageQueues[connectionId].HighestQueuedOrderId); // Returned id of last queued message for the client to check if it is in sync }
//========== Background Methods ====================================================================================== private void HeartbeatCheck() { try { // Try catch needed because errors are otherwised silenced, somehow :-( // Remove dead connections (Ping not received timely) foreach (var kvp in LastSeenConnections.ToList()) { if (kvp.Value < DateTime.Now.AddSeconds(-CleanZombieAfter)) { var connectionId = kvp.Key; Log.SignalR(LogTag.CleaningZombieConnection, new { connectionId }); CleanConnection(connectionId); } } // Remove inactive members from "crowded" rooms var chatState = ChatCtrl.GetState(); foreach (var roomKvp in chatState.Rooms) { if (roomKvp.Key.IsMonoLang() && roomKvp.Value.Count > BootAboveUserCount) { var mostInactiveUserKvp = roomKvp.Value.Aggregate((l, r) => l.Value.LastActive < r.Value.LastActive ? l : r); if (mostInactiveUserKvp.Value.LastActive < DateTime.Now.AddMinutes(-BootOutOfRoomAfter)) { Log.SignalR(LogTag.KickSlackerOutOfRoom, new { roomId = roomKvp.Key, userId = mostInactiveUserKvp.Key }); // Get the list of connections that have to leave the room var userConnectionIds = UsersConnections.GetFromKey(mostInactiveUserKvp.Key) .Where(connId => RoomsConnections.HasConnection(roomKvp.Key, connId)) .Select(connId => connId.ToString()).ToList(); Clients.Clients(userConnectionIds).LeaveRoom(roomKvp.Key); } } } // Check for inactive members foreach (var chatUser in chatState.AllUsers.Values) { if (chatUser.LastActivity < DateTime.Now.AddMinutes(-UserIdleAfter)) { ChatCtrl.SetIdleStatus(chatUser.Id); Log.SignalR(LogTag.SetChatUserIdle, new { userId = chatUser.Id }); } } Log.SignalR(LogTag.HubHeartbeatCheckCompleted); } catch (Exception e) { Log.Error(LogTag.HeartbeatCheckError, e); } }