This looks like a long post but it has lots of code and little explanations since SignalR is on an early stage. Stick until the end 🙂
Previously I’ve overheard about SignalR, which I’ve been told that it’s a library to allow “real time” communication with multiple web clients. My first thought was that it was some kind of message broker (like I’ve used before the RabbitMQ and MQTT protocol), but after a little reading and talking, I found out it’s much easier than that.
A couple of weeks ago, Microsoft announced SignalR (Alpha) for ASP.NET Core 2.0. This blog post is a really good entry point, and if you read it, you get a pretty straightforward idea of what’s new/different, like:
- Support for Binary Protocols and Custom Protocols
And many more things like “What’s Changed” (which I won’t cover because like I said, I’ve had zero experience with SignalR before this Alpha) and even the other topics won’t be covered here.
So what will you be talking about?
I will give you my approach/idea about how to work with SignalR in a scenario which is similar to my job’s needs.
My test case scenario and examples
In order to show a similar “real world” scenario, I’ll build a simple application simulating the following :
There are different type of workers on their workspace and they want to communicate between them. There are 3 groups:
- Cool employees
- Way Cooler Employees
For obvious reasons, they only want to talk to the others on the same group (Cool employees with cool employees, etc etc).
To simulate this interaction we will have another application that simply perform 3 actions, according to correspondent button pressed:
- C key pressed – send message only to Cool employees
- W key pressed – send message only to Way cooler employees
- B key pressed – send message only to Bosses
OK… so how does all of this work? Let’s try to visualize it:
Resuming: we want to have employees that belong to the “Cool Employees” group, others to the “Way Cooler Employees” group and the bosses that belong to the “Bosses”group. Have you noticed that I’ve been repeating the word “Groups”? It was with a purpose. With this post, I will show you how can you send messages to specific groups. But before jumping into it, let’s start from the beginning.
On the previous image, everything points to a hub, but what’s a hub? A hub is a “kind of” point-of-access (or bridge if you prefer) between the caller and the receiver(s). Having a quick look at this class:
It gives us access to properties like the Context, Clients and Groups, which we will explore next.
Starting with clients
The default Clients property is of the type
This one, also implements an IHubClients interface, which has the following methods:
Since in this case
T is of the type
IClientProxy, we will have the following available:
We are getting somewhere! With this we already know that when sending a message, we will send it to a group and our code will be something like:
But how do we register a client on a group? We’ve seen that the hub has a
Consists on a property of the type IGroupManager, which has two methods:
This is it! We already have the knowledge on how to register a client on a group and how to send a message to a specific group. Let’s implement our Hub. We need to allow each connecting client to register himself on a group, in order to receive only the messages that he wants to, and we need to create the method that will say “ok, I will send this information to this group”.
The Hub implementation
Please note that the
Groups.AddAsync() method requires a
connectionId, which in this case is the
connectionId of the client that is registering himself on a group. To get the
connectionId for the current client, we can use the
A little parenthesis:
I had a doubt on how the groups were created: did I need to create if before trying to add a connection to it, or would it try to add and if the group did not exist it would be created? Due to the amazing community, it was easy to get some of my questions answered pretty quickly, and it even generated a little and quick discussion. Share your questions/ideas with the community, join ASP.NET slack. Here’s an example of what I’m talking about:
Another awesome “feature” of the open source, is that we can have things like this. Gurgen developed an android client library for SignalR that according to him:
“At the moment, it works only by websockets transport, but later I will add all other transports
The plus is that, this is only implementation of .net core signal r for android”
Ok, so before we can run our code and see things happening, there are two things that we need to add to the
routes.MapHub(“entryPoint”) we are specifying that in order to connect to the hub, the route must be
ourAddress/entryPoint (in my case it was http://localhost:52846/entryPoint)
Testing what we’ve just created
In order to test this, we need to have a total of three projects: the project with the hub, one to simulate our clients (employees and bosses), and other to send the messages. We already know that our clients must register themselves on a group so that they can receive the desired messages.
In order to simulate the referred scenarios, on the project that simulates the employees and bosses, I decided to create Tasks, in which each simulate a client application, by connecting to the Hub, register itself on a group and setting the callback. Afterwards it just stays on a forever loop. With this in mind, some of the code that I will show isn’t obviously the best approach, but it’s just to make the simulation easier/doable.
Connect to the hub and prepare to receive the messages (employees and bosses project)
The connection itself is pretty straightforward, all we need to do is create a
HubConnection object and call the
StartAsync method. When the connection is established, we register ourselves on a group, by calling the
RegisterConnectionOnGroup method that we created on the hub. For last, we set the callback, defining what should be done when we receive a message. Let’s have a look on the code that simulates a “Cool Employee” client:
NOTE: As I just mentioned above, the
connection.StartAsync().GetAwaiter().GetResult() should not be done like that for obvious reasons (it will not be async at all), but honestly I couldn’t figure out how could I await that call inside a Task, because since I’m using
Task.Factory.StartNew(new Action(…)) I can’t set the await on the method, because it would change the signature, hence it wouldn’t be an Action. In this case it is just to ensure that we invoke the
RegisterConnectionOnGroup only after the connection is established. Do you know how to solve this? Please PM me or even better, comment down below!
The MessageSender project
This application just sits on a forever loop after establishing connection to the hub (via the same
HubConnection logic) and “implements” the logic that I previously referred:
- Send a message only to the bosses? Press B
- Send a message only to the Cool Employees? Press C
- Send a message only to the Way Cooler Employees? Press W
With this, we can finally test what was developed. First launch the project that has the Hub (SignalR project, in this case the
EmployeeSignalR), then the
SignalRClient1 which will create the various clients referred, and finally the
MessageSender. Place the
MessageSenderconsoles side by side, and press the desired key on the
MessageSender‘s console. See the magic happening!
Here’s an example:
For me, this was a whole new world since I’ve never really had contact with SignalR until I had created this project. Obviously, there might be some things that I said that are not exactly like i said, but as I stated at the beginning I tried to “give you my approach/idea about how to work with SignalR in a scenario which is similar to my job’s need”. Since none of us know everything, if you spot a mistake or you have another approach, please let me know!
A special thanks to David Fowler (b | t) for his help and quick replies to my questions! And also, thanks to Gurgen for his intervention on the explanation and his availability to talk to me about signalR.
You can download the full example from my github repo
Thank you very much for reading