/// <summary> /// Event raise every heartbeat /// </summary> /// <param name="source"></param> /// <param name="e"></param> private static void Heartbeat(Object source, ElapsedEventArgs e) { IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); if (hub == null) { return; } Info("Running heartbeat ..."); using (Module.Administration.DatabaseContext requester = new Module.Administration.DatabaseContext()) { Debug("Ping all clients connected ..."); foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { try { Verbose($"Ping the client {connection}"); hub.Clients.Client(connection.ConnectionId).ping(); } catch (System.Exception exception) { Exception("Unable to ping the client", exception); } } Debug("Disconnect all clients without any updates since a while ..."); foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { if (connection.ConnectionLast < DateTime.Now.AddSeconds(-ConfigurationManager.ClientHubTimeout)) { try { Info($"Disconnecting the client {connection} ..."); hub.Clients.Client(connection.ConnectionId).stop(); connection.Allow = false; requester.SaveChanges(); } catch (System.Exception exception) { Exception("Unable to disconnect the client", exception); } } } } }
/// <summary> /// Launch the heartbeat of the application if it is enabled /// </summary> public static void StartHeartbeat() { // Start Heartbeat to disconnect client if no updated done since a while if (ConfigurationManager.HeartbeatDelay <= 0 || ConfigurationManager.ClientHubTimeout <= 0) { return; } Info($"Launching heartbeat every {ConfigurationManager.HeartbeatDelay} seconds and disconnect clients after {ConfigurationManager.ClientHubTimeout} seconds without response ..."); // Disconnect all connections if no updates done since a while ... IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); using (Module.Administration.DatabaseContext requester = new Module.Administration.DatabaseContext()) foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { if (connection.ConnectionLast < DateTime.Now.AddSeconds(-ConfigurationManager.ClientHubTimeout)) { try { Info($"Disconnecting the client {connection} ..."); if (hub != null) { hub.Clients.Client(connection.ConnectionId).stop(); } connection.Allow = false; requester.SaveChanges(); } catch (System.Exception exception) { Exception("Unable to disconnect the client", exception); } } } // Run the heartbeat Debug("Starting heartbeat ..."); Timer heartbeat = new Timer(ConfigurationManager.HeartbeatDelay * 1000); heartbeat.Elapsed += Heartbeat; heartbeat.Start(); }
/// <summary> /// The client tests if the server if up ... this call generally occurs on connection on an existing account! /// </summary> public void Ping() { try { DateTime now = DateTime.Now; Debug($"Ping from '{Context.ConnectionId}'"); using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { // receive the ping from the client PingRecord existingPing = database._Ping.Find(Context.ConnectionId); if (existingPing != null) { existingPing.Date = now; } else { database._Ping.Add(new PingRecord { ConnectionId = Context.ConnectionId }); } // update the last connection time of the client ConnectionRecord currentConnection = database._Connection.Find(Context.ConnectionId); if (currentConnection != null) { currentConnection.ConnectionLast = now; } database.SaveChanges(); } } catch (System.Exception ex) { Exception($"An exception occurs on pinging of the connection '{Context.ConnectionId}'", ex); Warn("This exception is ignored ... may be the connection was previously deleted or disconnected!"); } }
/// <summary> /// Check if the given user is already connected through hub /// </summary> /// <param name="userId"></param> /// <returns></returns> public static bool IsAlreadyConnected(int userId) { // the user exists, check if it's already connected // In case of reloading page, the connection can't be disconnected as quick as expected ... IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); if (hub == null) { // no hub ... disconnect the user and go to the index page Common.Logger.LoggerManager.Instance.Info(MODULE, $"Cleaning up all connections for the user {userId} ..."); using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { ConnectionRecord currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); while (currentConnection != null) { Common.Logger.LoggerManager.Instance.Debug(MODULE, $"Connection {currentConnection} removed ..."); try { database._Connection.Remove(currentConnection); } catch { } currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); } database.SaveChanges(); } return(false); } // DatabaseCommonContext is frequently open/close here to clean up the cache of the database (else the ping record is never updated) ... bool alreadyConnected = true; DateTime pingDate = DateTime.Now; int count = 0; while (count < ConfigurationManager.ConnectionMaxWaiting && alreadyConnected) { count++; ConnectionRecord currentConnection = null; using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); if (currentConnection != null && count < ConfigurationManager.ConnectionMaxWaiting) { // Ping the client and wait 1 secondes to check if the client responses if (count == 1) { Thread.Sleep(1000); Common.Logger.LoggerManager.Instance.Debug(MODULE, $"Ping to '{currentConnection.ConnectionId}'"); hub.Clients.Client(currentConnection.ConnectionId).ping(); } // Did the client answer to the ping ? PingRecord currentPing = null; using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) currentPing = database._Ping.Find(currentConnection.ConnectionId); // The client has answered to the ping ... So, it already exists if (currentPing != null && DateTime.Compare(currentPing.Date, pingDate) >= 0) { break; } // The client didn't answer ... wait a few more second if (count > 1) { Thread.Sleep(1000); } } else if (currentConnection != null) { // The max seconds has expired ... it means that no client is already connected ... disconnect it alreadyConnected = false; try { hub.Clients.Client(currentConnection.ConnectionId).stop(); Common.Logger.LoggerManager.Instance.Info(MODULE, $"The connection '{currentConnection.ConnectionId}' is disconnected"); } catch (System.Exception ex) { Common.Logger.LoggerManager.Instance.Exception(MODULE, $"(Warning) Unable to disconnect '{currentConnection.ConnectionId}' due to an exception", ex); } using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { currentConnection = database._Connection.Find(currentConnection.ConnectionId); currentConnection.Allow = false; database.SaveChanges(); } } else { alreadyConnected = false; Common.Logger.LoggerManager.Instance.Info(MODULE, $"No connection already existing or the previous connection is disconnected ..."); } } if (alreadyConnected) { Common.Logger.LoggerManager.Instance.Debug(MODULE, $"The user '{userId}' is already connected on another support"); return(true); } return(false); }