SignalR: Wire-up the ConnectionId with a user instance

While working on a RPG game prototype based on ASP.NET I started experimenting with SignalR. This library was a perfect fit as it allows pushing data from server to clients in a very easy way (read: with just a few lines of code) and of course server method invocations from clients through JavaScript.

Unfortunately SignalR is not providing a direct and intuitive way of gluing together application specific (server side) objects with the SignalR internal connections. This means that at any time you know how many clients are connected but you have absolutely no idea who they are. A client is basically a connection. A connection has a unique connectionId so you can distinguish clients but that's about it. Even worse, each time a client reconnects it gets a new connection id.

In real world applications you have a bunch high level objects like: users, accounts, players etc. that are required to be in sync with client connections. So the problem is how to wire up a particular client with a well known object instance, say a Player object instance.

SignalR has few extension points that allow us to change the default behavior. One of those is a IConnectionIdGenerator. This interface is in charge of generating connection Id's and we will replace the built in behavior with a new one. The bellow implementation relies upon the fact that all clients must authenticate and during the authentication their playerId is stored in the tickets UserData.

public class ConnectionFactory : IConnectionIdGenerator
    {
        public string GenerateConnectionId( IRequest request )
        {
            if ( request.Cookies[FormsAuthentication.FormsCookieName] != null )
            {
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt( request.Cookies[FormsAuthentication.FormsCookieName].Value );
                
                int playerId;
                if ( int.TryParse( ticket.UserData, out playerId ) )
                {                    
                    return ticket.UserData;
                }
            }

            throw new InvalidOperationException( "No cookie" );
        }
    }

To tell the SignalR framework to use our connection generator instead the build-in we simply point to our class at a convenient place like Application_Start:

void Application_Start( object sender, EventArgs e )
{
   GlobalHost.DependencyResolver.Register( typeof( IConnectionIdGenerator ), () => new ConnectionFactory() );
}

Now all clients will get the connection id based on their player Id, even better if a client disconnects and reconnects again it will get the very same connection id!

Comments

  1. Does this approach work when player\user open more than 1 browser ?

    ReplyDelete
  2. Yes, the connections would get the same connectionId provided the user has logged in with same account (and your backend system has stored the same playerId in both cookies).

    If you want to allow that or not is to be handled by your implementation.

    ReplyDelete
  3. Thanks for the helpful post.

    I tried to implement the IConnectionIdGenerator interface,
    but could only find IConnectionIdPrefixGenerator under Microsoft.AspNet.SignalR.
    Where can I find the right interface?

    ReplyDelete
    Replies
    1. SignalR is not a finished product and therefore some breaking changes are expected. Seems you run into one of those.

      The handling of connection Id's has been changed to face a problem where multiple clients can get the same id.

      Try using the IConnectionIdPrefixGenerator, it allows you to define a custom prefix prepended to a unique id. You should use it the same way as the old IConnectionIdGenerator but adjust the dependent server/client side logic to parse just the prefix instead the whole string!

      Delete
  4. Oh, very Weel!

    dude ... you could send me a simple hub ... where I have a few users connected, and I can select them to send an individual message? You would be asking too much? It would be of great help, I am Brazilian and I am very interested in learning how to use SignalR ...

    Already very grateful!

    I am not a spammer lol !

    ReplyDelete

Post a Comment

Popular posts from this blog

SignalR: send messages to single client

Manipulating connection strings