i can not send message to specific user by connectionId when I try to send all users like this: context.Clients.All.updateMessages(message) - this code is working.
hare is Hub code:
public void Send(string userToId, string userForId, string message)
{
//Get Recipent (userIdfor) connectionId
var signalrhelper = new HomeController();
string userForconnectionId = signalrhelper.GetConnecionIdByUserId(userForId);
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHubs>();
string messageSenderConnId= signalrhelper.GetConnecionIdByUserId(userToId);
//Call Receiver
context.Clients.Client(userForconnectionId).updateMessages(message);
//Call Sender
context.Clients.Client(messageSenderConnId).updateMessages(message);
}
Hare is My View:
$(function() {
// Declare a proxy to reference the hub.
var notifications = $.connection.chatHubs;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function(data) {
if (window.location.href.indexOf("Messages/DetailMessage?userId") > -1) {
$('#timeline-messages').append('{0}'.Stringformat(data));
} else {
ReplaceUpdateTargetIdToReturnData("Messages/GetMessages", "#header_inbox_bar", "#systemMessage");
}
};
$.connection.hub.start().done(function() {
var myClientId = $.connection.hub.id;
GetConnectionIdToSignalR("Home", "SaveConnectionIdbyUserName", "userId", #Session["UserId"], "hubConnectionId", myClientId);
$('#sendMessageButton').click(function() {
if ($('#sendMessageFiled').val().length > 1) {
// Call the Send method on the hub.
notifications.server.send(#Session["UserId"], myClientId, $('#sendMessageButton').attr("title"), $('#sendMessageFiled').val());
// Clear text box and reset focus for next comment.
$('#sendMessageFiled').val('').focus();
} else {
$('#sendMessageFiled').focus();
}
});
}).fail(function (e) {
alert(e);
});
});
Can anybody Know what's happen ?
By user, I assume you mean an authenticated user? If that is the case, you have to map connections to users first. For instance, a user can have 2 or more signalr connections. So the first step is mapping users to connections, then you can send a message to the user and all his/her connected agents will receive it.
There are several ways to map connections to users, the guide is here: http://www.google.co.uk/url?q=http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections&sa=U&ei=Tjj-VJuPMsPBOZGGgYgH&ved=0CAsQFjAA&usg=AFQjCNFXoGJOm3mzenAJbz46TUq-Lx2bvA
Although this post is already old: I had a similar issue yesterday and it took me hours! I had the connectionIds but no client received a notification. Context.Clients.All.aMethod(...) worked fine, but Context.Clients.Client(theid).aMethod(...) did not work. I finally realized that I stored the connection-ids in the database as an uniqueIdentifier and MS SQL converted the uniqueIdentifier values to uppercase and therefore the connectionids were not valid any more. I converted the connectinIds to lowercase before publishing to my connected clients and then it worked...maybe you experience a similar problem. But your post with an invalid connectionid because of blanks helped my finding the problem.
Related
I have written an Application where I am using SignalR. I am sending connectionId from Client to Server(controller).
Everything is working fine with single browser (request will sent to server with connectionId="conn_1") and signalR is sending response to only conn_1, but when i open new browser and send a request from that client the previous connection gets disposed. Which means only one connection with particular connectionId remains alive.
Is there any way SignalR can not dispose and send response to both with data they want?
I am new to SignalR and would really appropriate any help or guidance.
Angular SignalRService to start connection with server
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.paymentDraftHubUrl)
.build();
return this.hubConnection
.start()
.then(() => this.hubConnectionStatus = 'Connection started')
.catch(err => (this.hubConnectionStatus = 'Error while starting connection: ' + err));
}
sending connectionId from client component to Api
this.signalRService.startConnection().then((connection) => {
this.connectionId = connection.toString();
//Calling Api
this.getAllTransactionException(
this.connectionId,
this.pageNumber,
this.pageSize
}
MyHub class in C#
public class PaymentDraftServiceHub : Hub, IPaymentDraftHub
{}
Controller for API
using timer to keep calling repository for new data,
[HttpGet]
[Route("GetCsrTranactions")]
public IActionResult GetCsrTranactions([FromQuery] TransactionExceptionDataRequest queryParams)
{
TimeManager.Dispose();
var timerManager = new TimeManager(async () =>
await _paymentDraftHub.Clients.Clients.Client(queryParams.ConnectionId).SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetCsrTranactionsAsync(queryParams)));
var response = new ResponseMessage { Message = "Accepted", Code = "201" };
return Ok(response);
}
Client can have multiple connections with multiple connection IDs if client connect from multiple browser windows or tabs.
According to the code you provided, we can find that you just pass connection ID of SignalR client within current active browser tab/window to your controller, and in your controller action, you use this code snippet .Client(queryParams.ConnectionId).SendAsync() to send message to a specific client, so other browser windows or tabs would not receive the message.
If you'd like to send message(s) to a client with multiple connections, you need to map SignalR users to connection Ids and retain information about users-to-connectionIds mapping, then you can get all connectionIds of a client and send messages to that client with with multiple connectionIds, like below.
//code logic here
//to get all connectinIds of a client/user
//from user-to-connectionIds mapping table
await _paymentDraftHub.Clients.Clients(connectionIds_here).SendAsync("method_here",args_here);
I can't make works the message sending to one specific user from the code behind. Clients.All works, Clients.AllExcept(userId) works, but not Client.User(userId).
My hub:
public class MessagingHub : Hub
{
public override Task OnConnected()
{
var signalRConnectionId = Context.ConnectionId;
// for testing purpose, I collect the userId from the VS Debug window
System.Diagnostics.Debug.WriteLine("OnConnected --> " + signalRConnectionId);
return base.OnConnected();
}
}
My controller to send message from code behind:
public void PostMessageToUser(string ConnectionId)
{
var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();
// doesn't works
mappingHub.Clients.User(ConnectionId).onMessageRecorded();
// doesn't works
mappingHub.Clients.Users(new List<string>() { ConnectionId }).onMessageRecorded();
// works
mappingHub.Clients.All.onMessageRecorded();
// works (?!)
mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded();
}
How my hub is initialized on the JS:
var con, hub;
function StartRealtimeMessaging()
{
con = $.hubConnection();
hub = con.createHubProxy('MessagingHub');
hub.on('onMessageRecorded', function () {
$(".MessageContainer").append("<div>I've received a message!!</div>");
});
con.start();
}
And finally how I send a(n empty) message to the hub:
function TestSendToUser(connectionId)
{
$.ajax({
url: '/Default/PostMessageToUser',
type: "POST",
data: { ConnectionId: connectionId},// contains the user I want to send the message to
});
}
So, it works perfectly with mappingHub.Clients.All.onMessageRecorded(); but not with mappingHub.Clients.User(ConnectionId).onMessageRecorded(); or mappingHub.Clients.Users(new List<string>() { ConnectionId}).onMessageRecorded();.
But interestingly, it works with mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded(); : All users connected receive the message except the given userid, which means the userid is good, and the user is well identified. So, why Clients.User(ConnectionId) doesn't works?
If you want to send a message to one particular connection and when you want to use the ConnectionId, make sure you use Clients.Client, and not Clients.User
Like this:
public void PostMessageToUser(string connectionId)
{
var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();
// Like this
mappingHub.Clients.Client(connectionId).onMessageRecorded();
// or this
mappingHub.Clients.Clients(new List<string>() { connectionId }).onMessageRecorded();
}
I had the same problem. I couldn't get .User(ConnectionId) to work.
I have just spent days trying to get SignalR to report progress on a long processing job to only the client who requested the job. That is, it isn't a chat app which most of the examples describe.
Any 'long processing progress reporting' examples I found only have a sim of the job in the hub. I have a controller doing real work and need to send messages from the controller, not the hub.
I used this answer https://stackoverflow.com/a/21222303/3251300. as a workaround for your stated problem but have included all the code snippets I use for the long processing job in case they are useful for anyone who stumbles on this answer.
The workaround has an elegance in that it uses the .Group() feature. By setting each groupID equal to the internal userID, messages can be sent using .Group(userID) without having to separately maintain a list of the userID/connectionID relationships outside SignalR.
There may be a way to maintain the relationships in SignalR without using the .Group() feature but I haven’t found it yet.
Pass the userID to the view using a hidden type which then makes it available to the js.
<input type="hidden" value="#ViewBag.UserID" id="userID" />
Then in the js hub script use the following to send the userID to the hub when the hub connection starts up.
$.connection.hub.start()
.done(function () {
var userID = document.getElementById('userID').value;
$.connection.myHub.server.announce(userID);
})
.fail(function () { alert("Hub failed to start.") });
The hub then has one statement which associates the userID and connectionID to the groupID, which is then the same string as the userID.
public class MyHub : Hub
{
public void Announce(string userID)
{
Groups.Add(Context.ConnectionId, userID);
}
}
To send messages from the controller (Again, not the hub in this case, the message is reporting progress to the client on a long processing request running in the controller) after setting the hub context, use .Group() and the internal userID.
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
string fileMessage = "Some message";
hubContext.Clients.Group(userID).hubMessage(fileMessage);
This is then displayed in the view using the js to place the message in a div
$.connection.myHub.client.hubMessage = function (message) {
$("#hubMessages").html(message);
}
'#hubMessages' refers to this div in the view. Examples use .append which makes the div grow each time you send a message, .HTML replaces whatever is in the div with the new message.
<div id="hubMessages"></div>
Anyone who comes to this answer and is trying to get going on MVC and SignalR, a big shout out to Caleb who has a great series of intro vids for SignalR https://youtu.be/kr8uHeNjOKw Anyone who finds this answer who is new to SignalR I recommend you spend an hour watching these.
I face same problem.
I change from:
Clients.User(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);
to:
Clients.Client(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);
And it work :D
Thank to: Matthieu Charbonnier
I send a guid Id via Service Bus. Everything works fine and my page automatically add the new Id. However, whenever I load the page or refresh it, I can't receive any message for a while. They are somehow disappearing from nowhere. I checked azures queue for messages but it says zero, so that means the messages somehow leaking away from my code below during that time.
The "dead-time" last about 15-20 sec, then it works perfectly fine till I re-load the page again.
Sender:
public void Post(Guid id) {
var connectionString = "X";
var queueName = "Send";
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
var message = new BrokeredMessage(id);
client.Send(message);
}
Receiver:
public string GetMessage() {
var connectionString = "X";
var queueName = "Send";
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
var message = client.Receive(TimeSpan.FromMinutes(1));
var getId = message?.GetBody<string>();
if (getId == null) { return null; }
message.Complete();
return getId;
}
Ajax/JQuery:
<script type="text/jscript">
var url = "/Home/GetMessage";
function getData () {
$.get(url, function (data) {
var div = $("<div>");
div.html(data);
$("#content").append(div);
getData();
});
}
getData();
</script>
Any idea how to fix this?
I had a problem that sounded a lot like this, my solution was to make sure the ConnectivityMode was set to HTTPS prior to the call to QueueClient.CreateFromConnectionString(...). Your problem could be that it's trying to connect over TCP, and the 20 seconds might be due to it waiting for the TCP connection to fail before trying HTTP/HTTPS. On consecutive calls it will use HTTP/HTTPS, but it's on this initial Client Creation you feel this pain.
Try this:
ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Https;
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
If it works make sure you put the code that sets the SystemConnectivity.Mode in a more thought through place, and also create your Client in some sort of a Singleton. You don't want to create new Clients all the time.
I am trying to send a message to the group and I cannot get it working. I add the users to the group but the message is not sent, though using Clients.All works. Here is my setup.
Javascript called to connect to the hub, it fetches the users in the group and returns them as users in the chatroom and then sends a joinRoom to the server so I can add the user to the group and send a message back from the server to the client stating they have joined.
Javascript To Connect
$.connection.hub.start()
.done(function () {
chatHub.server.getConnectedUsers("MyChat") //return user list
.done(function (connectedUsers) {
ko.utils.arrayForEach(connectedUsers, function (item) {
users.contacts.push(new chatR.user(item.Username));
});
}).done(function () {
chatHub.server.joinRoom("MyChat", "My Room")
.done()
.fail(function(){ alert('failed to join group')}); //join the group
});
});
Server Side JoinRoom
public async Task JoinRoom(string room, string displayName)
{
// context variables
var name = Context.User.Identity.Name;
var connectionId = Context.ConnectionId;
// new group
var group = new SignalGroup(room, displayName, SignalGroupType.Chatroom);
// adding relation to storage
_manager.AddGroup(name, group); <-- adds to database
// anouncing the room was joined
Clients.Group(room).joinedRoom(name); //<-- This does not work
//Clients.All.joinedRoom(name); <-- This works
//Clients.OthersInGroup(room).joinedRoom(name);
// add group to SignalR
await Groups.Add(room, connectionId); // <-- why does this have to be last? when I move this before the _manager.AddGroup it never sends the client message?
}
So the Clients.Group(room).joinedRoom(name) does not work, i get no error message and the client never receives the message. Here is the client function.
Client Side JoinedRoom
chatHub.client.joinedRoom = function (name) {
var connectedUser = new chatR.user(name);
users.contacts.push(connectedUser);
chat.messages.push(new chatR.chatMessage("System", name + " joined.", new Date()));
};
For a "Bonus" here is my SendChatMessage method that too works when I send to ALL but not to a group.
public void SendChatMessage(string room, string message)
{
// context variables
var name = Context.User.Identity.Name;
var user = _manager.GetUser(name);
if (user.IsInGroup(room))
{
// tells clients to addChatMessage
//Clients.All.addChatMessage(name, message, DateTime.Now);
Clients.Group(room).addChatMessage(name, message, DateTime.Now);
}
}
So my main question is, Why am I not able to send to the groups? I am clearly adding them to the group and sending a message to the group?
Second question is, Why does the JoinRoom method have to have the add to group call very last or it seem it does not work at all even with All?
If you have any helpful links for this stuff that would be great as well, I have read all the MS documentation and several tutorials which leaves me even more baffled on why this is not working.
EDIT: I added a Failed check on the joinRoom call to see if I get anything back and it appears as if SignalR is failing to join the group itself. I am not sure how I would troubleshoot this though.
You got the order of parameters wrong. It should be
Groups.Add(connectionId, room);
Also, if you want to use the group for this user in the same method, you need to add the user to the group first and await the call. Otherwise it's not really necessary to await.
I have a button that users can click to bid on something. Each bid, broadcasts the latest bid to every other client. That's the reason why I'm using SignalR.
Now, the user needs to have active credits, and if he doesn't have credits I want to redirect him somewhere.
The more obvious approach fails me, so any suggestion is welcome.
//Does the user have credits to spend?
if (user.LanceCreditBalance >= 1)
{
//populate the "ar" object and send it out to everybody.
var result = Json.Encode(ar);
Clients.addMessage(result);
}
else
{
//And this isn't working as expected. Doesn't redirect
//And causes a 302 error when viewing the Firebug console in Firefox.
HttpContext.Current.Response.Redirect(#"http://www.google.com");
}
The above code is all within the Chat class which inherits from the SignalR.Hub class.
Server:
if(user.LanceCreditBalance >= 1)
{
var result = Json.Encode(ar);
// send Message to all clients
Clients.addMessage(result);
}
else
{
// Invoke a js-Function only on the current client
Caller.redirectMe("http://www.google.com");
}
Client:
$(function () {
var chat = $.connection.chat;
chat.addMessage = function(message) {
// do something
};
// function the server can invoke
chat.redirectMe = function(target) {
window.location = target;
};
$.connection.hub.start();
});