I'm using signalr on my site. I have just one class that inherits from hub, and several aspx forms with client code that have client functions called by the hub.
when a client connects to the hub I add it to a collection with a clientID, when a client disconnect I remove it from that collection. Just to know if any client is connected. As long as at least one client is connected, a timer call some methods that fill a data repository on the server that is propagated to clients.
then I usually do before calling customers things like this:
if (users.Count() > 0)
{
this.Clients.All.UpdateData(JsonConvert.SerializeObject(someData));
...
}
all this works fine. But what I need and can not find how to do it in the hub, is to know what clients are connected to know if I should create the data repository for those clients and avoid making unnecessary database queries.
eg.
myHub.cs
Timer_tick()
{
...
//Collect data to clients Type 1
...
Clients.All.UpdateDataType1(jsonData);
...
//Collect data to clients Type 2
...
Clients.All.UpdateDataType2(jsonData);
...
//Collect data to clients Type 3
...
Clients.All.UpdateDataType3(jsonData);
ClientType1.aspx.js (2 clients connected)
hub.client.UpdateDataType1= function (jsonData) {...);
ClientType2.aspx.js (0 clients connected)
hub.client.UpdateDataType2= function (jsonData) {...);
ClientType2.aspx.js (0 clients connected)
hub.client.UpdateDataType3= function (jsonData) {...);
I know when I call clients 2 and 3 function I not need worry about whether clients are connected. but I need to avoid to obtain data that I will not use. The goal is just obtain data I will use to the connected clients.
All I can do is see if there are clients, but not if there are clients of an specic js
Is there any way to know this?
You can use groups for that, and you can write JavaScript code reusable across pages. Before starting the connection, your JavaScript could do this:
$.connection.hub.qs = { referer: document.location.pathname };
This way you store the calling page in a query string key. Of course you can store whatever other information you think it's useful for your goal. Then you can have this in your hub:
public override Task OnConnected()
{
var referer = Context.Request.QueryString["referer"];
Groups.Add(Context.ConnectionId, referer);
}
This way each client will notify which library is using, and with the groups you created against the client information you can easily target them by client type. No need to store anything in memory, which has always a lot of drawbacks.
You can check that link to map users to conection. If you take a look at the in-memory solution, you will be able to retrieve a list of connected users.
Related
In our system, we have one C++ component acting as a Thrift Server, and one .netCore/C# component as a client.
So far, I was managing a single connection, so using a singleton to create my ThriftPushClientWrapper which implements TBaseClient. (via the generated object from the thrift interface)
.AddSingleton<IThriftPushClientWrapper>(sp =>
{
var localIpAddress = IPAddress.Parse(serverIp);
var transport = new TSocketTransport(localIpAddress, dataPort);
var protocol = new TBinaryProtocol(transport);
return new ThriftPushClientWrapper(protocol);
});
(so far using 0.13 version of the Thrift library, need to update to 0.14.1 soon, but wonder if the server part must be updated too/first).
This is working great.
Now, I want multiple clients that can connect to the server simultaneously, all on the same ip:port
So I am starting a ClientFactory, but wonder how to deal with the creation of the client.
To be more precise, the server part is configured for 5 threads, so I need 5 clients.
One simple approach would be to create a new client each time, but probably inefficient.
A better approach is to have a collection of 5 clients, and using the next available free one.
So I started with the following factory, where I should get the index from outside.
private readonly ConcurrentDictionary<int, IThriftPushClientWrapper> _clientDict;
public IThriftPushClientWrapper GetNextAvailablePushClient(int index)
{
IThriftPushClientWrapper client;
if (_clientDict.ContainsKey(index))
{
if (_clientDict.TryGetValue(index, out client) && client != null)
return client;
else // error handling
}
// add new client for the expecting index
client = CreateNewPushClient();
_clientDict.TryAdd(index, client);
return client;
}
private IThriftPushClientWrapper CreateNewPushClient()
{
var localIpAddress = IPAddress.Parse(serverIp);
var transport = new TSocketTransport(localIpAddress, dataPort);
var protocol = new TBinaryProtocol(transport);
return new ThriftPushClientWrapper(protocol);
}
My next issue it to determine how to set the index from outside.
I started with a SemaphoreSlim(5,5) using the semaphore.CurrentCount as index, but probably not the best idea. Also tried with a rolling index from 0 to 5. But apparently, a CancellationToken is used to cancel further procceesing. Not sure the root cause yet.
Is it possible to determine whether a TBaseClient is currently busy or available?
What is the recommended strategy to deal with a pool of clients?
The easiest solution to solve this is to do it right. If you are going to use some resource from a pool of resources, either get it off the pool, or mark it used in some suitable way for that time.
It's notable that the question has nothing to do with Thrift in particular. You are trying to solve a weak resource management approach by trying to leverage other peoples code that was never intended to work in such a context.
Regarding how to implement object pooling, this other question can provide further advice. Also keep in mind that especially on Windows platforms not all system resources can be shared freely across threads.
Suppose, i want to keep some non-serializable data for my applciation, which the user can access and interact with across the application. So for example, a connected device (bluetooth, wlan), a media stream, something of this sort. Im wondering where to put such data, when using the Redux paradigm?
Pseudocode:
A normal lifecycle of a subscription might look like this:
Subscription connection = instance.connect(device, request).listen();
...
connection.send(data);
...
data = connection.read();
...
connection.unsubscribe();
the app store might look like this:
Store:
{
username: '',
friends:[], <--- Pulled from the server with async middleware
connections:[], <--- Connections data here ???
}
But I would go on and say, that such a device subscription or media stream is not serializable and therefore it would be not proper, to hold them in the Redux Appstate.
And i'm aware, that for example for server request, one is encouraged to use middleware. So for friends of the user, one would write asynchronous actions to pull the friends user names from the server at the login of a user. But a device id in the state wont do, since one has to actually interact with the device connection stream in the app.
So would writing a seperate middleware be an acceptable way to hold on to such data? What is the common practice herefore?
I've been looking for a good way to do, but haven't found anything that doesn't seem hacky. I want to signal the client without going through the database and a subscription. For example, in a game I want to send a message to the client to display "Player 1 almost scores!". I don't care about this information in the long run, so I don't want to push it to the DB. I guess I could just set up another socket.io, but I'd rather not have to manage a second connection if there is a good way to go it within meteor. Thanks! (BTW, have looked at Meteor Streams, but it appears to have gone inactive)
You know that Meteor provides real-time communication from the server to clients through Publish and Subscribe mechanism, which is typically used to send your MongoDB data and later modifications.
You would like a similar push system but without having to record some data into your MongoDB.
It is totally possible re-using the Meteor Pub/Sub system but without the database part: while with Meteor.publish you typically return a Collection Cursor, hence data from your DB, you can also use its low-level API to send arbitrary real-time information:
Alternatively, a publish function can directly control its published record set by calling the functions added (to add a new document to the published record set), changed (to change or clear some fields on a document already in the published record set), and removed (to remove documents from the published record set). […]
Simply do not return anything, use the above mentioned methods and do not forget calling this.ready() by the end of your publish function.
See also the Guide about Custom publications
// SERVER
const customCollectionName = 'collection-name';
let sender; // <== we will keep a reference to the publisher
Meteor.publish('custom-publication', function() {
sender = this;
this.ready();
this.onStop(() => {
// Called when a Client stops its Subscription
});
});
// Later on…
// ==> Send a "new document" as a new signal message
sender.added(customCollectionName, 'someId', {
// "new document"
field: 'values2'
});
// CLIENT
const signalsCollectionName = 'collection-name'; // Must match what is used in Server
const Signals = new Mongo.Collection(signalsCollectionName);
Meteor.subscribe('custom-publication'); // As usual, must match what is used in Server
// Then use the Collection low-level API
// to listen to changes and act accordingly
// https://docs.meteor.com/api/collections.html#Mongo-Cursor-observe
const allSignalsCursor = Signals.find();
allSignalsCursor.observe({
added: (newDocument) => {
// Do your stuff with the received document.
}
});
Then how and when you use sender.added() is totally up to you.
Note: keep in mind that it will send data individually to a Client (each Client has their own Server session)
If you want to broadcast messages to several Clients simultaneously, then the easiest way is to use your MongoDB as the glue between your Server sessions. If you do not care about actual persistence, then simply re-use the same document over and over and listen to changes instead of additions in your Client Collection Cursor observer.
It's completly fine to use the database for such a task.
Maybe create a collection of "Streams" where you store the intended receiver and the message, the client subscribe to his stream and watches any changes on it.
You can then delete the stream from the database after the client is done with it.
This is a lot easier than reinventing the wheel and writing everything from scratch.
I have a concurrent dictionary List on my controller that stores list of online users. For example when Client A and Client B connects there are 2 online clients present on the list, but when i disconnect B and then re- connect again it must still show 2 online clients but in my case, only Client B detected as online user(disconnected then reconnected). I am using IIS server 7.5.. Please help me with this, do i need to use a database rather than dictionary? I think it resets the dictionary to zero if one user disconnects and reconnects again.... :( Below is my hub class code
public class Chat : Hub
{
//add online client
private static ConcurrentDictionary<string, string> personLists
= new ConcurrentDictionary<string, string>();
public void Connect(string Username, int ID)
{
string id = Context.ConnectionId;
if (!personLists.ContainsKey(Username))
{
personLists.TryAdd(Username, id);
Clients.Caller.viewOnlinePersons(personLists.Where(p => p.Key != Username));
Clients.Others.enters(Username);
}
else
{
string notif = "user: "+Username+" is already used";
Clients.Caller.onUse(notif);
}
The concurrent dictionary should work just fine. It'd be good if you could post some code, but if you go this route with concurrent dictionary, make sure it's static (the Hub classes get created and destroyed per signal) and I think it'd be better suited placed on the hub itself and private (and of course static, again). You can also use Dependency Injection with SignalR which will be a lot cleaner.
You'll only need database as a backplane if you plan on running the application on multiple servers where of course a memory of a single server is not accessible by the other servers and a duplicate dictionary would be created for each server, so in that case you need to take the storage and move it up a bit in the architecture to be accessible by all the servers.
To set the right context, let me explain the problem. Till RC1, we used to implement GenerateConnectionIdPrefix() to prefix user Id to the connection Id. Then we could retrieve user id from the connection string anytime we need.
With RC2, we now cannot inherit IConnectionIdPrefixGenerator & implement GenerateConnectionIdPrefix anymore. So I was wondering what are other avenues available to set any property on the hub with our data, that persists throughout the lifetime of the connection.
Going through documentation, I realized setting query strings is one way, but that would mean we need to set it for every call. Setting a round trip state might be another option, but it looks like even that is persistent for a single round-trip and not entire lifetime.
So my end goal is set to property once at start on SignalR connection that can be used throughout the connection lifetime.
If there is nothing available now, are there any plans to add support to achieve something similar in next version?
[Update]
As suggested below, I tried to set a state Clients.Caller.Userid in the OnConnected method, then tried to access it in the subsequent call, I found that its null. Both calls are from same connection Id.
Look at the "Round-tripping state between client and server" section on https://github.com/SignalR/SignalR/wiki/Hubs.
Basically you can read and write from dynamic properties on Clients.Caller in Hub methods such as OnConnected or anything invoked by a client. Ex:
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
namespace StateDemo
{
public class MyHub : Hub
{
public override Task OnConnected()
{
Clients.Caller.UserId = Context.User.Identity.Name;
Clients.Caller.initialized();
return base.OnConnected();
}
public void Send(string data)
{
// Access the id property set from the client.
string id = Clients.Caller.UserId;
// ...
}
}
}
State that is stored this way will be persisted for the lifetime of the connection.
If you want to learn how to access this state using the SignalR JS client look at the "Round-tripping state" section of https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client-Hubs.
There are other ways to keep track of users without IConnectionIdPrefixGenerator discussed in the following SO answer: SignalR 1.0 beta connection factory