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:
- Add a Hub called
EchoHub
. - Add an OWIN Startup class named
Startup
with itsConfiguration()
method containing just a simpleapp.MapSignalR();
bootstrap call. - 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 theConnectionId
property exposed by theContext
member from the Hub type - When
msg
is ready, we use theClients
member from the Hub type, and from there we take a reference to theAll
member, representing all the currently connected clients - We finally call the
greetings()
method on theall
variable, supplying themsg
we just built; we expect this call to be performed on all the connected clients, and to be triggered at every call to theHello()
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 theclient
member, to which we add a function calledgreetings()
whose name and signature are matching the method that the Hub is trying to call back; the function will append every received message to aul
list present on the page marked with anid
attribute whose value ismessages
- We then start the connection, and when it's ready we hook the
click
event of thesend
button on the page to the action of calling theHello()
method on theecho
hub proxy through itsserver
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 it works…
The mechanics are the same as explained in the previous recipe, so please refer to it for more details.