SignalR Private Chat Not Returning Data - signalr

I implemented SignalR to my project. But I have problem sending private message. I am sending 'toConnectionID' from page.
ChatHub.cs
public void LoadPrivateMessages(string toUserName,string toConnectionID)
{
var chatHub = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
string fromUserName = Context.QueryString["userName"].ToString();
string fromConnectionID = Context.ConnectionId;
List<ChatMessageDetail> currentMessages = cachePrivateMessages.Where(x => x.ToUserName == toUserName && x.FromUserName == fromUserName && x.ToConnectionID==toConnectionID && x.FromConnectionID==fromConnectionID).ToList();
chatHub.Clients.Client(toConnectionID).privateMessagesHistory(currentMessages);
}
My currentMessages list is filling. I am OK here. But I can't take messages to page.
Javascript
chatHub.client.privateMessagesHistory = function (data) {
console.log(data);
};
My console screen is null after this JavaScript code.
Edit :
That is my connection code in document.ready function. Second is sending information about receiver.
var chatHub = $.connection.chatHub;
$.connection.hub.qs = { "userName": "#Session["userName"]" };
$.connection.hub.start().done(function () {
console.log("Connection OK !");
});
$(document).on('click','.onlineUser', function () {
var userName = $(this).attr('id');//That is which user to send(Receiver)
var toConnectionID = $(this).attr('connectionID');
chatHub.server.loadPrivateMessages(userName, toConnectionID);
$('.privateMessagediv').show();
$('.userName').html("");
$('.userName').append("<h4>" + userName + "</h4>");
$('.btnSendingPrivateMessage').attr('id', userName);
$('.btnSendingPrivateMessage').attr('connectionid', toConnectionID);
chatHub.client.privateMessagesHistory = function (data) {
$('#privateChatBox').append(data);
};
});
Edit 2 :
I solved issue.
chatHub.Clients.Client(fromConnectionID).privateMessagesHistory(currentMessages);
instead of
chatHub.Clients.Client(toConnectionID).privateMessagesHistory(currentMessages);

My original suspicion was with the toConnectionId, but after examining your privateMessageHistory callback registration, it's more likely a timing issue.
You need to setup the client callback before the hub .start().
From Microsoft:
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method
There's no need to place your callback registration in the click event.
var chatHub = $.connection.chatHub;
$.connection.hub.qs = { "userName": "#Session["userName"]" };
// callback first
chatHub.client.privateMessagesHistory = function (data) {
$('#privateChatBox').append(data);
};
// start the hub connection - note chatHub is different from your .hub
chatHub.start().done(function () {
console.log("Connection OK !");
});

Related

SignalR needs to target specific games with Game ID and not all live games

I didnt think about this but this code is sending the game model to all clients. I need to use the GameID from this controller action and only target the clients watching that game. How do I do that?
Publish Controller Action
public UpdateGameResponse UpdateGame(int gameId)
{
...
var model = Game.Create(XDocument.Load(httpRequest.Files[0].InputStream)).Parse();
GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.All.receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model));
}
Hub
[HubName("gamecastHub")]
public class GameCastHub : Hub
{
}
Client
var connected = false;
var gamecastHub = $.connection.gamecastHub;
if (gamecastHub) {
gamecastHub.client.receiveUpdates = function (updates) {
console.log('New updates received');
processUpdates(updates);
};
connectLiveUpdates();
$.connection.hub.connectionSlow(function () {
console.log('Live updates connection running slow');
});
$.connection.hub.disconnected(function () {
connected = false;
console.log('Live updates disconnected');
setTimeout(connectLiveUpdates, 10000);
});
$.connection.hub.reconnecting(function () {
console.log('Live updates reconnecting...');
});
$.connection.hub.reconnected(function () {
connected = false;
console.log('Live updates reconnected');
});
}
I suggest using either the connection Id associated with each connection to the hub or creating groups.
Note: Each GameID must have its own connection to the hub in order to use the connection Id solution.
I prefer to use groups from personal experience but either way can be done.
To create a group in the hub you will need to create a method in your hub class.
public async void setGroup(string groupName){
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
Secondly, you will need a JS function on the client side to call the hub function.
$.connection.hub.invoke("setGroup", groupName).catch(err => console.error(err.toString()));
In your case, you can place your gameID as the groupname and then call GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.Groups(gameID).receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model));
To retrieve the connection Id:
var _connectionId = $.connection.hub.id;
Then send the connection Id to the server,
and proceed to using the call GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.Clients.Client(_connectionId).receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model)); to call that specific connection.

