reSPAWNed
reSPAWNed

Reputation: 1099

SignalR - Loop while clients are connected

I have been playing a lot with SignalR lately and find it really awesome for server/client communication.

Then I thought of a scenario that was quite relevant for a project I am currently working on.

The basic idea is to indicate on a webpage if a server is currently online by pinging the machine.

The process is started by the first user to visit the page and should stop, when all clients have disconnected.

My server code so far is this:

public class PingHub : Hub
{
    private static readonly ConcurrentDictionary<string, DateTime> _users = new ConcurrentDictionary<string, DateTime>();
    private static bool _isPolling = false;

    public override Task OnConnected()
    {
        _users.TryAdd(Context.ConnectionId, DateTime.Now);

        return Clients.All.UserCountChanged(_users.Count);
    }

    public override Task OnReconnected()
    {
        _users.AddOrUpdate(Context.ConnectionId, DateTime.Now, (key, value) => value);

        return Clients.All.UserCountChanged(_users.Count);
    }

    public override Task OnDisconnected()
    {
        DateTime value;
        _users.TryRemove(Context.ConnectionId, out value);

        return Clients.All.UserCountChanged(_users.Count);
    }

    public void GetStatus()
    {
        if (_isPolling)
            return;

        _isPolling = true;

        var ping = new Ping();
        do
        {
            var reply = ping.Send("X.X.X.X");

            if (reply == null)
            {
                Clients.Caller.SetError(new { error = "No reply!" });
                break;
            }

            Clients.All.SetStatus(new { Status = reply.Status.ToString(), Time = reply.RoundtripTime });

            Thread.Sleep(500);
        } while (_users.Count > 0);

        _isPolling = false;
    }
}

The problem is that the user that initiates the process keeps being connected, even if the browser is closed (I tested this by logging each ping request).

So can anyone help me to make this work as intended?

Upvotes: 0

Views: 2858

Answers (1)

Joel Lucsy
Joel Lucsy

Reputation: 8706

I recently did something similar. In my Global.asax.cs I have:

private BackgroundProcess bp;
protected void Application_Start( object sender, EventArgs e )
{
    ...
    bp = new BackgroundProcess();
}

Then I have a BackgroundProcess.cs:

public class BackgroundProcess : IRegisteredObject
{
    private Timer TaskTimer;
    private IHubContext ClientHub;

    public BackgroundProcess()
    {
        HostingEnvironment.RegisterObject( this );

        ClientHub = GlobalHost.ConnectionManager.GetHubContext<webClientHub>();

        TaskTimer = new Timer( OnTimerElapsed, null, TimeSpan.FromSeconds( 0 ), TimeSpan.FromSeconds( 10 ) );
    }

    private void OnTimerElapsed( object sender )
    {
        if (...) //HasAnyClientsConnected, not sure what this would be, I had other criteria
        {
            ClientHub.Clients.All.ping(); //Do your own ping
        }
    }

    public void Stop( bool immediate )
    {
        TaskTimer.Dispose();
        HostingEnvironment.UnregisterObject( this );
    }

The key here is IRegisteredObject. You can look it up, but the web application will notifiy it when it shuts down for any reason.

Upvotes: 8

Related Questions