Scott Smith

Blog Tutorials Projects Speaking RSS

SignalR: Awesome Real-Time With .NET - Part 2

Welcome to part two of the series SignalR: Awesome Real-Time with .NET.

Part 1 - SignalR: Awesome Real-Time with .NET

Part 2 - SignalR: Awesome Real-Time with .NET - Part 2

For this post, we will start where we left off from part one. If you don’t want to follow part one and create the solution, project, code, etc, you can get the source on GitHub.

Step 1 - Specifying the route for the Hub

Because Hubs are at a higher level than PersistentConnections, there is no need to manually set the route for the Hub. Some of the magic I talked about before does this for us automatically by creating the route /SignalR. To change the route for the Hub you need to register the route in the Global.asax file and update any client side references to the Hub.

Update Global.asax to change the route to /SignalR2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using SignalR;

namespace SignalRTutorial
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            RouteTable.Routes.MapHubs("~/SignalR2");

            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}

Update your client side code in Index.cshtml to use the new route

<script src="/SignalR2/Hubs"></script>

Step 2 - Specifying the name of the Hub

Another thing that is done automatically is the name of the Hub itself. By default, the name we use client side to reference the Hub is the same as the Type of the Hub. In our code, this is TagHub which we reference with

$.connection.tagHub;

If we want to change it to something else, we only have to decorate our TagHub class with the HubName attribute.

Decorate the TagHub class in TagHub.cs with the HubName attribute using tagHub as our name.

[HubName("ourTagHub")]
public class TagHub : Hub

Because we changed the name of our Hub, we also need to update our reference to this Hub client side.

Update your client side code in Index.cshtml to reference the new Hub name

var tagHub = $.connection.ourTagHub;

Step 3 - Detecting connects and reconnects

It is pretty straightforward to detect connects and reconnects in your Hub. All you have to do is implement IConnected on your Hub. There are two functions that need to be implemented, Connect and Reconnect. Update your TagHub class by implementing IConnected.

using System.Collections.Generic;
using System.Threading.Tasks;
using SignalR.Hubs;

namespace SignalRTutorial.Hubs
{
    [HubName("ourTagHub")]
    public class TagHub : Hub, IConnected
    {
        static List _tags = new List();

        static TagHub()
        {
            _tags.Add("c#");
            _tags.Add(".NET");
            _tags.Add("SignalR");
        }

        public void getTags()
        {
            //Call the initTags method on the calling client
            Caller.initTags(_tags);
        }

        public void setTag(string tag)
        {
            //Add the new tag to the list of tags
            _tags.Add(tag);

            //Call the addTag method on all connected clients
            Clients.addTag(tag);
        }

        public Task Connect()
        {
            return Clients.joined(Context.ConnectionId);
        }

        public Task Reconnect(IEnumerable groups)
        {
            return Clients.rejoined(Context.ConnectionId);
        }
    }
}

As you can see, the Connect and Reconnect methods are making calls to all of our connected clients. Before we implement the client side code to support these calls, I want to point out Context.ConnectionId. Context.ConnectionId is the unique id assigned to the current connection and caller. The Caller property we used before to access the caller is the same thing as Clients[Context.ConnectionId]. The Context.ConnectionId is something that can be stored and used to access connections later.

Here is the client side code needed to support the two new calls being made by our Hub. First add the following markup as the first element inside the <body> tag.

<div style="float: right">
    <ul id="connections"></ul>
</div>

Next add these two methods just below the tagHub.addTag method.

tagHub.joined = function (connectionId) {
    $('#connections').append('<li>Connect: ' + connectionId + '</li>');
}

tagHub.rejoined = function (connectionId) {
    $('#connections').append('<li>Reconnect: ' + connectionId + '</li>');
}

Step 4 - Detecting disconnects

Detecting disconnects is very similar to how we detected connects. This time we need to implement IDisconnect on our Hub and the method Disconnect.

First implement IDisconnect on your TagHub class and then add the following method.

public class TagHub : Hub, IConnected, IDisconnect
{

    public Task Disconnect()
    {
        return Clients.leave(Context.ConnectionId);
    }

}

Next you will need to update the client side code and add the following method.

tagHub.leave = function (connectionId) {
    $('#connections').append('<li>Disconnect: ' + connectionId + '</li>');
}

Step 5 - State between client and server

It is very easy to  maintain state between the client and server. Any state sent by the client can be accessed by the server Hub via the Caller property. The server Hub can also set state that will be accessible by the client by setting any property on Caller. In our application you would do this by setting any property you want client side on the tagHub variable such as tagHub.name = ‘scott’;. You would then be able to access this server side in our Hub with Caller.name. You can also create a new property server side by using the  Caller property doing something like Caller.isCool = ‘true’;.

Step 6 - Broadcasting over a Hub outside of a Hub

There may be cases where you want to broadcast over a Hub from outside of the Hub itself. One example would be an action on a controller you want to be able to broadcast to all connected clients when it is called.

Update the HomeController with the following code.

using SignalR;
using SignalRTutorial.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SignalRTutorial.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

        public void Notify()
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<TagHub>();
            context.Clients.addTag("notify called");
        }
    }
}

The newly created Notify action on the Home controller gets the context to our TagHub. With this context we can now make calls to the clients via the Caller or Clients property.

Conclusion

That is it for now! Stayed tuned for part 3.

The final source code can be found GitHub.

Further reading

SignalR official documentation

ASP.NET MVC, SignalR and Knockout…

signalr tutorial with knockoutjs

Testing SignalR Connections with NUnit