SignalR2 OnConnected not working as per documentation

Below is the code I wrote for SignalR implementation based on ASP.Net documentation and I use manual proxy creation method. I Could see only negotiate happening and received a Connection id.
I can't see OnConnected method in my hub gets executed when I start connection. According to the note section in the document I have attached event handler before I call start method
SignalR Hub
public class MyTestHub: Hub
{
private static Dictionary<int, List<string>> userConnections
= new Dictionary<int, List<string>>();
public override Task OnConnected()
{
RegisterUserConnectionInMap();
return base.OnConnected();
}
}
Startup.cs
app.Map(
"/signalr",
map =>
{
var hubConfiguration = new HubConfiguration { EnableDetailedErrors = true};
map.RunSignalR(hubConfiguration);
});
Javascript Client Code
var connection = $.hubConnection();
var contosoChatHubProxy = connection.createHubProxy('MyTestHub');
contosoChatHubProxy.on('addContosoChatMessageToPage', function(userName:any, message:any) {
console.log(userName + ' ' + message);
});
connection.start()
.done(function(){ console.log('Now connected, connection ID=' + connection.id); })
.fail(function(){ console.log('Could not connect'); });
Note section in documentation
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. One reason for this is that there can be many Hubs in an
application, but you wouldn't want to trigger the OnConnected event on
every Hub if you are only going to use to one of them. When the
connection is established, the presence of a client method on a Hub's
proxy is what tells SignalR to trigger the OnConnected event. If you
don't register any event handlers before calling the start method, you
will be able to invoke methods on the Hub, but the Hub's OnConnected
method won't be called and no client methods will be invoked from the
server.
I could not figure out what I miss for past two days.
UPDATE:
Even I tried with auto generated proxy class by including <script src="~/SignalR/hubs" with the following client code. Still OnConnected Not fired
var contosoChatHubProxy = $.connection.myTestHub;
contosoChatHubProxy.client.addContosoChatMessageToPage = function (name, message) {
console.log(userName + ' ' + message);
};
$.connection.hub.start()
.done(function(){ console.log('Now connected, connection ID=' + $.connection.hub.id); })
.fail(function(){ console.log('Could not Connect!'); });
Console Log after connectton
I have ended with the below solution. Hope it will help some one.
declare var $: any;
#Injectable()
export class CityChangeNotifier {
constructor(private appService: AppService, private router: Router) {
this.connection = $.hubConnection();
this.CityChangeHub = this.connection.createHubProxy('CityChangeNotificationHub');
this.CityChangeHub
.on('CityUpdatedByServer', (newLocation:any, connectionId:string) => this.onCityUpdatedByServer(newLocation, connectionId));
this.connection.transportConnectTimeout = 10000;
this.startConnection();
}
private startConnection(): void {
let that = this;
this.connection.start()
.done((connection: any) => { that.connectionId = connection.id; })
.fail(() => { });
}
}

SignalR - Passing back a Client's response to the server

I am calling a client-side method from the server that is essentially a Javascript confirm dialog. If the client clicks theCancel button on the dialog I want to delete their token from local storage (essentially logging them out) ...
sessionStorage.removeItem('access-token');
If the client clicks the OK button I want to do the same on a different client (I have the connectionId of this client stored on the server side). So I need a way for the server to know which option the client chose (OK or Cancel).
How would I go about taking the client's choice and passing it back to the server, so the server could call the "Logout" Javascript on the proper client?
I think you have a couple of options.
You could send a message back to the Hub once the user has clicked on a button.
Hub
[HubName("message")]
public class MessageHub : Hub
{
public void sendmessage(bool logout)
{
Clients.Caller(????).logoutuser(logout); //However you identify who to logout??
}
}
Client
var hub = $.connection.message;
hub.client.logoutuser = function(message) {
if (message.logout = true) {
sessionStorage.removeItem('access-token');
}
}
hub.server.sendmessage(true); //result of the user's click OK -> True, Cancel -> False
$.connection.hub.start().done(function () { });
Or you could hit an API that gets the connection for some? user who you're wanting to log out.
API
[RoutePrefix("api/messaging")]
public class MessagingController : ApiController
{
[Route("")]
public void Post(Message message)
{
var notificationHub = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
if (notificationHub != null)
{
try
{
notificationHub.Clients.User(message.UserName).logoutuser(message);
}
catch (Exception ex)
{
}
}
}
}
Client
function notifyController(responseObj) {
$.ajax({
url: '/api/Messaging/',
type: 'POST',
data: responseObj, // some object containing the users response information?
success: function (data) { return; },
error: function (ex) { return; }
});
}

