SignalR Realtime Application Cookbook
上QQ阅读APP看书,第一时间看更新

Broadcasting to all connected clients

After several examples where a single client was involved at a time, we'll start looking at scenarios where multiple clients are connected at the same time, and we'll start making them interact. We will see how a Hub can call back into all the connected clients from the context of a single remote method call performed by just one of them. This is where SignalR becomes a more interesting and outstanding library!

Getting ready

Before proceeding, we need to create a new empty web application, which we'll call Recipe07.

How to do it…

As usual, we need some simple steps to get started as follows:

  1. Add a Hub called EchoHub.
  2. Add an OWIN Startup class named Startup with its Configuration() method containing just a simple app.MapSignalR(); bootstrap call.
  3. Modify the file content to make it look like the following code:
    using System;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace Recipe07
    {
        [HubName("echo")]
        public class EchoHub : Hub
        {
            public void Hello()
            {
                var msg = string.Format("Greetings {0}, it's
                    {1:F}!",
                    Context.ConnectionId, 
                    DateTime.Now);
                var all = Clients.All;
                all.greetings(msg);
            }
        }
    }

What's important here? We need to make sure that we perform the following steps:

  • Inside the Hello() method, we build a message (msg) using the current time and the ConnectionId property exposed by the Context member from the Hub type
  • When msg is ready, we use the Clients member from the Hub type, and from there we take a reference to the All member, representing all the currently connected clients
  • We finally call the greetings() method on the all variable, supplying the msg we just built; we expect this call to be performed on all the connected clients, and to be triggered at every call to the Hello() method performed by any of them

The All member, like the Caller one we saw earlier, is a dynamic object over which we can try to call whatever method we want. The call will be resolved at runtime by SignalR, and it will reach all the connected clients with the only precondition that a method with the same name and expecting the same parameters must be available on the client-side hub proxy.

To test our hub, let's build a client HTML page, which we'll call index.html. Add the following code to this file:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Recipe07</title>
    <script src="Scripts/jquery-2.1.0.js"></script>
    <script src="Scripts/jquery.signalR-2.0.2.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
        $(function () {
            var hub = $.connection.echo;

            hub.client.greetings = function (message) {
                var li = $('<li/>').html(message);
                $('#messages').append(li);
            };

            $.connection.hub
                .start()
                .done(function () {
                    $('#send').click(function () {
                        hub.server.hello();
                    });
                });
        });
    </script>

</head>
<body>
    <button id="send">Say Hello!</button>
    <ul id="messages"></ul>
</body>
</html>

What's important here? We need to make sure that we perform the following steps:

  • We take a reference to the echo hub, and from there we access the client member, to which we add a function called greetings() whose name and signature are matching the method that the Hub is trying to call back; the function will append every received message to a ul list present on the page marked with an id attribute whose value is messages
  • We then start the connection, and when it's ready we hook the click event of the send button on the page to the action of calling the Hello() method on the echo hub proxy through its server member
  • Remember, the client member has to be filled with the callback function(s) before starting the connection

Now we can run the application by navigating to index.html multiple times in different browser windows (or tabs), and then clicking on the Say Hello! button on each of them. We should see all the browser windows receiving the same message each time we push the button. The Clients.All member is effectively gives us access to the whole set of active connections, and it provides a way to broadcast method calls to all of them at once.

At every call, we'll notice that the printed time changes, but not the ConnectionId of the browser window from which the call comes, and no page refresh happens. On the other hand, every time we refresh one of the windows and click on the button, a new ConnectionId, representing that specific browser instance connection, will be shown. The following screenshot depicts this:

How to do it…

How it works…

The mechanics are the same as explained in the previous recipe, so please refer to it for more details.