So I want to send a message to a specific client via SignalR. That client is not Clients.Caller - currently I can only identify it by let's call it "ID", a property in the context: this.Context.Items["ID"]
So to find a client by its ID, how do I...access all clients or contexts? Or should I save this ID in a different way? This is not the connection ID, it is an ID that maps to something in the database.
Basically I'd like to go Clients.Single(c => c.Items["ID"] == specificId).SendMessage(msg);
In the OnConnectedAsync method in your hub, you can group a user by their "ID" each time they connect.
E.G:
public async override Task OnConnectedAsync()
{
var currentUserId = /*get your current users id here*/
await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{currentUserId}");
await base.OnConnectedAsync();
}
Then when you need to send that person a message, you can just broadcast it to their 'personal' group. (One person may have multiple connections, so the group approach work perfectly for us).
E.G:
Clients.Group($"user-{userId}").SendAsync("UserSpecificMessage", message);
When users disconnect, SignalR will handle the removing of connections from the groups automatically.
In using this approach, we do not have to unnecessarily broadcast a message to every single client with the intention of only one client filtering out the message based on the id.
You can send the ID down to the clients using context.Clients.User(...) or context.Clients.All(). Then in JavaScript, read the ID and compare it to what's on the page. If it's a match, carry out some action; else ignore.
As an example, let's say your app's processing a specific record on an edit screen. The record ID is on the page as a form field. You send a SignalR message down from C# with the ID. You do a comparison in JavaScript between the ID and the form field value; if they match, you display a toaster message, perform other processing, etc.
Related
Been mulling and working on this last 48hrs and just not getting anywhere. So let me jump right to the issue.
I'm working with an API.
I send a request to the API as such
http://localhost:8080/Execute....etc..etc <--- a webrequest
This in turn returns an "ID".
The API also sends via a UDP port an Order# to that ID
-So the API receives the Request and responds with an ID.
Then
-API sends an Order# it associates with that ID on a UDP port AFTER issuing the ID
However, there is no set Timing to this, it's as it comes.
Now on the other end of this is the BackEnd Server.
This is constantly sending [Msg Updates] via UDP.
So the sequence can go something like this.
I have my UDP listener configured for Port 1234.
I have my UDP Lister configured for Port 2222 <-- the back end server sending msgs
I execute via the API http://localhost:8080/execut...etc
(String response from API server) <--- receive ID
(ms time goes by)
(UDP Port 1234) <--- Order#
[Port 2222]<--- Receives [Msg update] with Order details.
Problem is the [Msg Update] can and does come in faster than the Order# associated with the [MsgUpdate].
Currently My solution is as follows but feels very cluncky and well (You know that feeling when your code/design makes ya feel a little dirty) well that's what I'm going through now. Here's my current way of approaching the problem.
The associations are as follows.
1 Unique ID ----> 1 OrderNumber.
Multiple OrderStates ---> 1 OrderNumber (accepted, cancelled, ....etc)
the Incoming Msg from the Server has Only the OrderNumber and no (ID) tag that you get from the API.
So here's how I'm handling it....or trying to.
And please if you guys can see a better way to do this I'm all for it I'll toss this entire garbage solution out the window in a heartbeat.
Is there a much cleaner more elegant way to accomplish this?
As you can see you can receive [Msg Updates] faster than you receive the OrderNumber, AND each Incoming [MsgUpdate] can have different states for the Order...
For example: #1000 accepted, #1000 cancelled, #1001 Accepted, #1001 Rejected [Not enough BP], #1002 Cancelled, #1010 Partially Filled
But takeaway is that there is nothing unique about the incoming orders except the ordernumber (#1001) and that is only associated with the ID from the API you get after the execute, then have to wait for the UDP response from the API to get the orderNumber (#1001) and only then you know what that [1001] is YOUR msg. ugh......
Anyway my pseudo code to give a basic premise of what I did to attempt to address this.
[Client Class]
private IOrderNumberListener ordListener;
private IBackendListener _blistener;
public Client(IOrdernUmberListener ordListen, IBackendListener backListen)
{
ordListener = ordListen;
_blistener = backListen;
ordListener += IncomingOrderNo;
_blistener += ReceiveIncomingMsgsFromServer;
}
public string IncomingOrderNo(string exeord)
{
//Deal with it
}
/// This method will recieve data for everything including things that isn't related to the ID
private void ReceiveIncomingMsgsFromServer(object sen`enter code here`der, data)
{
//Process Incoming data
//So in here is a check for the OrderNumber which doesn't have the ID associated with it. Just
the OrderNumber with details.
if(data.ordernumber == this.Ordernumber) <--- this I feel isn't all that great.
// QUERY the _blistener.GetOrderDetails(this.OrderNumber);
//Deal with things here.
}
public string executeOrder(string url)
{
webrequest..(url)...etc
}
[Back End Server Class]
//UDP Listener Configuration stuff here
......
public event EventHandler<Msg> Update;
private Dictionary<string, msgData> OrderDb = new Dictionary.....etc
private void sendUpdate(data)
{
//raise event method etc..
}
private process packet(byte[] data)
{
/// process data
sendUpdate(processedData);
}
public msgData GetOrderDetails(string ordernumber)
{
//Check db return orderdetails.
}
I use websocket-sharp ( https://github.com/sta/websocket-sharp ) (serveur side only).
I can have many clients applications connected to my websocket server. Each client send an ID (let's call it CLIENT_ID).
I want to know which websocket session is related to CLIENT_ID.
IWebSocketSession expose ID (let's call it SESSION_ID).
What I have tried:
Firstly I have a storage class which store for each SESSION_ID his CLIENT_ID (a simple dictionary).
When I receive a message from a client application, i store SESSION_ID and CLIENT_ID in my dictionary.
So when i want to send a message to all sessions having CLIENT_ID == XXX i can use this dictionary. This works fine ...
BUT sessions are only temporary. A client can use multiple sessions. So for a single client, I will soon have many inputs in my dictionary. When i send a message to all sessions in my dictionnary having CLIENT_ID == XXX, I will send the same message to the same client multiple time.
My question is : How to register unique clients with websocket-sharp ? Which property should I use ?
Edit : Even with a unique client, every 20s it changes ID of the session. It's probably a new session created for ping.
Ok as I find nothing, I kept SESSION_ID but when i register a new item in my dictionary (SESSION_ID => CLIENT_ID) I have to manually delete all "old sessions".
This can be done by checking if session.State == WebSocketState.Open.
It's not perfect but it works.
I am working on a crm where Jobs are assigned to employees automatically and when a job is created application looks for the employee with maximum idle time.I am able to broad cast notification to all the employees but I want to know how to send notification to the employee for which job is assigned.Just like facebook each user recieves his notifications only.I am using this statement in hub class
Clients.All.broadcastMessage( message);
You are currently broadcasting your messages to everyone I suggest you useClients.User(userid)
Get the current logged in user string userName = HttpContext.Current.User.Identity.Name;
And then to send out notification to the particular user you can have something like.
Clients.User(userName).broadcastMessage(message);
You have to recognize user depending on connectionId.
Here is a good blog post describing this:
http://www.tugberkugurlu.com/archive/mapping-asp-net-signalr-connections-to-real-application-users
I was struggling with this just 3 days ago, and finally got it working.
Suppose you have a Hub class called YourHub, your javascript code should look like this
$(function () {
var yourHub = $.connection.YourHub;
yourHub.client.OnMessage = function ( message) {
console.log(message):
};
});
Then you need to implement IUserIdProvider on the server, which receives a IRequest (there should be a cookie or something for you to identify the person), and implement the method
public string GetUserId(IRequest request)
Then as Izzy told you use Clients.User(userId).OnMessage(message)
where userId is the id you return on the GetUserId and OnMessage is the method in your javascript file.
Be carefull to use Clients.User(userId) not Clients.Client(userId)
I'm playing around with SignalR and was wandering how SignalR creates a connectionID is this based on my IP adress, the device I connect with or something else? And wether it is possible to set this ID manually. Let say I have a database with users which I give a unique number, can I use that number for the connectionID?
Kind regards
The SignalR connection Id is a generated guid. If you'd like to target users in a more meaningful way with your data, I found it useful to pass something with your client connection, such as a user Id and some other data from your database, along to the signalr hub and craft up a group based on what you provide. It should be a 1:1 mapping if you're trying to isolate users with your own identifiers.
you can do this by overriding OnConnected() in your hub, and implementing something like this, which would map the generated Id to your own. You can then target these groups (remember, 1:1, emulating an Id selector) to your liking.
public override Task OnConnected()
{
var user = new User()
{
Id = Context.QueryString["Id"]
Name = Context.QueryString["Name"]
}
Groups.Add(Context.ConnectionId, user.Id);
return base.OnConnected();
}
Mapping SignalR Users to Connections goes into more detail as well.
Is there a way to get database's exist fields and new fields with SignalR?
I would like to make a multi chat application with MVC; and I would like to store all messages etc. in sql server database...
Planned rotation of the data will be client=>sql server=>client...
The technology is new or unknown so I couldn't find smart examples...
check this article and you should be able to save all incomming messages to DB on OnReceivedAsync Method.
http://visualstudiomagazine.com/articles/2013/01/22/build-a-signal-r-app-part-1.aspx
if you need to send last 20 records to any specific user or broadcast you can use below statement once you fetched last 20 records from you DB.
Connection.Broadcast(chatData); // this will broadcast data to all connected clients.
protected override Task OnConnectedAsync(IRequest request, string connectionId)
{
_clients.Add(connectionId, string.Empty);
ChatData chatData = new ChatData("Server", "A new user has joined the room.");
return Connection.Broadcast(chatData);
}