Client side SignalR on reconnect doesn't receive messages - javascript

I have SignalR Hub running on a server being run as a service. This server for whatever reason, may stop due to power loss, being updated (we use Octopus to automatically update), or whatever.
If I have a user connected to this service sending messages to the server, and then in turn, forwarding this message to a sensor. The sensor then returns a message to the server, and gets forwarded on to the client webapp.
This works fine on the first connection. If the service is stopped and restarted before the reconnect times out, I get my reconnect with the same connection id, and I can send messages. I can't however, receive messages.
If I refresh the page, I will get them as this creates a new connection. The same code is called whether it is OnConnect() or OnReconnect().
Example
public override Task OnConnected()
{
EstablishConnection("Connect");
return base.OnConnected();
}
public override Task OnReconnected()
{
// Remove old Connection Id to receive messages on reconnect
ConnectionsHandler.Instance.RemoveTerminalClient(Context.ConnectionId);
EstablishConnection("Reconnect");
// This block is to tell the user that connection has been reconnected. This message shows on the webapp so connection has been restored.
var hubContext = GlobalHost.ConnectionManager.GetHubContext<TerminalHub>();
var message = TerminalMessageColorer.ColorMessageLime(String.Format("Connection has been re-established!"));
hubContext.Clients.Client(Context.ConnectionId).TerminalEcho(message);
return base.OnReconnected();
}
private void EstablishConnection(string conType)
{
ConnectionsHandler.Instance.AddTerminalClient(Context.ConnectionId, "null");
var terminal = ConnectionsHandler.Instance.GetTerminalClient(Context.ConnectionId);
#if DEBUG
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("{0}\t{1}\t...{2}", terminal, Context.ConnectionId, conType);
#endif
}

I found what the problem was. I needed to wrap a $timeout in the client side code as there seems to be a little delay in the C# code. I stumbled across this from the chrome dev tools once I was debugging the initializer, thinking something had to be wrong that the user id wasn't being passed. Once I added a breakpoint at the call to initialize, it would go there so that work, but continuing from there I would receive messages afterwards. Without the breakpoint I wouldn't. So I added the $timeout around the call.
$timeout(function () {
initializeTerminal();
}, 100);

does not work - due to jquery version
<script src="~/Scripts/jquery-3.6.0/jquery-3.6.0.min.js"></script>
<script src="~/Scripts/signalr/jquery.signalR-2.4.2.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Areas/Noc/NocDOCSIS/Scripts/script.noc.light.js"></script>
works - due to Jquery version
<script src="~/Scripts/jquery-2.2.4/jquery-2.2.4.min.js"></script>
<script src="~/Scripts/signalr/jquery.signalR-2.4.2.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Areas/Noc/NocDOCSIS/Scripts/script.noc.light.js"></script>

Related

SignalR multi user live chat desynchronisation

I have a live chat in which multiple people would be connected simultaneously. All public messaging works fine, but sometimes private messaging to a specific id doesn't work. I believe i narrowed it down to when people disconnected and reconnected that they connected to a different instance (perhaps IIS had recycled and started a new hub).
I thought I had fixed it, but I haven't and now I'm here because I'm stuck. What I thought would fix it was changing the connection variable within the startChat() function to refresh it with the correct information.
This is a cut down version of the code, as I didnt thing the rest would be necesary.
Issue is that when connected to signalR recipients of a message directly to them doean't come through, even though the chat Id it's being sent to it correct. Possible hub/socket mismatch?
var chat = $.connection.chatHub;
$(document).ready(function () {
// Start the chat connection.
startChat();
//restart chat if disconnected
$.connection.hub.disconnected(function () {
setTimeout(startChat(), 5000);
});
$.connection.hub.error(function (error) {
$('#messagebar').html('Chat ' + error + '. If this message doesn\'t go away, refresh your page.');
});
chat.client.addToChat = function (response) {
$('#chat-' + response.Type).prepend(response.Message);
};
});
function startChat() {
chat = $.connection.chatHub;
$.connection.hub.start().done(function () {
//get recent chat from db and insert to page.
//also saves user's chat id to their user for lookup when private messaging
$.ajax({
method: 'POST',
url: '/api/Chat/SetupChat/'
});
$('#messagebar').html('Connected to chat.');
});
}
Any help appreciated, Thanks.
Not sure exactly how your message is going missing, but you should send messages to a User instead of by connection id. This way you be able to identify on the server that a User has at least one connection, and send messages to all connections for that User. if a given connection id is no longer valid, but another one is (because the client has refreshed the page for example) the message wont be lost.
From the docs https://learn.microsoft.com/en-us/aspnet/core/signalr/groups:
The user identifier for a connection can be accessed by the
Context.UserIdentifier property in the hub.
public Task SendPrivateMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", message);
}
Not sure how this relates exactly to your server code, but you could add it if you're unclear how to progress.

