SignalR send message to Clients from external application - signalr

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

Related

SignalR hub is shortlived

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)

How to use SignalR with Redis scale-out backend in a console application or worker role?

I have a web app that uses SignalR with Redis scale-out (allegedly) and hubs, and that works fine. Now I need a worker role to be able to send messages to all connected clients but I'm not sure how to do it. I also have a console application that serves as a test app which runs the methods immediately that the worker will run periodically.
Hosting a web server within the worker or console app seems odd, but that's mostly I've found so far. Talking to the web app seems odd also. Shouldn't it be able to just post a message to Redis and all the subscribers pick it up?
This is the code I'm using. I copied the UseRedis call and the handleExecutedAction pretty much verbatim from the web app. It doesn't throw or anything but the message doesn't get to the web client:
GlobalHost.DependencyResolver.UseRedis(new RedisScaleoutConfiguration("...", "..."));
var hubContext = GlobalHost.ConnectionManager.GetHubContext<WebUI.EventHub>();
hubContext.Clients.All.handleExecutedAction(new Action { Type = "testing" });
I used this method (the context) after finding this page. I know the client is working because other messages come through from the web app, just nothing from the console app...

SignalR Hub Issue - Restarting iis creates a new hub instance

I am using asp.Net SignalR Dependency injection technique for our implementation, so in our case, we have a hub constructor with one parameter. I was wondering if there is a problem I have with the following, or is this how the system should react?
When I restart IIS, signalR will lose connection of course, and then come back online. Afterwards, when it reconnects to our SignalR hub, it attempts to connect to the parameterless Hub constructor instead of the constructor with one parameter that is setup via the NinjectDependecnyResolver. It seems to be trying to connect to the default Hub DependcyResolver after I restart IIS and signalR reconnects. If I refresh the page after the IIS refresh, however, the paramaterized constructor is called, as it should be.
Does anyone know why my provided constructor is not fired after reconnecting to the SignalR hub after an IIS restart/IIS Stop-Start?

How to Intercept ScaleoutMessage Broadcast: (Edited: How to send message directly to ServiceBus SignalR Backplane)

I have following scenario:
User request for certain resource on server, This request is long running task and very like 2~3 seconds to 10 seconds. We issue a JobTicket to user, As our user want to wait.
On receiving request we store that request in persistence storage and issue a token to user as JobTicket (GUID).
User make connection with Hub to get information about that GUID.
In Background:
We have WAS Hosted as well as Windows Service to perform some operation on that request.
On complete, WAS Hosted/Windows Service call our Web Application that job has been completed.
From there based on job Ticket we identify which user and on its connection we let user know its job has been completed.
Now we have farm of servers, we are using Windows Server On Prem ServiceBus 1.1 which is working fine, But challenge we have is that we are not able to intercept ServiceBus based backplane message broadcast and message is going to all the client. As we have farm, user intermediately may have drop connection and connected to other server based on load balancer so we need to have scale out using Service Bus as its kind of seamless to integrate and we are also using for our internal purpose in our application so we don't want to user any other mix in complex solution.
I have tried using IHubPipelineModule but still Scale out message broadcast not passing thru that, I tried to hookup SignalR code directly and debug thru it but its taking long. I don't want to mess-up something arbitrary in actual code. As I can see that in OnReceive I can see message are coming, but not able to follow further. I just need small mechanism that I can intercept broadcast message and make sure that it goes to client it intended and not all the client by wasting resources, and security concern as well.
Please help me on this issue, it's kind of stuck from last 4 days and not able to come to any solution and same time I want to go with establish pattern and don't want to fork any special build for this kind of small issues which I am sure one of you expert knows how I can do that seamlessly.
Thanks,
Shrenik
After lots of struggling and not finding straight forward way, I have found the way as below for someone else in future it might help.
Scenario:
1. Web Farm: Host External User facing Web Pages
2. Backend Process: Which is mix of WebApi, SharePoint, Windows Service etc.
User from Web Page submit some request and get a unique id as return back. Internally on receiving request, we queue that request to Service Bus using TopicClient for processing.
There are pool of Windows Service watching on Message on Service Bus using SubscriptionClient and process that message. On completion of process which can run from 5 seconds to 30 seconds and some cases even more. We need to inform client that its job done if its waiting on web page or waiting for completion notification.
In this story, We are using SignalR to push job completion notification to client.
Now my earlier problem is How I let know from windows service to web application that job is done so send notification to client who submitted request.
One way is we hosted another hub internally in web application, Windows service act as client and call web application hosted hub, and in that hub method it will call external facing hub method to propagate message to specific client who submitted request, for which we are using Single user Group.
And as we have register service bus as backplane it will propagate to other servers and then appropriate client will get notification. So this is ideal solution and should work in most cases.
In above approach we have one limitation that, how Windows Service connect to Web Client, as we donot have windows auth, but we have openid based auth with ADFS. Now in such case Web Application required special code in which provide separate userid or password for windows service to communicate or have windows authentication also allowed for that hub for service account of windows service.
I was trying and trying how to remove all this hopes between interserver communication and again management of extra security.
So I did below with simplicity, though it tooks me whole night to find our internal of SignalR. But it works:
Approach is to send message directly to ServiceBus Backplane, and as all Web Server already hooked-up with ServiceBus backplane then they will get message.
Unfortunately SignalR doesn't provide such mechanism to send message directly to Backplane. I think its on pub/sub model so they don't want somebody to hack in their system :). or its violation of their pattern, but its make sense, in my case because of different roles and security, I have simplify code as below:
Create a ServiceBusMessageBus instance in my code, Same way as Below: Though I have created separate instance and store till lifetime of Windows Service, so I don't create instance every time:
ServiceBusMessageBus serviceBusBackplane = new ServiceBusMessageBus(new DefaultDependencyResolver(), new ServiceBusScaleoutConfiguration(connectionString, appName));
Create a ClientHubInvocation Object: This is a message which actually get created in SignalR infrastructure when Backplane based message broadcast:
ClientHubInvocation hubData = new ClientHubInvocation
{
Args = new object[] { msg },
Hub = "JobStatusHub",
Method = "onJobStatus",
State = null,
};
Create a Message object which accept by ServiceBusMessageBus.Publish, Yes, so this is a method which actually get called on base class ScaleoutMessageBus.Publish. This class is actually responsible for sending message to topic and other subscribers on other server nodes. Why not use that directly. Now to create Message Object, You need following code:
Message backplaneMessage = new Message(
sourceId,
"hg-JobStatusHub." + name,
new ArraySegment(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(hubData))));
In above second parameter is something interesting,
In case if you want to publish to all the client then syntax is "h-", in my case specific group user, so syntax is "hg-.. You can check the code here: https://github.com/SignalR/SignalR/blob/bc9412bcab0f5ef097c7dc919e3ea1b37fc8718c/src/Microsoft.AspNet.SignalR.Core/Infrastructure/PrefixHelper.cs
Publish your message to backplane directly as below:
await serviceBusBackplane.Publish(backplaneMessage);
I wish this PrefixHelper class have been public.
Remember: This is not recommended way and doent insulate from future upgrade for SignalR, as its internal they may change so any upgrade might come with small hazale to change this code. But in summary this works. Hope SignalR Team provide some mechanisam out of box to send message directly to backplane instead.
Thanks

SignalR- Removing a Client From signalR Hub

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

Resources