SignalR on mono Group.Add not working in OnConnected - c#

I have a problem with SignalR running in MVC3 on mono.
I created the following Hub:
public class TestHub : Hub
{
public override Task OnConnected()
{
Trace.WriteLine(String.Format("Join Group: {0}", Context.User.Identity.Name), "SignalR.General");
Task addTask = Groups.Add(Context.ConnectionId, Context.User.Identity.Name);
return addTask;
}
public void JoinGroup(String group)
{
Trace.WriteLine(String.Format("Join Group2: {0}", Context.User.Identity.Name), "SignalR.General");
Groups.Add(Context.ConnectionId, group);
}
public void Echo(String group, String value)
{
Trace.WriteLine(String.Format("Echo: {0}, {1}", group, value), "SignalR.General");
Clients.Group(group).echo(value);
}
}
The main problem is the following:
A user is signed in via Forms auth and their name is correctly found in Context.User.Identity.Name.
When the client connects, OnConnected is called and the connection is added to a single user group with the same name as the username.
When I call the Echo function from the client with the group name being the username then I get the correct response (response is sent to client) IF I am hosting in IIS but I get no response when hosting in mono.
If on the other hand I call JoinGroup from the client first, it works in IIS and Apache on mono.
Sending a message to Clients.All works in IIS and mono as well but I need the message to arrive only for a specific user.
I tried different variations of this code but have no idea why the group registration should not work in OnConnected.
Any ideas would be greatly appreciated since I wasted 2 days on this problem already.

I dug a bit deeper into the problem and came up with this root cause:
When the client connects, it tries to connect first using Server Sent Events. This connection seems to work and is received by the server, firing OnConnected and correctly returning the group id to the client.
For some reason though, when running on apache2 and mono this response is never received and the "open" event of the EventSource object also does not fire. This means that after 3 seconds SignalR times out this connection request on the client and assumes that SSE is not working and starts a longPolling session.
This session connects successfully and can send and receive data but the connection id has been sent to the server already and OnConnected is not fired again so there is no way for my hub to get the second connection attempt and the client will not get the group id.
Starting the client directly with longPolling through $.connection.hub.start({ transport: "longPolling" }) fixes the problem at the expense of using longPolling only.

Related

gRPC : How to find a specific client in the stream gRPC

I want to create Client and Server through gRPC Server Streaming.
I would like to distinguish each client at this time. I looked into it, and I think we can choose using Uid to distinguish client or use peer to distinguish it.
What I am curious about is that when I want to send a response to only one specific client, I wonder if there is only a way to distinguish it through uid or peer after sending it to everyone. Can't the server send it to only one client from the beginning?
For example, suppose you created a server streaming grpc service called send(), request send() from multiple clients and wait for a response through stream. The server needs to respond to send() to a particular client, but I don't know how to generate only the send() service for this particular client.
One solution (but there might be better solutions) could be to pass a unique ID from the client to the server in the metadata, and have a centralized storage, like a singleton client manager class, for all connected clients on the server-side. This client manager class decides to who streams happen, and to who not.
For example, on the client-side the machine name is passed in the metadata when subscribing to the server stream as a identifier with the key client-id (though something like a GUID would probably more unique):
// client-side method that subscribes to the gRPC client
public async void Subscribe()
{
// subscribe to the server stream with a unique client id
_call = _serviceClient.SubscribeToServerStream(new Empty(), headers: new Metadata
{
new Metadata.Entry("client-id", Environment.MachineName)
});
// handle incoming messages from the server for this client
await HandleIncomingServerMessages(_call.ResponseStream);
}
The server-side code is kinda simplified, but I think it explains the idea. The client-id gets extracted from the metadata, and added to the (singleton) _clientManager class, which holds all connected clients and manages to who streams should happen. Now if you want to stream to this connected client, the centralized client manager can set (just as an example) this client's streaming property to active, and you can check in the outgoing stream if this client is active or not (with the id you extracted from the call context), and therefore stream messages or not.
// server-side method in the gRPC service that implements the stream
public override async Task SubscribeToServerStream(Empty request,
IServerStreamWriter<Message> responseStream,
ServerCallContext context)
{
// extract the unique client id
_clientId = context.RequestHeaders.FirstOrDefault(m => String.Equals(m.Key, "client-id", StringComparison.Ordinal));
// register it to the server-side client manager
_clientManager.AddNewConnectedClient(_clientId);
while(_isStreaming)
{
if(_clientManager.IsActiveClient(this._clientId))
{
// this client is set to active in the manager, therefore stream
}
else
{
// do nothing, since you currently do not want to stream to this connected client
}
}
}
I am pretty sure this will work, since I implemented something very similar not long ago, although it was kinda hacky (where multiple clients could connect, but only one of them could receive a stream at the same time, and there were some other conditions). But there might be better solutions to your problem than my answer, and I sadly do not have the time to write down and debug all the code to give you something that works out of the box.