How to allow JavaScript to receive a message sent to a specific user (or self) using Signal R?

I previously asked this question but it was closed for duplication owing to this thread (SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*) - but this doesn't show the JavaScript as made clear in my title.
I have a WebForm application in ASP.Net that uses SignalR to push live data to the user logged in. The setup works perfectly, but realised I am broadcasting messages to all clients, which whilst it doesn't cause the wrong data to displayed to the logged in user, does cause the JavaScript function to get called for all users when just one has a data push.
I have amended the Hub code to broadcast to a specific user (User) and provided the User ID, and I have also tried Client with a Connection ID. Both fire off fine in the codebehind, but the javascript will not update the front end.
I believe it's because the JavaScript has not been modified to listen for a message sent to the user, but I'm not sure how I need to adapt the code to allow the message to be received.
The 2 tried lines in Hub here:
context.Clients.User(Me.Context.User.Identity.GetUserId()).ReceiveNotification(notifCount)
context.Clients.Client(Me.Context.ConnectionId).ReceiveNotification(notifCount)
JavaScript/jQuery function for the SignalR message here:
$(function () {
var nf = $.connection.notificationHub;
nf.client.receiveNotification = function (notifCount) {
// Update data
}
$.connection.hub.start().done(function () {
nf.server.sendNotifications();
}).fail(function (e) {
alert(e);
});
//$.connection.hub.start();
});
For calling back to the client (or self) you should use:
Clients.Caller.addContosoChatMessageToPage(name, message);
And for calling users you should use:
Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
Reference - docs

How to close connection to sse flux with javascript-client?

I build a server-send-event endpoint with spring webflux. My javascript app subscribes to that endpoint and receives the published events correctly. BUT when call
EventSource.close() it seems the publisher is not informed that the client closed the connection.
Hence, the publisher is still thinking that there is a subscription, and publishes events to that.
The event stream is potentially never ending, so I never get a complete signal on the publisher side, which means the client has to close the connection.
I think that this happens because the close() call can't reach the underlying flux.
Do you have any solution or workaround for this problem?
#GetMapping(value = INSTANCE_UPDATE_ENDPOINT + "/{dealId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<InstanceUpdatedQuery>> getInstanceUpdateEventStreamForDeal(#PathVariable final String dealId) {
return this.instanceUpdatedApplicationEventFlux
.filter(instanceUpdatedEvent -> instanceUpdatedEvent.getDealId().equals(dealId))
.map(instanceUpdatedEvent -> ServerSentEvent.<InstanceUpdatedQuery>builder()
.id(instanceUpdatedEvent.getId())
.retry(Duration.ofSeconds(1))
.data(new InstanceUpdatedQuery(instanceUpdatedEvent))
.build())
}
You should add a handler to catch the doOnNext(Signal) and then detect the close signal and respond to it

Chromecast not receiving messages over CastMessageBus

