6

I configure the server as following on startup.cs

GlobalHost.HubPipeline.RequireAuthentication();

// Make long polling connections wait a maximum of 110 seconds for a
// response. When that time expires, trigger a timeout command and
// make the client reconnect.
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(40);
// Wait a maximum of 30 seconds after a transport connection is lost
// before raising the Disconnected event to terminate the SignalR connection.
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
// For transports other than long polling, send a keepalive packet every
// 10 seconds. 
// This value must be no more than 1/3 of the DisconnectTimeout value.
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);

GlobalHost.HubPipeline.AddModule(new SOHubPipelineModule());
var hubConfiguration = new HubConfiguration { EnableDetailedErrors = true  };

var heartBeat = GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>();
var monitor = new PresenceMonitor(heartBeat);
monitor.StartMonitoring();

app.MapSignalR(hubConfiguration);

where PresenceMonitor is the class responsible of check unlive data . as I keep them in database using the following code

public class PresenceMonitor
{
    private readonly ITransportHeartbeat _heartbeat;
    private Timer _timer;
    // How often we plan to check if the connections in our store are valid
    private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromSeconds(40);

    // How many periods need pass without an update to consider a connection invalid
    private const int periodsBeforeConsideringZombie = 1;

    // The number of seconds that have to pass to consider a connection invalid.
    private readonly int _zombieThreshold;

    public PresenceMonitor(ITransportHeartbeat heartbeat)
    {
        _heartbeat = heartbeat;
        _zombieThreshold = (int)_presenceCheckInterval.TotalSeconds * periodsBeforeConsideringZombie;
    }

    public async void StartMonitoring()
    {
        if (_timer == null)
        {
            _timer = new Timer(_ =>
            {
                try
                {
                    Check();
                }
                catch (Exception ex)
                {
                    // Don't throw on background threads, it'll kill the entire process
                    Trace.TraceError(ex.Message);
                }
            },
            null,
            TimeSpan.Zero,
            _presenceCheckInterval);
        }
    }

    private async void Check()
    {
        // Get all connections on this node and update the activity
        foreach (var trackedConnection in _heartbeat.GetConnections())
        {
            if (!trackedConnection.IsAlive)
            {
                await trackedConnection.Disconnect();
                continue;
            }
            var log = AppLogFactory.Create<WebApiApplication>();
            log.Info($"{trackedConnection.ConnectionId} still live ");

            var connection = await (new Hubsrepository()).FindAsync(c => c.ConnectionId == trackedConnection.ConnectionId);
            // Update the client's last activity
            if (connection != null)
            {
                connection.LastActivity = DateTimeOffset.UtcNow;
                await (new Hubsrepository()).UpdateAsync(connection, connection.Id).ConfigureAwait(false);
            }                
        }

        // Now check all db connections to see if there's any zombies

        // Remove all connections that haven't been updated based on our threshold
        var hubRepository = new Hubsrepository();
        var zombies =await hubRepository.FindAllAsync(c => 
            SqlFunctions.DateDiff("ss", c.LastActivity, DateTimeOffset.UtcNow) >= _zombieThreshold);

        // We're doing ToList() since there's no MARS support on azure
        foreach (var connection in zombies.ToList())
        {
            await hubRepository.DeleteAsync(connection);
        }             
    }
}

and my hub connect disconnect , reconnect looks like

public override async Task OnConnected()
{
    var log = AppLogFactory.Create<WebApiApplication>();
    if (Context.QueryString["transport"] == "webSockets")
    {
        log.Info($"Connection is Socket");
    }

    if (Context.Headers.Any(kv => kv.Key == "CMSId"))
    {
        // Check For security
        var hederchecker = CryptLib.Decrypt(Context.Headers["CMSId"]);
        if (string.IsNullOrEmpty(hederchecker))
        {
            log.Info($"CMSId cannot be decrypted {Context.Headers["CMSId"]}");
            return;
        }
        log.Info($" {hederchecker} CMSId online at {DateTime.UtcNow} ");

        var user = await (new UserRepository()).FindAsync(u => u.CMSUserId == hederchecker);
        if (user != null)
            await (new Hubsrepository()).AddAsync(new HubConnection()
            {
                UserId = user.Id,
                ConnectionId = Context.ConnectionId,
                UserAgent = Context.Request.Headers["User-Agent"],
                LastActivity = DateTimeOffset.UtcNow
            }).ConfigureAwait(false);

        //_connections.Add(hederchecker, Context.ConnectionId);
    }
    return;
}

