Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

A Note on SignalR

4.85/5 (17 votes)
27 Oct 2016CPOL5 min read 29.4K   296  
This is a quick note on SignalR.

Introduction

This is a quick note on SignalR.

Background

SignalR is the Microsoft implementation of the WebSocket, which provides full-duplex communication channels over a single TCP connection. But this note only talks about sending signals to the browser from the server. Besides giving a running example, this note has a focus to create a customized client proxy to address the following limitations on the Microsoft automatically generated proxy:

  • If the connection is interrupted, the automatically generated proxy does not make all the effort to re-establish the connection.
  • The automatically generated proxy does not support adding client listeners after the connection is established.

Image 1

The attached is a typical ASP.NET MVC application created in Visual Studio 2015 (update 3). Although it only addressed the topic to broadcast a signal to all the connected browsers, it should be an easy starting point if you want to explore more complicated subjects about WebSocket in the Microsoft environment.

Server Side Environment

To use SignalR in an ASP.NET MVC project, you need to add the SignalR dependency in your NuGet packages.

Image 2

You need to create a "Hub" class, so the web browsers can connect to the server through the WebSocket.

C#
using Microsoft.AspNet.SignalR;
    
namespace siganlR_example
{
    public class SignalRHub: Hub { }
}
  • You can name the "Hub" class anything that you want. In this example, I named it "SignalRHub", but you do not have to name it the same way. This class needs to be a sub-class of the "Microsoft.AspNet.SignalR.Hub" class that comes with the NuGet package;
  • For the purpose of this note, I will only broadcast signals to all the connected browsers, so I left this class empty. If you want to do more with the WebSocket, you can read the SignalR document and add whatever to this class at your will.

In order for the MVC application to be aware of the "SignalRHub" class, you need to call the extension method "MapSignalR()" on the "IAppBuilder" interface at the startup of the application.

C#
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // Map the SignalR
        app.MapSignalR();
    
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}

The "MapSignalR()" function searches all the sub-classes of the "Microsoft.AspNet.SignalR.Hub" classes at the application startup time, so the web application knows which "Hub" classes to use when the browser tries to connect to them.

Client Side Environment

To use SignalR, you will need the client side SignalR JavaScript library.

JavaScript
<script src="@Url.Content("~/Scripts/jquery.min-3.1.0.js")"></script>
JavaScript
<script src="@Url.Content("~/Scripts/jquery.signalR-2.2.1.min.js")"></script>
  • SignalR depends on jQuery, so we will need the jQuery library;
  • You can get the SignalR JavaScript library either through the Nuget or from the Microsoft CDN. My personal preference is to take JavaScript libraries from Nuget, so I just downloaded the "jquery.signalR-2.2.1.min.js" file from the CDN.

Microsoft provided an automatically generated client proxy, but it has the following drawbacks:

  • It will not make all the effort to re-connect if the connection is lost.
  • If a listener is added after the connection is established, it will not be able to receive signals.

To address these two drawbacks, I created my own client proxy in the "SignalRClient.js" file.

JavaScript
var SignalRClient = function (url, hubName) {
    var self = this;
    var RECONNECT_IF_DISCONNECTED = true;
    
    var connectionUrl = url;
    var connection = $.hubConnection();
    var proxy = connection.createHubProxy(hubName);
    
    // Register a dummy handler
    proxy.on('*', function () { });
    
    var connectToServer = function () {
    
        connection.url = connectionUrl;
        connection.start()
        .done(function () {
            console.log('SignalR Connected ID=' + connection.id);
        })
        .fail(function () {
            console.log('SignalR Connection failed');
        });
    };
    
    connection.disconnected(function () {
        console.log('SignalR Disconnected');
    
        // If 'RECONNECT_IF_DISCONNECTED' set the timer
        // to connect to the server until re-connected
        if (RECONNECT_IF_DISCONNECTED) {
            setTimeout(connectToServer, 5 * 1000);
        }
    });
    
    self.on = function (caller, handler) {
        proxy.on(caller, handler);
        return self;
    };
    
    self.connect = function () {
        RECONNECT_IF_DISCONNECTED = true;
        connectToServer();
    };
    
    self.disconnect = function () {
        RECONNECT_IF_DISCONNECTED = false;
        connection.stop();
    };
};
  • The "SignalRClient" is a typical JavaScript constructor function.
  • At the beginning, it registers a dummy listener, which allows adding listeners after the connection is established. You can check out this link for the details.
  • In the "disconnected" event, I added the code to make effort to re-connect to the server until the connection is re-established. You can check out this link for the details. You should be aware that the "disconnected" event fires if the connection is disconnected and if the connection fails at the initial attempt by calling the "connect" method.