SignalR: How to disconnect clients from server?

We currently have a Web Application, a SignalR Server and a RabbitMQ Server. The Web application receives events from SignalR based on the events received via RabbitMQ. Our Web Application has a fallback which, in case the connection to SignalR closes and can't be established, has a polling mechanism to try to keep the data updated.
The problem: If, instead of the SignalR server, the RabbitMQ is having issues, then, from the perspective of teh WebApplication it is still connected to SignalR server, which prevents it from polling data.
The possible solution: On the event of the RabbitMQ Server has issues connecting to SignalR, we would like to the SignalR server to actively refuse connections on a specified Hub and terminate all the connections it has.
Based on that i tried the following approach (inside the specified Hub), but no success:
public async override Task OnConnectedAsync()
{
var feature = Context.Features.Get<IConnectionHeartbeatFeature>();
feature.OnHeartbeat(state =>
{
try
{
if (rabbitMQWorker.IsConnected == false)
Context.Abort();
}
catch
{
}
}, Context.ConnectionId);
await base.OnConnectedAsync();
}
Even if it worked, I would still need to refuse any connections. Is this something feasable or am I heading towards the wrong direction?

Back-end for Location sharing between two clients

Want to create one back-end for location sharing between clients and back-end should be in .net-core and MS-SQL.
Simple approach is user1 send x and y co-ordinates and save to db every 2 second and user2 call the get api and get the x and y co-ordinates in every 2 second.
Issue - If 1 million user register then 1 million request per 2 sec will hit. Not good for servers and MS-SQL
Question - Is it possible to create web socket for every user which send their location and send the data to that socket in every 2 sec and when other user who want to see the location, merge that user with socket.
or any other approach???
You can use memory based database (Redis for example) to store the data (save and answer).
Web socket as transport, but even using web sockets you need to store the data somethere. To store in the application - not a good idea.
Maybe you can use SignalR - receive data from one client and send it to all connected clients. But there will be some difficulties with many connections.
You could use Asp.net SignalR, it can send messages to all connected clients simultaneously. For example, a chat room. Or, send messages to specific clients or groups of clients.
The SignalR Hubs API provides the following method to send messages to clients:
SendMessage sends a message to all connected clients, using Clients.All.
SendMessageToCaller sends a message back to the caller, using Clients.Caller.
SendMessageToGroups sends a message to all clients in the SignalR Users group.
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}
More details information about using it, check the following links:
Tutorial: Get started with ASP.NET Core SignalR
How can I make one to one chat system in Asp.Net.Core Mvc Signalr?.
Mapping SignalR Users to Connections

Using ASP.NET Core SignalR in .NET Console Client App to Receive Messages from Azure SignalR

I am learning SignalR but have hit a road block.
I have an Azure function which is successfully posting to Azure SignalR Hosted Service (configured in Serverless Mode)
I've been following this quickstart:
Quickstart: Create a chat room with Azure Functions and SignalR Service using C#
What I would like to achieve is to essentially receive messages from Servers into my client application. To prototype this I have created a Console Application.
I have added the following Nuget Packages
Microsoft.AspNetCore.SignalR.Client -Version 1.1.0
Microsoft.Azure.WebJobs.Extensions.SignalRService
All of the infrastructure seems to be working fine - I am basing that assumption on the face that I can run the demo website at the following address, point it at my local instance (or my instance hosted in Azure)
https://azure-samples.github.io/signalr-service-quickstart-serverless-chat/demo/chat-v2/
Messages posted by my AzureFunction publish directly into the Chat Window.
How can I get these messages to print to a console?
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
var connection = new HubConnectionBuilder().WithUrl("http://localhost:7071/api").Build();
connection.On<SignalRMessage>("newMessage", (message) =>
{
Console.WriteLine(message.Arguments);
});
connection.On("newMessage", (string server, string message) =>
{
Console.WriteLine($"Message from server {server}: {message}");
}
);
await connection.StartAsync();
Console.ReadKey();
}
}
}
I strongly suspect my problem is related the
connection.On<...>
statements. They never fire. The Connection.StartAsync() seems to work fine and establishes a connection to Azure SignalR instance.
Am I missing some fundamental point? I am just thrashing about at this point.
In Short - can someone please give me a pointer to RECEIVING and WRITING messages to my console window - much in the same way as messages are printed to the web browser in the web chat demo (see second link above).
The messages are simple broadcast messages that I want to go to all connected clients.
Nearly all examples are in Javascript.
Thanks in Advance.
Once I discovered how to add logging to SignalR I could see that it could not resolve the type which was being sent.
It worked once I changed my connection.On to the correct type such as
connection.On<CorrectType>("newMessage", (message) =>
{
Console.WriteLine(message.stringproperty);
});
My thinking was mislead by looking at the article Azure Functions development and configuration with Azure SignalR Service
Where they "seemingly" (atleast in my mind) add a message of type "SignalRMessage" to SignalR. When in fact they were adding a message type of "CorrectType"
CorrectType message
signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to these user IDs
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
I managed to get around it by passing in 'object' when calling 'connection.On' instead of having to create the CorrectType class and let .net figure out of how the object is going to look like.
It was indeed the resolving of the type that prevented the .On from firing on a windows client.

