How to detect which clients was disconnected? I'm using Hub and saw the chat example where the IDisconnect interface was implemented. De Disconnect() function is called when a page is refreshed or closed, for example. It's working ok, but how to detect which one was disconnected ?
There's a Client id property on the Hub (Context.ClientId) that you can use to figure out which client disconnected.
Related
I have a self hosted webApi project in a desktop C# application. This uses Microsoft.Owin.Hosting.WebApp.
I have a Hub derived class that has a send and a receive from an angular client.
The hub is instanced by the WebApp.start. I find that it is short lived. When the client sends a message, the hub is instanced and then disposed. I need to send message to the client, but I don't have an instance of the hub to send a message in the other direction.
At one point, I was holding a reference to the instance (as in memory leak) and I could get a message to the client.
The client shows as it is always connected. No disconnected messages.
What am I missing?
You should not hold this instance by yourself and you shold never create an instance by yourself.
Read the details about the hub onject life time here:
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server#hub-object-lifetime
In the case you need to send a message to the connected clients you need to use GetHubContext.
Example:
var context = GlobalHost.ConnectionManager.GetHubContext<yourHub>();
context.Clients.All.Send("Something");
(In the case you are using core signalr read: Call SignalR Core Hub method from Controller)
I have a simple demo with only 1 client and the server. I can send messages back and forth, very trivial stuff.
The server Hub has a timer which sends a message to the client(s) every 1000 milliseconds. Now I have a button, where when clicked, sends a message to the server (via signalR).
Problem:
When the button is clicked (and the message sent to the server), the Hub is instantiated each time (I read about the SignalR lifecycle here).
Of course, when the Hub is instantiated the Timer is also instantiated. So the side effect (ie. bug) that I am seeing is that messages are being send to the client from multiple Hub instances.
What I would like:
I would like the client to receive messages (from the Timer that is running on the Hub), but only 1 set of messages from a single Hub (latest Hub instance?). I do not want simultaneous/multiple messages that were spawned from each Hub that was instantiated.
But perhaps I am doing something drastically wrong in design here.
You shouldn´t set the timer in the hub instance because they are re-created on every request.
Just create a Singleton class to handle the timer and actions. Then access that singleton from your hub instance.
The singleton instance will persist during the whole live cycle of your application, thus you will create only one timer.
To avoid concurrency problems, your singleton should be Lazy
Is it possible to remove a client from signalR hub using its connectionId? I'am trying to create an application in which I can remove a client from hub?
SignalR does not provide an API to disconnect clients. You could workaround it by defining a client method and in the body call connection.stop()
SignalR version 2 does not have a built-in server API for disconnecting clients. In the current SignalR release, the simplest way to disconnect a client from the server is to implement a disconnect method on the client and call that method from the server. The following code sample shows a disconnect method for a JavaScript client using the generated proxy.
var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
$.connection.hub.stop();
};
Security Note: Neither this method for disconnecting clients nor the proposed built-in API will address the scenario of hacked clients that are running malicious code, since the clients could reconnect or the hacked code might remove the stopClient method or change what it does. The appropriate place to implement stateful denial-of-service (DOS) protection is not in the framework or the server layer, but rather in front-end infrastructure.
More details here
In a Hub, I am sending a message to a client using
try {
GlobalHost.ConnectionManager.GetHubContext<ContextManagerHub>().Clients.Client(connectionID).SendMessage(message);
} catch(Exception ex) {
HandleError(ex);
}
The connectionId is stored using OnConnected and removed using OnDisconnected on the Hub.
I am a little worried that I am sending messages to disconnected clients, and would like to get exceptions if I do. But the code above never raises exception, even if I try using connectionID = "BAD_ID". Anyone got an idea on how to figure out if the SendMessage actually fails?
So SignalR does not actually validate if your sending to an invalid ConnectionId. The reason why this does not occur is because ConnectionId's for clients are a lot like SignalR groups. They reference a topic that can be subscribed and published to.
Therefore when sending to an invalid ConnectionId you push a message to a topic that doesn't exist yet; so it's created. The reasoning behind this is if a client happens to come to the server with that connection id via a reconnect or by other means they will then receive any messages that they had missed during their down time.
Sooo for your case I'd recommend tracking clients via the OnConnected and OnDisconnected methods and then running logic to verify that they are present prior to sending to them.
Is it possible to send a message to the clients connected to a Hub from another process? I set up LINQPad to reference my Hub project's DLL and set VS to attach debugging to the LINQPad process. My project has a HubNotification class that uses the following code:
dynamic clients = Hub.GetClients<MyHubClass>();
clients.SendMessage("My Message");
When debugging I can see this code being called, but my connected clients never get sent a message. I have verified with Fiddler that there is nothing HTTP happening when the above code runs. Am I missing something or is this just not possible?
That call only works if you're in the same app domain. If you want to connect to the signalr service from another process/application, then you need to use a signalr client. More info here:
https://github.com/SignalR/SignalR/wiki/SignalR-Client-Hubs