How to debug SignalR server event that fails to fire from client? - c#

When I copy the code from this tutorial into my current project, I have no problems using their Send method.
However, in my own code, my server Send method never fires.
Server code:
public class ModelingHub : Hub
{
public void Send(string message) // my breakpoint is never hit here
{
Clients.All.broadcastMessage(message);
}
}
Client Code
$(function () {
var modelHub = $.connection.modelingHub;
modelHub.client.broadcastMessage = function(response) {
alert(response);
};
$.connection.hub.start().done(function () {
alert(1); // this fires
$("body").on("click", function () {
alert(2); // this fires too
modelHub.server.send("test"); // never fires
});
});
});
I don't want to go messing with the library code from SignalR, but I'm not sure how to debug this any further.
I can't call the method from the console either:
>$.connection.modelingHub.server.send("test")
Note that when I implement OnConnected and OnDisconnected events in my ModelingHub subclass, they work fine. I've taken them out to debug the Send failure.

Your issue stems from your class, you do the following:
public class ModelingHub : Hub
{
public void Send(string message)
{
var call = message;
}
}
But you aren't actually doing anything with your message, you need your hub to actually broadcast the data. You would need to add code similar or this below your variable call.
Clients.All.broadcastMessage(call);
If you don't sanitize your data, the method body could be:
public void Send(string message) => Clients.All.broadcastMessage(message);
Also, if I recall that tutorial has an interface called Send which expects two parameters, you may want to also check there to ensure you modified to accept your single parameter.

Make sure that you reference the correct version of SignalR. The tutorial references version 2.2.1 but the current version is 2.2.2 so that is a potential issue.
Check the browser console to see if you have any errors and make sure the version numbers of the references match up with what you have in your project.

Related

ASP.NET Core 2.2 SignalR Buffering Calls Instead of Invoking Asynchronously