The Example

It is very simple to broadcast a message to the connected browsers from the server.

C#
protected void Application_Start() {
    
    Timer timer = new Timer(1000);
    timer.AutoReset = true;
    
    timer.Elapsed += (sender, e) =>
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("Server Time: ")
            .Append("<span style=\"color: red\">")
            .Append(System.DateTime.Now.ToLongTimeString())
            .Append("</span>");
    
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
        context.Clients.All.JustSendAMessage(sb.ToString());
    };
    
    timer.Start();
}
  • In this example, I set up a timer in the "Application_Start" event to send the server time to the browsers once every second;
  • It is important to know that the "JustSendAMessage()" is a call on a dynamic type. Even it is just a .NET syntax sugar, the "JustSendAMessage" text is important. We will need this text to receive the messages at the browser side when we register a client side listener.

To receive the messages from the server, I created the "Index.cshtml".

HTML
<html>
<body>
    <div id="spanMsg"></div>
</body>
</html>

and added the links to the client side JavaScript files to the HTML document.

HTML
<script src="@Url.Content
("~/Scripts/jquery.min-3.1.0.js")"></script>
<script src="@Url.Content
("~/Scripts/jquery.signalR-2.2.1.min.js")"></script>
<script src="@Url.Content
("~/Scripts/SignalRClient.js")"></script>
JavaScript
$(document).ready(function () {
    var sClient = new SignalRClient
    ('@Url.Content("~/signalR")', 'signalRHub');
    
        sClient.on('JustSendAMessage', function (msg) {
            $('#spanMsg').html(msg);
        });
        sClient.connect();
});
  • To create an instance of the "SignalRClient", you need to provide the "url" and the name of the "Hub" class:
    • The "url" is always the root directory of the web site + "/signalR";
    • The "Hub" name corresponds to the name of the C# "SignalRHub" class in the server. You may have noticed that you will need to change the case of the "S" to "s". I do not know why Microsoft has to make the case of the "Hub" name different from the C# class, but it is the way it is.
  • The "on" method registers a listener to the message sent by the "JustSendAMessagedynamic type. Upon a new message, the "spanMsg" is changed to display the updated server time.

Specify a Transport

The communication between a SignalR client and the server can use different transports.

  • webSockets
  • foreverFrame
  • serverSentEvents
  • longPolling

SignalR will negotiate with the server to find the most suitable transport, but sometimes it is desirable that we can choose the transport to use.

connection.start({ transport: ['serverSentEvents', 'foreverFrame', 'webSockets'] })

The above "start()" method excludes the "longPolling" option from the choice when the client tries to connect to the server.

Run the Application

If you have the appropriate version of the Visual Studio, you can load the project and run it.

Image 3

  • If everything runs OK, you should see the message sent from the server through the WebSocket.
  • If you have some time, you may try to re-start your server to see if the SignalR connection is re-established after it is interrupted.
  • If you are interested, you can also change the code to register the listener after the SignalR connection is established. You should see that the listener receives the server messages nicely.

Points of Interest

  • This is a quick note on SignalR.
  • This note comes with a very simple example to use SignalR, which is so simple that it only broadcasts message to all the connected browsers.
  • I hope you like my postings and I hope this note can help you one way or the other.

History

  • 10/25/2016: First revision

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)