New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to disconnect specified client from outside hub #5333
Comments
Yeah IClientProxy doesn't have the Abort method. It's just available in the hub context. I'd suggest invoking a method on client side which will gracefully disconnect then. |
We don't really support this from HubContext, but it's something to consider for the future. A graceful shutdown based on invoking a "Shutdown" method on the client is more appropriate here, as @atresnjo suggests. Abruptly terminating connections should generally only be used when the client is unresponsive for uncooperative. It's difficult to add this ability to the HubContext because in a multi-server scenario (when scaling), we don't know if the code that's calling "Abort" is running on the same server as the connection. If it's not, then we have to send that information to the appropriate server, which has a whole bunch of other issues behind it. |
I want client to directly invok OnDisconnectedAsync method ( |
No. Implement your own method and invoke it when needed. Something like:
|
You may have misunderstood me. My demo code is as follows : -----server code
// webAPI
// web client JS
I want to disconnect connection of HTTP, Such as websocket , Should I request DisconnectUser1 or |
Do not call |
In these cases, you can just use Here's a complete sample that will allow you to signal a client to disconnect from the server outside the Hub: NotificationHub.cs: public class NotificationHub: Hub
{
// You don't need any code here to specifically support this. But if you want to
// run code when a client does disconnect, you can override OnDisconnectedAsync
} Client.js var connection = new signalR.HubConnectionBuilder().withUrl("http://localhost:5050/notificationhub").build();
connection.on("RequestShutdown", function (reason) {
console.log("Server requested we disconnect because: " + reason);
connection.stop();
});
connection.start().catch(function (err) {
console.log(err.toString());
return console.error(err.toString());
}).then(function(data) {
// Do startup things
}); WebAPI.cs [Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHubContext<NotificationHub> _hubContext;
[HttpPost]
public bool DisconnectUser(string userName)
{
_hubContext.User(userName).SendAsync("RequestShutdown", "reason");
}
} |
OK,I understand,Thank you very much @atresnjo @anurse @BrennanConroy |
Blocked by #3284 |
Has there been any progress made on this issue? |
It's still in the backlog. Let us know your scenario though and it will help us prioritize this work in a future release! |
We are developing an MMO game, and we are in the process of switching over to use SignalR. The functionality we have not been able to port over to SignalR includes:
In both of these scenarios, the server needs to have the authority to disconnect players without sending a disconnect request. |
That makes sense! Thanks for the info. |
For us, this would also be a very important feature. We are working on a new backend for a game. There are many scenarios where a client could not be cooperative and will try to cause harm - in which case we have to forcefully close the connection from the server. In addition, we also have similar scenarios like those described by @thorgeirk11 Modifying the client and changing any client-side disconnection logic in order to ignore the requested disconnect from the server is trivial. |
There is likely a workaround way to implement this right now. The necessary logic should already exist in the While we don't generally recommend doing this, it is currently OK to capture the I do agree that we should look at a more first-class way to do this, but I'd be curious if this workaround works for your scenario. Would you be able to try it and report back? We'll look at this for 5.0, but that's not coming until November 2020 and I'd like to be able to give you something that works before then :). |
An important note about my workaround: This will only work on the physical server process on which the client is connected. It will not be possible to forcibly disconnect a connection that resides on a different physical server process (in a scenario where you have multiple servers connected via a backplane like Redis or Azure SignalR). Honestly, I'd expect the first-class solution to have a similar restriction, since it's very tricky to make that work reliably (since we don't have a single source of truth for which connections exist and on which servers they reside). |
I was thinking about storing the |
It should be safe. I do want to clarify that it's a bit of an unsupported path. I don't expect we'd break it (and certainly not until we have a first-class way to disconnect a specific client) but I would definitely put some comments in there referencing this issue and plan to switch to the actual feature once we get one in ;). It may be that we just make this a fully-supported thing (capturing the |
As useful as this feature is I don't think we should implement anything that needs to communicate across servers over the backplane to take an action on a connection. Aborting the socket needs to happen from the server where that socket is. It's simple an reliable (at least is should be) and there's no possibility of missing the notification because redis went down or because a backplane is poorly written. This one of the major reason the old version of SignalR has such gnarly reliability bugs and those mistakes shouldn't be repeated here. SignalR needs to remain a super thin layer of RPC over WebSockets with some nice ways to send/receive messages with different protocols. |
@davidfowl et al; I am using SignalR for sending notifications to clients in a social app (ASP.NET sever and Xamarin .NET client). It is of utmost importance that when a client logs out (either from the current session, or all of his sessions), that the server aborts connections to said client. I can not rely on the client aborting the connection itself; no the server must obey when a client requests a disconnection. In no scenario can a connection remain open to a client after he has requested a disconnection or I won't be able to ship my product, as it is trivial to hack a client to not obey. I have implemented what was suggested here, ie I cache the context object in a ConcurrentDictionary in a singleton service. @davidfowl you said that even when Abort() is fully supported outside of a hub you won't implement it "at scale". My plan was to use the SignalR Service on Azure. Also, is it possible that I'll run into memory issues by saving the context object? I have to do it now as there is no supported way to call Abort() outside of the hub, but I want to make sure I am not opening up more problems by following this guidance. Thanks. |
Abort works fine on the Azure signalr service and that’s the way to do it |
Thank you. It was not clear to me that Azure SignalR service would work out of the box. Thanks for the clarification. |
@davidfowl @anurse I'd like to just verify that it will indeed work with the Azure SignalR service at scale, as @anurse wrote in the quoted section that it won't. So I have conflicting information here. Thank you. |
@davidfowl @anurse Hi, is there any way you guys could confirm whether calling Abort() works in all cases when using the SignalR service? @anurse said it only works when they are on the same server, while @davidfowl said it works fine in all cases. I will be deploying my server with the SignalR service. My server sends notifications to connected clients through SignalR. It also provides a REST API where clients can log out, in which case the server revokes his access token and calls Abort() on any connection(s) the client may have. Is it guaranteed that Abort() will break the pipe to the user in all cases in this scenario? If it works, fantastic, if not, what is the guidance on how to work around this? Thank you so much! |
really need a way to disconnect Clients/Users from the server side, API like below: should remove all those connectionIds from the joined Group automatically when call those APIs. to manually bookkeeping the connectionId and intercept the call to Hub then call Context.Abort() if Context.connectionId match the saved connectionId, there is too much work to do out of the lib and hardly do it right by the application developers. if simply one server, keep in the memory is fine, but if want to scale, say, with Redis, then need another different implimentation,to keep the connectionId in the redis as well. better to have this feature in the signalr lib itself, and can scale the same way like scale out signalr. |
I also want an answer to the question @Tommigun1980 asked earlier:
How can this be achieved if connections are shared between multiple machines, and when call REST api endpoint, I cannot know for sure if the client is connected or not on the machine where the REST api call originated? |
By storing the presence information in a database and checking that information when you want to send the message to the client. If the message is server generated then you don't need send a message. You can build a system that polls the status of connections on each node/machine/pod/compute unit and abort connections that are marked for abort. There are lots of ways to build this functionality |
@davidfowl , Good idea! Thanks |
Hi and thanks for the feedback.
Considering that the SignalR service has to ultimately deduce this very information itself (which is the exact service it provides) I would like to use this very information myself, instead of trying to build a robust fault-tolerant scaling system just for this (while essentially replicating the service).
...
Suggestion:
Add an alternative call/method that returns delivery status (say just a boolean for whether connected or not).
It would work just like the regular send, but await until status is known, and trickle it back to the caller.
...
This is the very kind of thing I would really appreciate (and even expect) from a service as it solves a very common real life scenario, where some other action should take place if a user is not connected (in my case sending a push notification instead).
If I have to start implementing this then I’m circumventing/replicating the entire service for pretty much every call, and this information must be known by/deduced by the service in order to do its thing - if it just could be sent back would be absolutely fantastic!!
Thanks so much for considering! I would really really appreciate this feature!
|
There's no way to implement this feature in a distributed system efficiently in a way where you can always know if the person in online or offline before sending a message. Once you accept that then you'll recognize that you need to adjust the application behavior to deal with the potential to send a message to the client that isn't there and deal with the fallout of that. Signalr doesn't give any feedback about messages being received by the client. That said I do think presence information is useful and maybe will be in the service one day. |
As for the client acks, there is an issue for it here #5280 |
Hi and thanks for the reply. This doesn’t need to be known before the fact. At some point some part of your service has to make the decision for whether to send (ie does a connection exist), right? Could this information be trickled back? It is an extremely common use case to send a push if a user is not connected, and for this it needs to be known whether any connection exists to a user. It may be somewhat tricky, but this is the exact reason why I’m using your service in the first place. To solve these things for me. Thank you again for considering. |
And to clarify, I only need to get back whether the service tried to send the message. bool connected = await client.Send(x);
if (!connected)
// send push The send call goes through your system through various parts, until some part ultimately takes the decision whether to proceed or abort. Every part in the chain trickles this information up. Why wouldn’t this be possible? This would save us from recreating the service. Thank you again. |
Between the send and the call you make to send a push notification. The client can be back online. Then what? |
This “problem” exists everywhere, in any system that sends a push if a client is not connected (like Facebook Messenger).
Naturally the system returns the state that was valid at the time and that information trickles up.
If a client comes online while a call is being made which returns “not connected”, then that information is used and a push is sent. That push is not displayed on the client as the app is in the foreground (or he sees the notification while the app boots if some OS allows foreground push).
*It’s the same with every application.* Why would it be a problem at all? This is how they all work.
It feels to me like solving a problem that’s not there, all systems just return the state they operated on at the point in time it happened.
…On Sunday, February 7, 2021, David Fowler ***@***.***> wrote:
Between the send and the call you make to send a push notification. The
client can be back online. Then what?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5333 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AOL6P6ENCCKXIBJMFIUREG3S5XD2LANCNFSM4GK3Y6OA>
.
--
|
Also, if this was a real problem it would be a thousand times worse by having to make a separate call first to see how to proceed. The best most efficient way is for the service to return this info, it can’t get any better than that. |
And I’ll elaborate a bit even further: The other option is to always also send a push (ie send message + push).
So only sending a push when needed is the way to go. If the client connects a millisecond after the server checks connection status, and sends one "unnecessary" push - what’s the harm? It’s the best we can do, how every app works, and it doesn’t have negative effects. We were operating on the information available at the time, and it’s the best we can ever do (unless we implement time travel). |
Today the presence information isn't provided by the service so you need to do that, it's not about the send itself, it's about this information and it isn't tied to the call to send. Just use that information to send the push notification based on the current state even if it is potentially out of sync. As long as your application can handle the eventual consistency of it (and it has to), then it's fine. |
Thank you for the reply.
I understand that. Hence my suggestion to not just "fire and forget" the call, but to send back the information all the way to the caller.
Ultimately some part in the service has to make the actual call to a client - all I'm asking for is to trickle this back, i.e. whether an invocation was attempted.
I'm not asking for a state info endpoint or such, I just want to the status to be returned back to the caller.
…On Sun, Feb 7, 2021 at 1:50 AM David Fowler ***@***.***> wrote:
Today the presence information isn't provided by the service so you need
to do that, it's not about the send itself, it's about this information and
it isn't tied to the call to send. Just use that information to send the
push notification based on the current state even if it is potentially out
of sync. As long as your application can handle the eventual consistency of
it (and it has to), then it's fine.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5333 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AOL6P6FYL6J4LQTI6JO7PWDS5XIVPANCNFSM4GK3Y6OA>
.
|
While that's a separate issue, I don't think we'd add the API for this scenario. If you choose to use it for that, that's great, but the purpose of that issue is to return results from the client, not to detect if that client is online. Maybe you can use a failure to retrieve the result as the way to detect the offlineness. |
I would love to do that, but I am failing to see how. This is my current implementation: public async Task SendNotification(
Guid accountId,
Func<IClient, Task> RPCCallAction = null,
Func<Task<PushMessage>> pushMessageGetter = null)
{
if (RPCCallAction == null && pushMessageGetter == null)
return;
if (await this.IsConnected(accountId))
{
if (RPCCallAction != null)
{
var clientInterface = this.connectionHub.Clients.Group(accountId.ToString());
await RPCCallAction(clientInterface);
}
}
else
{
if (pushMessageGetter != null && this.pushService.IsEnabled())
await this.pushService.SendPush(await pushMessageGetter(), new string[] { accountId.ToString() });
}
} I.e. if a client is connected, it sends an RPC to all his active devices. Else it sends a push message. var clientInterface = this.connectionHub.Clients.Group(accountId.ToString());
bool hadConnections = await RPCCallAction(clientInterface); // the status of this would trickle back here!
if (!hadConnections)
// send push here You said I could use an error status for this which I'd be very happy to do, but how? There will be no error if no call is made here: var clientInterface = this.connectionHub.Clients.Group(accountId.ToString());
await RPCCallAction(clientInterface); So how could I catch this as an error? Thank you again!! |
I just can't understand why awaiting an RPC can't return the status information, or why it can't be added. But if it can't be added for any reason I'd be more than happy to catch "nothing was sent" errors and handle that as an offline case as you suggested. But calling an RPC on an empty IClient doesn't raise an error, so I don't understand how I could do that? I'm very very happy if this can be done! |
If anyone is interested, IsConnected is currently implemented as such: Every node uses an IMemoryCache for the case where the same node also serves the destination connected. If not it calls the Azure SignalR service's I also keep connection ids in an IDistributedCache (Azure Redis), so I can disconnect any client on any node when needed. |
As I mentioned, there's an issue to enable this feature when sending to specific clients #5280. It's not currently on track to be implemented for .NET 6 either. It's non trivial to implement acks like this but its highly requested and we may spend some time figuring out all of the pieces. Then you could build something that used this new feature and tried to send a message to all clients for a specific user and decide if you should send a push notification instead. Right now, you need to store presence information (as mentioned above) and optimistically do it. Making the RPC wait for a reply from the client would slow down every operation and is even trickier when the ACK needs to work across a server farm. This conversation has veered far from the original topic so I'd suggest piling onto that issue specified. |
Fair enough. As a last thing, could you please just let me know how to implement I loved that suggestion but I don't see any errors happening anywhere. So how would I achieve what you suggested? Thank you! |
It was part of that paragraph meaning that's how you could use the client ACK feature to implement the semantics you want. The feature doesn't exist. |
Thank you for the clarification, I thought you meant I could use it as an alternative until ACK is implemented. |
Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue. This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue! |
I have too many other things to try to work on besides opening a new issue for this, but I'm still interested in having inbuilt support for disconnecting a specific client for security reasons (leaving clients without valid accounts connected leaks information they should no longer have access to). |
I have a web Api for disconnectting specified client ,how to disconnect specified client from outside hub.
I do not find a method about disconnect client from IHubContext;
The text was updated successfully, but these errors were encountered: