Adding a connection to a group
Multiple clients can connect to a specific Hub at the same time, and the number of connections can easily increase. SignalR offers a feature called groups in order to define subsets of connections and use them when broadcasting to clients. We will see how a Hub can place connected clients into groups and then broadcast calls to all the connections belonging to a specific group.
Getting ready
Before writing the actual code, we need to create a new empty web application, which we'll call Recipe08
.
How to do it…
We're ready to start building our sample. We need to perform the following steps:
- Add a Hub called
EchoHub
. - Add an OWIN Startup class named
Startup
with aConfiguration()
method callingapp.MapSignalR();
. - Add the following to the Hub code:
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace Recipe08 { [HubName("echo")] public class EchoHub : Hub { public void Subscribe(string groupName) { Groups.Add(Context.ConnectionId, groupName); } public void Hello(string groupName) { var msg = string.Format("Welcome from {0}", groupName); Clients.Group(groupName).greetings(msg); } } }
What's important here? We need to make sure that we perform the following steps:
- We first expose a
Subscribe()
method, which receives agroupName
parameter, and then uses theAdd()
method of theGroups
member inherited fromHub
to add theConnectionId
of the caller to a group called after the specified argument - The
Hello()
method builds a message (msg
) and uses theGroup()
method of theClients
member inherited fromHub
to broadcast a request to invoke the client-sidegreetings()
method towards all the connections that joined thegroupName
group by calling theSubscribe()
method - We are allowed to dynamically call whatever method we want to because
Group()
returns a dynamic object; it works the same way we saw earlier forCaller
orAll
This is a very powerful API, but it's also very straightforward. You first add any connection to a certain group, and then you can access all the connections belonging to any group altogether to broadcast client-side calls towards them from the context of a server-side Hub method.
You could think about a group as a sort of topic attached to the connection. SignalR is able to efficiently target sets of connections marked with the same topic.
Let's proceed with the index.html
client page and add the following code to it:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Recipe08</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 () { $('#subscribe').click(function () { $(this).toggle(); var v = $('#group').val(); hub.server.subscribe(v); $('#send').click(function () { hub.server.hello(v); }); $('#send').toggle(); }); }); }); </script> </head> <body> <label>Group:</label> <select id="group"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <button id="subscribe">Subscribe</button> <button id="send" style="display: none">Say Hello!</button> <ul id="messages"></ul> </body> </html>
At the bottom of the file, we see some HTML markup declaring a drop-down list with three group names to choose from (A
, B
, and C
), a button to perform the subscription, another one to say hello, and an unordered list to collect all the messages that will be received from the hub. Let's see what happens inside the <script>
block:
- 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 match the method that the Hub is trying to call back. The function will append every received message to aul
list present on the page and marked with anid
attribute whose value ismessages
. - We then start the connection and when it's ready, we hook the
click
event of thesubscribe
button to the action of calling thesubscribe()
method on theecho
hub through theserver
member of the hub proxy. - After calling
subscribe()
, we set up theclick
event of thesend
button to call theSend()
method on theecho
hub. Bothsubscribe()
andSend()
target the same destination group, but this is just the way this sample is built; of course, it does not have to be like that. The code will also take care of toggling the visibility of the two buttons with simple jQuery methods. - We decide to hook the
click
event on the buttons just inside thedone
callback to ensure that we can call thesubscribe()
andHello()
methods only when the connection has been established (remember that starting a connection is an asynchronous operation).
We might have done the same to enable the send
button after the asynchronous completion of the subscribe()
call, but we'll dig more into this kind of complex workflow in the future recipes.
Now we can open index.html
multiple times in different browser windows (or tabs), and then we can click on the Subscribe button after selecting one from the three available groups. After that, we'll be allowed to click on the Say Hello! button on any of the browser windows we have, and we'll notice the following points:
- Each hit on the Say Hello! button will send a message only to the windows that have already subscribed to the same group as the one where the button was clicked on
- Each window will receive messages only from other windows that have subscribed to the same group as the source window
How it works…
SignalR manages the group topics in a very smart way, which actually does not involve any state storage at the server side. It has information associated to the connection only, and this strategy allows SignalR to have great horizontal scalability. If you need to persist group associations across physical connections (think about putting users, not just connections into groups), you'll have to resort to more complex strategies, which will be analyzed in future recipes.
Adding a connection to a group is always a safe operation, as depicted by the following points:
- A group, if not yet existing, is automatically created when calling
Add
- You do not have to check if a connection is already present inside the group, because calling
Add
multiple times for the same connection is an idempotent operation without any unexpected side effects - A connection can be added to multiple groups at the same time