How to send updates from server to clients?

I am building a c#/wpf project.
It's architecture is this:
A console application which will be on a virtual machine (or my home computer) that will be the server side.
A wpf application that will be the client app.
Now my problem is this - I want the server to be able to send changes to the clients. If for example I have a change for client ABC, I want the server to know how to call a service on the clients computer.
The problem is, that I don't know how the server will call the clients.
A small example in case I didn't explain it well:
The server is on computer 1, and there are two clients, on computers 2 and 3.
Client 2 has a Toyota car and client 3 has a BMW car.
The server on computer 1 wants to tell client 2 that it has a new car, an Avenger.
How do I keep track and call services on the clients?
I thought of saving their ip address (from calling ipconfig from the cmd) in the DB - but isn't that based on the WI-FI/network they are connected to?
Thanks for any help!
You could try implementing SignalR. It is a great library that uses web sockets to push data to clients.
Edit:
SignalR can help you solve your problem by allowing you to set up Hubs on your console app (server) that WPF application (clients) can connect to. When the clients start up you will register them with a specified Hub. When something changes on the server, you can push from the server Hub to the client. The client will receive the information from the server and allow you to handle it as you see fit.
Rough mockup of some code:
namepsace Server{}
public class YourHub : Hub {
public void SomeHubMethod(string userName) {
//clientMethodToCall is a method in the WPF application that
//will be called. Client needs to be registered to hub first.
Clients.User(userName).clientMethodToCall("This is a test.");
//One issue you may face is mapping client connections.
//There are a couple different ways/methodologies to do this.
//Just figure what will work best for you.
}
}
}
namespace Client{
public class HubService{
public IHubProxy CreateHubProxy(){
var hubConnection = new HubConnection("http://serverAddress:serverPort/");
IHubProxy yourHubProxy = hubConnection.CreateHubProxy("YourHub");
return yourHubProxy;
}
}
}
Then in your WPF window:
var hubService = new HubService();
var yourHubProxy = hubService.CreateHubProxy();
yourHubProxy.Start().Wait();
yourHubProxy.On("clientMethodToCall", () => DoSometingWithServerData());
You need to create some kind of subscription model for the clients to the server to handle a Publish-Subscribe channel (see http://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html). The basic architecture is this:
Client sends a request to the messaging channel to register itself as a subscriber to a certain kind of message/event/etc.
Server sends messages to the channel to be delivered to subscribers to that message.
There are many ways to handle this. You could use some of the Azure services (like Event hub, or Topic) if you don't want to reinvent the wheel here. You could also have your server application track all of these things (updates to IP addresses, updates to subscription interest, making sure that messages don't get sent more than once; taking care of message durability [making sure messages get delivered even if the client is offline when the message gets created]).
In general, whatever solution you choose is plagued with a common problem - clients hide behind firewalls and have dynamic IP addresses. This makes it difficult (I've heard of technologies claiming to overcome this but haven't seen any in action) for a server to push to a client.
In reality, the client talks and the server listens and response. However, you can use this approach to simulate a push by;
1. polling (the client periodically asks for information)
2. long polling (the client asks for information and the server holds onto the request until information arrives or a timeout occurs)
3. sockets (the client requests server connection that is used for bi-directional communication for a period of time).
Knowing those terms, your next choice is to write your own or use a third-party service (azure, amazon, other) to deliver messages for you. I personally like long polling because it is easy to implement. In my application, I have the following setup.
A web API server on Azure with and endpoint that listens for message requests
A simple loop inside the server code that checks the database for new messages every 100ms.
A client that calls the API, handling the response.
As mentioned, there are many ways to do this. In your particular case, one way would be as follows.
Client A calls server API to listen for message
Server holds onto call, waiting for new message entry in database
Client B calls server API to post new message
Server saves message to database
Server instance from step 2 sees new message
Server returns message to Client A.
Also, the message doesn't have to be stored in a database - it just depends on your needs.
Sounds like you want to track users à la https://www.simple-talk.com/dotnet/asp.net/tracking-online-users-with-signalr/ , but in a desktop app in the sense of http://www.codeproject.com/Articles/804770/Implementing-SignalR-in-Desktop-Applications or damienbod.wordpress.com/2013/11/20/signalr-a-complete-wpf-client-using-mvvm/ .

Categories

Resources