SignalR: Building real time web applications

Note: This sample is targeting SignalR 1.0.1 

SignalR offers a simple and clean API to write real time web applications where the server needs to continuously push data to clients. Common applications are chat, news feed, notifications, multiplayer games.

In this sample, I demonstrate powerful features like:

  • A server implementation hosted in IISExpress
  • Client implementations running on IISExpress, a Console Application, a Windows Store App, and a Silverlight App
  • Doing request/response operations sync and async
  • Server pushing broadcast messages to ALL clients
  • Server pushing group messages to specific devices like a web browser, a desktop, or a tablet.

Server

Server derives the Hub class, and it handles incoming requests in two ways:

  1. Method "y Request(x)" transparently sends a response to client
  2. Method “Task<y> RequestAsync(x)” allows server to do async long processing and transparently sends a response to client
  3. Method "Task RequestWithCallbackAsync(x)" uses Clients.Others.<DynamicMethod> to send a response to all other clients connected at the moment
public class SampleHub : Hub
{
  public FromServerToClientData Request(FromClientToServerData request)
  {
    FromServerToClientData response = new FromServerToClientData();
    response.Text = "Responding to: " + request.Text;
    return response;
  }

  public async Task<FromServerToClientData> RequestAsync(FromClientToServerData request)
  {
    FromServerToClientData response = new FromServerToClientData();
    response.Text = "Responding to: " + request.Text;
    await Task.Delay(TimeSpan.FromSeconds(5));
    return response;
  }

  public async Task RequestWithCallbackAsync(FromClientToServerData request)
  {
    FromServerToClientData response = new FromServerToClientData();
    response.Text = "Responding to: " + request.Text;
    await Task.Delay(TimeSpan.FromSeconds(5));
    Clients.Others.OthersCallback(response);
  }

  public void JoinGroup(string groupName)
  {
    Groups.Add(Context.ConnectionId, groupName);
  }
}

Server is also pushing data to clients in two ways using the BackgroundThread class:

  1. hubContext.Clients.All.<DynamicMethod>
  2. hubContext.Clients.Group(groupName).<DynamicMethod>
public class BackgroundThread
{
  public static void Start()
  {
    ThreadPool.QueueUserWorkItem(_ =>
    {
    IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<SampleHub>();

    while (true)
    {
      ...
      hubContext.Clients.All.Broadcast(data);
      hubContext.Clients.Group("WebApp").BroadcastToGroup(
        string.Format("WebApp {0}", DateTime.Now));
      hubContext.Clients.Group("ConsoleApp").BroadcastToGroup(
        string.Format("ConsoleApp {0}", DateTime.Now));
      hubContext.Clients.Group("SilverlightApp").BroadcastToGroup(
        string.Format("SilverlightApp {0}", DateTime.Now));
      hubContext.Clients.Group("WindowsStoreApp").BroadcastToGroup(
        string.Format("WindowsStoreApp {0}", DateTime.Now));
    }        
    }
  });
}

As you can see, dynamic methods define the events that need to be handled on the client side. Think of them as if they were interface methods to execute remote calls.

 

Client

To write a client, you must define how to handle Server <DynamicMethods>: "client.Broadcast", "client.Group", and "client.ResponseAsync".

Then, client is started, and after the connection succeeds, the client can make requests to server by invoking "server.requestAsync" and "server.joinGroup".

You will see similar patterns in the ConsoleApp and WindowsStoreApp clients.

$(function () {
  $.connection.sampleHub.client.broadcast = function (value) {
    $('#BroadcastText').html('<pre>Broadcast: ' + value.Now + ' ' +
      value.Integer + ' ' + value.Text + '</pre>');
  }

  $.connection.sampleHub.client.broadcastToGroup = function (value) {
    $('#BroadcastToGroupText').html('<pre>BroadcastToGroup: ' + value + '</pre>');
  }

  $.connection.sampleHub.client.othersCallback = function (value) {
    $('#OthersCallbackText').html('<pre>OthersCallback: ' + value.Text + '</pre>');
  }

  $.connection.hub.logging = true;
  $.connection.hub.start().done(function () {
    var request = {};
    request.Text = 'This is a request from a WebApp!'

    $.connection.sampleHub.server.request(request).done(function (response) {
      $('#ResponseText').html('<pre>Response: ' + response.Text + '</pre>');
    });
    $.connection.sampleHub.server.requestAsync(request).done(function (response) {
      $('#ResponseAsyncText').html('<pre>ResponseAsync: ' + response.Text + '</pre>');
    });
    $.connection.sampleHub.server.requestWithCallbackAsync(request);
    $.connection.sampleHub.server.joinGroup('WebApp');
  });
});

There is more information about how I wrote the code when running the sample, simply follow these instructions:

  1. Download and extract SignalR.Sample.zip
  2. On Visual Studio 2012, open SignalR.Sample.sln
  3. Right-click the solution and select "Set Startup Projects…"
  4. Select "Multiple startup projects", and set the projects in this order:
    • SignalR.Sample
    • SignalR.Sample.Client
    • SignalR.Sample.WinRTClient
  5. Set "Action" column in all projects to "Start". Click OK.
  6. Ctrl-F5 to run the projects, it will start a browser, a console app, and a windows store app.

There are more advanced samples in the SignalR source code repository, follow these steps to get them:

  1. Install the latest Git for Windows
  2. Open a Command Prompt, create a folder, and download source code


md C:\SignalR\release

cd C:\SignalR\release

git clone -b release https://github.com/SignalR/SignalR.git .

  1. On Visual Studio 2012, open C:\SignalR\release\Microsoft.AspNet.SignalR.sln

SignalR has recently shipped as RC, more details here. For the RTM version, I want to post a more complex sample, my idea is to demonstrate how to publish the server implementation on Azure and use scale-out with Service Bus. Please post a comment if you are interested to see something else.

SignalR.Sample.zip