Public method not firing in SignalR

I have a simple application, like a chat, integrated with SignalR. I added a new method on my Hub and a new function on client side, like you can see below.
The problem is, my method called SendMessageChat isn't firing, because occurs the following error
TypeError: chat2.server.SendMessageChat is not a function
but the method chat2.server.send works fine, and I don't know why my second method doesn't work. Can someone help me ?
JavaScript
$(function () {
var chat2 = $.connection.redirectTask;
chat2.client.broadcastMessage = function (name, message) {
// Do something here
};
chat2.client.sendMessage = function (name, message) {
// Do something here
};
//$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
/* BUTTON CLICK IN ANOTHER PAGE */
$('#btnFinish').click(function () {
chat2.server.send($.cookie("User"), $("#lista :selected").text());
});
/* CASE HIT ENTER INSIDE THE TEXT FIELD IN CHAT */
$(document).on("keypress", "#txtChat", function (e) {
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13) {
var message = $(this).val();
$(this).val("");
chat2.server.SendMessageChat($.cookie("User"), message);
}
});
});
});
SERVER SIDE
public class RedirectTask : Hub
{
public void Send(string nome, string message)
{
Clients.All.broadcastMessage(name, message);
}
public void SendMessageChat(string nome, string message)
{
Clients.All.sendMessage(name, message);
}
}
Reference
Need to change to
chat2.server.sendMessageChat($.cookie("User"), message);
Camel-casing of method names in JavaScript clients
By default, JavaScript clients refer to Hub methods by using a camel-cased version of the method name. SignalR automatically makes this change so that JavaScript code can conform to JavaScript conventions.
Server
public void NewContosoChatMessage(string userName, string message)
JavaScript client using generated proxy
contosoChatHubProxy.server.newContosoChatMessage(userName, message);
If you want to specify a different name for clients to use, add the HubMethodName attribute.
Server
[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)
JavaScript client using generated proxy
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

The On event on the SignalR Client Hub does not get called

I seem to have an issue with SignalR's JS Client Hub.
The problem is that the 'on' handler does not seem to work - it generates no error but doesn't receive any signals sent by the server.
The code below shows an extract where I call the server (using the invoke) which works fine - then on the server I call back to acceptHubData which should be picked up on the client but isn't.
My objective is when navigating to pages that each page will open a connection to a specific hub and releases this connection when the user moves to another page!!
EDIT: using the following code snippet works but I wonder why the code further below using the 'on' event doesn't work!
var superHub = $.connection.mySuperHub;
superHub.client.acceptHubData = function (data) {
$('<li>hello there' + data + '</li>').prependTo($('#ul1'))
}
$.connection.hub.start().done(function () {
$('<li>done phase 1</li>').prependTo($('#ul1'))
});
Any help would be much appreciated!
This is the client code (in js)
$(document).ready(function () {
var myHub;
try {
var connection = $.hubConnection();
connection.start().done(function () {
myHub = connection.createHubProxy("mySuperHub");
myHub.on('acceptHubData', function (data) {
alert(data); // THIS IS NOT CALLED!
});
myHub.invoke('AcceptSignal', "hello from the client2");
});
}
catch (e) {
alert(e.message);
}
});
This is the Server code:
[HubName("mySuperHub")]
public class MyHub : Hub
{
private readonly HubEngine _hubEngine;
public MyHub() : this(HubEngine.Instance) { }
public MyHub(HubEngine hubEngine)
{
_hubEngine = hubEngine;
}
public void AcceptSignal(string msg)
{
Clients.Caller.acceptHubData("hi");
Clients.All.acceptHubData("hi");
}
}
You can still use the on method to add events for JS client hub method calls in the latest version of SignalR, but if you do not add any event listeners to a hubProxy before calling hubConnection.start(), you will not be subscribed to the hub. SignalR subscribes to the hubs you have event handlers for when the hubConnection starts. If you are not subscribed to your hub, adding any events to that hub after start() won't work.
If you add at least one event listener to the hub before start(), even if it doesn't do anything, you can then add any additional event handlers you want to the hub using on after start() and your handlers will be called.
It doesn't matter if you add an event using hubProxy.on('eventName', function (... or autogeneratedHubProxy.client.eventName = function (... before you call start(), but only on will successfully add event listeners after start() is called.
Not sure which version of SignalR you are using, but I have had more success using the following syntax on my server:
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.acceptHubData("hello");
and on my clients:
myHub.client.acceptHubData = function (data) {
console.log(data);
}

Resources