I'm writing an ASP.NET Core 2.2 C# web application that uses SignalR to take calls from JavaScript in a web browser. On the server side, I initialize SignalR like this:
public static void ConfigureServices(IServiceCollection services)
{
...
// Use SignalR
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
});
}
and
public static void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Route to SignalR hubs
app.UseSignalR(routes =>
{
routes.MapHub<ClientProxySignalR>("/clienthub");
});
...
}
My SignalR Hub class has a method like this:
public class ClientProxySignalR : Hub
{
...
public async Task<IEnumerable<TagDescriptor>> GetRealtimeTags(string project)
{
return await _requestRouter.GetRealtimeTags(project).ConfigureAwait(false);
}
...
}
and on the client side:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/clienthub")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.start().then(function () {
...
// Enable buttons & stuff so you can click
...
}
document.getElementById("tagGetter").addEventListener("click", function (event) {
connection.invoke("GetRealtimeTags", "Project1").then(data => {
...
// use data
...
}
}
This all works as far as it goes, and it does work asynchronously. So if I click the "tagGetter" button, it invokes the "GetRealtimeTags" method on my Hub and the "then" portion is invoked when the data comes back. It is also true that if this takes a while to run, and I click the "tagGetter" button again in the meantime, it makes the .invoke("GetRealtimeTags") call again...at least in the JavaScript.
However...this is where the problem occurs. Although the second call is made in the JavaScript, it will not trigger the corresponding method in my SignalR Hub class until the first call finishes. This doesn't match my understanding of what is supposed to happen. I thought that each invocation of a SignalR hub method back to the server would cause the creation of a new instance of the hub class to handle the call. Instead, the first call seems to be blocking the second.
If I create two different connections in my JavaScript code, then I am able to make two simultaneous calls on them without one blocking the other. But I know that isn't the right way to make this work.
So my question is: what am I doing wrong in this case?
This is by design of websockets to ensure messages are delivered in exact order.
You can refer to this for more information: https://hpbn.co/websocket/
Quoted:
The preceding example attempts to send application updates to the
server, but only if the previous messages have been drained from the
client’s buffer. Why bother with such checks? All WebSocket messages
are delivered in the exact order in which they are queued by the
client. As a result, a large backlog of queued messages, or even a
single large message, will delay delivery of messages queued behind
it—head-of-line blocking!
They also suggest a workaround solution:
To work around this problem, the application can split large messages
into smaller chunks, monitor the bufferedAmount value carefully to
avoid head-of-line blocking, and even implement its own priority queue
for pending messages instead of blindly queuing them all on the
socket.
Interesting question.
I think the button should be disabled and show a loading icon on the first time it is clicked. But maybe your UI enables more than one project to be loaded at once. Just thought for a second we might have an X-Y problem.
Anyways, to answer your question:
One way you can easily deal with this is to decouple the process of "requesting" data from the process of "getting and sending" data to the user when it is ready.
Don't await GetRealtimeTags and instead start a background task noting the connection id of the caller
Return nothing from GetRealtimeTags
Once the result is ready in the background task, call a new RealtimeTagsReady method that will call the JavaScript client with the results using the connection id kept earlier
Let me know if this helps.

How Can I Manually Add Connections to Groups with Multiple Hubs in SignalR

I have two hubs, call them NotificationHub and ReminderHub. Think of NotificationHub as the main hub, and ReminderHub as an optional hub that I wish to separate from NotificationHub. Clients will connect to NotificationHub with the following typical server hub method.
public override Task OnConnected()
{
return base.OnConnected()
}
with corresponding client connection
$.connection.hub.start().done(function() {
subscribeToReminderHub();
});
subscribeToReminderHub(); contains the following
subscribeToReminderHub = function() {
reminderProxy = $.connection.reminderHub;
reminderProxy.server.subscribe().done(function() {
console.log('subscribed to reminder hub...');
});
}
reminderProxy.server.subscribe() refers to the following server method on ReminderHub
public async Task Subscribe()
{
var currentUser = Context.User.Identity.Name.ToUpperInvariant();
await Groups.Add(Context.ConnectionId, currentUser);
}
This all works as I would tyically expect. I can hit a break point on the server Subscribe() method, as well as log out
subscribed to reminder hub...
However, if I try to then invoke methods on users in the groupings I am trying to establish in ReminderHub, nothing will occur. I have defined two client functions inside my initial connection .done() callback. Consider the following example
public void Notify() // ... ReminderHub
{
// ***** notification chain - step 2
// ***** this never gets called
var userId = Context.User.Identity.Name.ToUpperInvariant();
Clients.Caller.notify();
}
// **** $.connection.hub.start().done(function() { **** callback
subscribeToReminderHub = function() {
reminderProxy = $.connection.reminderHub;
reminderProxy.server.subscribe().done(function() {
console.log('subscribed to reminder hub...');
});
reminderProxy.client.queryNotifications = function () {
// ***** notification chain - step 1
// ***** this never gets called
reminderProxy.server.notify();
}
reminderProxy.client.notify = function () {
// ***** notification chain - step 3
// ***** this never gets called
}
}
Starting this notification chain, I am invoking Notify() external from the hub like... note: I am passing userId which would relate back to the grouping
GlobalHost.ConnectionManager.GetHubContext<ReminderHub>().Clients.Group(userId).queryNotifications();
The most interesting part of this whole issue
is that if I don't introduce a second hub, and establish the group on NotificationHub's OnConnected and re-factor all logic back to the sole hub, this entire process works as expected. Somehow introducing a second hub and trying to establish a group outside of OnConnected is not working. Has anyone experienced this? Thoughts?
Even more interesting! (and awful!)
if I open up my browser dev tools and explicitly paste the following into my console
reminderProxy.server.notify()
I hit the breakpoint on ReminderHub's Notify()!
I continue through Clients.Caller.notify(); and the client function .notify() does not even get called in my client JS. I can't understand that at all. Bypassing all groping concerns, I can't even hit a client function now that I've introduced ReminderHub
You should make sure you prepare all of your client-side event handlers before starting the connection, otherwise SignalR will not fulfill them (at least in its current version).

How is this code being reached in SignalR

I have the following method in my Hub class:
public class NotificationHub : Hub
{
public void SendAll(string message)
{
if (1 == 0)
Clients.All.broadcastMessage(message); // this should be unreachable
}
}
Then, I (am trying) to call that method from my server-side code like so:
GlobalHost.ConnectionManager.GetHubContext<NotificationHub>()
.Clients.All.broadcastMessage("broadcastMessage was called");
The method is called, and everything works. But, I didn't want broadcastMessage() to be called since it should have been unreachable.
I read this from the documentation:
You don't instantiate the Hub class or call its methods from your own
code on the server; all that is done for you by the SignalR Hubs
pipeline. SignalR creates a new instance of your Hub class each time
it needs to handle a Hub operation such as when a client connects,
disconnects, or makes a method call to the server.
Ref. http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server
But it doesn't look like it uses my methods at all. It just looks like it calls its own methods and ignores mine. How can I call my own methods using SignalR?
When you do this:
GlobalHost.ConnectionManager.GetHubContext<NotificationHub>()
.Clients.All.broadcastMessage("broadcastMessage was called");
You're bypassing the method
public void SendAll(string message)
I am not sure why you'd expect the first method to be blocked. If you want your hub logic to work you have to work through the hub methods (example: public void SendAll(string message))
I like the solution presented here: https://stackoverflow.com/a/17897625/693272 if you want to call the hub method from outside.

SignalR Client in PartialView Not Receiving

I'm still getting the basics of SignalR down. I'd like to use it in different parts of my website, so I was thinking I could start the connection in the main layout, and add client methods in sub views.
As per this answer I have :
window.hub = $.connection.hub.start();
in my Layout.cshtml, and
window.hub.done(function () {
$.connection.notificationHub.server.joinScannerGroup(1);
alert("in sub view");
});
in my subview, which works. The hub is connected, and the method JoinScannerGroup() is called. However, if i try to add:
$.connection.notificationHub.client.scanReceived = function (text) {
alert("scan received");
};
anywhere in the javascript for the subview, it never gets called. Maybe I'm undertanding it wrong, but I'd like to connect on the mainpage, and then allow any subpages to receive client calls.
Also, if I move the $.connection.start() into the subview, it does work correctly. Am I understanding it wrong?
You need to either add all client hub methods before you call hub.start(), or you need to add at least one hub method before calling hub.start() (so SignalR will subscribe to the hub) and then add all other hub methods like this:
$.connection.notificationHub.on('scanReceived ', function (text) {
alert("scan received");
});
(also see documentation)

ServiceStack RedisMessageQueueClient strange behavior

My infrastructure:
Main - ServiceStack self hosted console app. 'Main' sends messages to MQ.
Background - ServiceStack self hosted console app. 'Background' receives messages from MQ.
Locally installed Redis
In 'Main' AppHost I configure Redis manager:
container.Register<IRedisClientsManager>(
new PooledRedisClientManager("localhost:6379"));
Then I run this code somewhere in service:
using (var client = new RedisMessageQueueClient(TryResolve<IRedisClientsManager>()))
{
client.Publish(new TestMessage { Value = "From ping" });
}
Everything works great and I can get message in my 'Background'. But problem comes when I wrap this code in class:
public class MessageQueuePublisher : IMessageQueuePublisher
{
public void Publish(object message)
{
using (var client = new RedisMessageQueueClient(
EndpointHost.AppHost.TryResolve<IRedisClientsManager>()))
{
client.Publish(message);
}
}
}
When I call MessageQueuePublisher.Publish method from the exactly same place where previous code was executed, it seems like it works correctly (no exceptions are thrown), but my message doesn't reach 'Background'.
Is this OK?
I found a solution. On my 'Background' I expect message with type TestMessage
mqService.RegisterHandler<TestMessage>(ServiceController.ExecuteMessage);
But when using MessageQueuePublisher.Publish message was of type object and went to the object queue and wasn't handled.
So to solve this problem Publish method should be generic:
public void Publish<T>(T message)
It doesn't change how method is called but code is not so good because if you look at it, it's not clear why generic is used. But at least it works.

Categories