I'm trying to initialise the cast message bus on my
receiver application. I am using the
CastReferencePlayer Sample I'm able to initialise the channel at launch, and
receive one message.
But after media is playing, I'm not able to receive
any additional messages over the channel using the same send methods on
the sender. My sender is an iOS Device
Where is the best place to init the message bus to ensure that the channel
is open to receive messages throughout the life of the app?
I am currently starting the channel
in sampleplayer.CastPlayer.prototype.start = function(). But I'm not sure if this is the correct place to start the message bus as I'm only receiving the message once at startup (i.e. before the media starts to play).
sampleplayer.CastPlayer.prototype.start = function() {
var messageBus = this.receiverManager_.getCastMessageBus(
"urn:x-cast:com.myTestChannel.app",
cast.receiver.CastMessageBus.MessageType.JSON);
messageBus.onMessage = function(event) {
var message = event.data;
if (message.testID) {
console.log("Message received");
} else if (message.someKey) {
console.log("pause message received");
}
};
this.receiverManager_.start();
};
I have tried to place the channel creation code in a window.onload function inside my player.js but this reacts the same as before (i.e. initial message only).
Does anyone have any idea where I should be placing the code to init the channel?
Any help would be appreciated.
EDIT: I have done some more investigating and discovered that it is just my iOS sender that will not send multiple messages to the receiver. I have tried both android and chrome and multiple messages work.
Is anyone aware of problems with sending messages from an iOS sender.
The problem with my code was in my sender.
The sendTextMessage method was not firing the after the first instance due to the my reference to the cast channel being destroyed.

Implementing notification/alert popup on job completion in Ruby on Rails

I am implementing background processing jobs in Rails using 'Sidekiq' gem which are run when a user clicks on a button. Since the jobs are run asynchronously, rails replies back instantly.
I want to add in a functionality or a callback to trigger a JavaScript popup to display that the job processing has finished.
Controller Snippet:
def exec_job
JobWorker.perform_async(#job_id,#user_id)
respond_to do |wants|
wants.html { }
wants.js { render 'summary.js.haml' }
end
end
Edit 1:
I am storing the 'user_id' to keep a track of the user who triggered the job. So that I can relate the popup to this user.
Edit 2:
The 'perform' method of Sidekiq does some database manipulation(Update mostly) and log creation, which takes time.
Currently, the user gets to know about the status when he refreshes the page later.
Edit 3(Solution Attempt 1):
I tried implementing 'Faye Push Notification' by using subscribe after successful login of user(with channel as user_id).
On the server side, when the job completes execution, I created another client to publish a message to the same channel (Using faye reference documents).
It is working fine on my desktop, i.e., I can see an alert popup prompting that the job has completed. But when I try testing using another machine on my local network, the alert is not prompted.
Client Side Script:
(function() {
var faye = new Faye.Client('http://Server_IP:9292/faye');
var public_subscription = faye.subscribe("/users/#{current_user.id}", function(data) {
alert(data);
});
});
Server Side Code:
EM.run {
client = Faye::Client.new('http://localhost:9292/faye')
publication = client.publish("/users/#{user.id}", 'Execution completed!')
publication.callback do
logger.info("Message sent to channel '/users/#{user.id}'")
end
publication.errback do |error|
logger.info('There was a problem: ' + error.message)
end
}
Rails 4 introduced the ActionController::Live module. This allows for pushing SSEs to the browser. If you want to implement this based on a database update you will need to look into configuring Postgresql to LISTEN and NOTIFY.
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello world\n"
sleep 1
}
ensure
response.stream.close
end
end
Here is a good article on it: http://ngauthier.com/2013/02/rails-4-sse-notify-listen.html
Thanks fmendez.
I looked at the suggestions given by other users and finally, I have implemented a 'Faye' based push notification by:
Subscribing the user on successful login to a channel created using 'user_id'
Replying to this channel from server side after job completion (By fetching user_id)
For better understanding(so that it may be helpful for others), check the edit to the question.

Categories