public override async Task OnDisconnected(bool stopCalled)
{
    try
    {
        //if (!stopCalled)
        {
            var hubRepo = (new Hubsrepository());

            var connection = await hubRepo.FindAsync(c => c.ConnectionId == Context.ConnectionId);
            if (connection != null)
            {
                var user = await (new UserRepository()).FindAsync(u => u.Id == connection.UserId);
                await hubRepo.DeleteAsync(connection);
                if (user != null)
                {
                    //var log = AppLogFactory.Create<WebApiApplication>();
                    //log.Info($"CMSId cannot be decrypted {cmsId}");

                    using (UserStatusRepository repo = new UserStatusRepository())
                    {
                        //TODO :: To be changed immediatley in next release , Date of change 22/02/2017 

                        var result = await (new CallLogRepository()).CallEvent(user.CMSUserId);
                        if (result.IsSuccess)
                        {
                            var log = AppLogFactory.Create<WebApiApplication>();
                            var isStudent = await repo.CheckIfStudent(user.CMSUserId);

                            log.Info($" {user.CMSUserId} CMSId Disconnected here Before Set offline  at {DateTime.UtcNow} ");

                            var output = await repo.OfflineUser(user.CMSUserId);

                            log.Info($" {user.CMSUserId} CMSId Disconnected here after Set offline  at {DateTime.UtcNow} ");

                            if (output)
                            {
                                log.Info($" {user.CMSUserId} CMSId Disconnected at {DateTime.UtcNow} ");

                                Clients.All.UserStatusChanged(user.CMSUserId, false, isStudent);
                            }
                        }
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        var log = AppLogFactory.Create<WebApiApplication>();
        log.Error($"CMSId cannot Faild to be offline {Context.ConnectionId} with error {e.Message}{Environment.NewLine}{e.StackTrace}");
    }
}

public override async Task OnReconnected()
{
    string name = Context.User.Identity.Name;

    var log = AppLogFactory.Create<WebApiApplication>();
    log.Info($" {name} CMSId Reconnected at {DateTime.UtcNow} ");

    var connection = await (new Hubsrepository()).FindAsync(c => c.ConnectionId == Context.ConnectionId);
    if (connection == null)
    {
        var user = await (new UserRepository()).FindAsync(u => u.CMSUserId == name);
        if (user != null)
            await (new Hubsrepository()).AddAsync(new HubConnection()
            {
                UserId = user.Id,
                ConnectionId = Context.ConnectionId,
                UserAgent = Context.Request.Headers["User-Agent"],
                LastActivity = DateTimeOffset.UtcNow
            }).ConfigureAwait(false);
    }
    else
    {
        connection.LastActivity = DateTimeOffset.UtcNow;
        await (new Hubsrepository()).UpdateAsync(connection, connection.Id).ConfigureAwait(false);
    }
}

all test cases passes well except when internet cut on client side the connection keep live for more than 10 minutes, is this related to authentication , or any configuration wrong at my side any help am really don't know what's wrong . client use websocket transport

Alex
  • 2,931
  • 6
  • 15
  • 35
AMH
  • 5,779
  • 27
  • 75
  • 132
  • Are you certain that it is connecting using websockets? I ask because you have the `KeepAlive` setting commented out for some reason and I'm not sure what effect that would have. How come it's commented out? – Luke Feb 28 '17 at 17:00
  • aha u mean add this and check again ?! – AMH Feb 28 '17 at 17:03
  • It's definitely worth doing, because that's a setting that probably should be set – Luke Feb 28 '17 at 17:07
  • ok iwll try tomorrow and update you – AMH Feb 28 '17 at 17:10
  • You shouldn't have to monitor the connection with your own logic, that's what SignalR does for you. You can check / subscribe to SignalR to tell you when a user is not connected anymore. If you're going to use your own connection logic then why piddle with SignalR? – Michael Puckett II Apr 09 '17 at 04:30
  • @AMH were you able to resolve this? – ajbeaven Jan 15 '19 at 03:27

0 Answers0