periodically sending messages to all clients using signalr - asp.net

I want to send some data from server to all connected clients using hubs after a specific interval. How can I accomplish this using signalr hubs.

Spin up the System.Threading.Timer, and from it's callback broadcast the message using specific hub.
Global.asax:
private Timer timer;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs("~/signalr2");
timer = new Timer(TimerCallback(timerCallback), null, Timeout.Infinite, 1000);
}
}
Check the “Broadcasting over a Hub from outside of a Hub” section in SignalR wiki page.

Use ReactiveExtensions and then setup an Observable.Interval call. Then reactive will automatically call the lambda which can broadcast to your clients.

I have stumbled upon this post by Jason Roberts => http://dontcodetired.com/blog/post/Using-Server-Side-Timers-and-SignalR-in-ASPNET-MVC-Applications.aspx
He uses IRegisteredObject and HostingEnvironment.RegisterObject then a System.Threading.Timer in the class that does the work, I haven't tried it myself, but it looks exactly the sort of thing.

Just add
Thread.Sleep(5000);
in your send Method.
Ex:
public void Send(string name, string message)
{
Thread.Sleep(5000);
//call the broadcast message to upadate the clients.
Clients.All.broadcastMessage(name, message);
}
Hope it helps.
Edit
The following code renders the current time for every 5 seconds.
Here is script for it:
<script type="text/javascript">
$(function () {
$.connection.hub.logging = true;
$.connection.hub.start();
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//Appending the responce from the server to the discussion id
chat.client.currentTime = function (time) {
$('#discussion').append("<br/>" + time + "<br/>");
};
// Start the connection.
$.connection.hub.start().done(function () {
//Call the server side method for every 5 seconds
setInterval(function () {
var date = new Date();
chat.client.currentTime(date.toString());
}, 5000);
});
});
</script>
<div id="discussion"></div>
And on the HubClass write the following:
public class ChatHub: Hub
{
public void currentTime(string date)
{
Clients.All.broadCastTime(date);
}
}

Related

How to use SignalR to re-direct after Session timeout?

I am using SignalR to redirect my app after session timeout:
void Session_End(object sender, EventArgs e)
{
var HubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
HubContext.Clients.All.clientListener(false);}
And my Hub class:
public class MyHub : Hub
{
public void ServerListener(bool result)
{
Clients.Caller.ClientListener(result);
}
}
JavaScript:
(function () {
var myHub = $.connection.myHub;
$.connection.hub.start()
.done(function () {
console.log("Connected");
})
.fail(function () {
alert("Failed!");
})
myHub.client.clientListener = function (data) {
if (data == false)
window.location.href = "/Home/Index";//#Url.Action("Index","Home");
}
})();
The Problems that I face is:
When multiple users are connected all users are logged out at the same time, even if they logged in at different times.
Logout redirection occurs even if regular requests are made.
It would really help me, if someone could tell me how to do a "server push" without invoking the Session_End() as I want to use some other session state other than "In Proc".
Regarding Question #1:
Since you're using HubContext.Clients.All.clientListener all users connected to SignalR are indeed going to receive the message from the server - it doesn't matter when they logged in to your app.
You should use this guide to send a message from the server to a specific user: https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections .
Personally I like single-user groups idea https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections#single-user-groups.
As for the additional question, as long as you can get the hub reference with GlobalHost.ConnectionManager.GetHubContext<MyHub>() all you have to do is call a registered client side method to perform a server push (e.g. Clients.Group(userid).clientListener(false)). It's not something that has to be done exclusively on Session_End().
What I did is:
On Page_Load I update a hidden label with the Session.SessionId
Pass it via query string before starting hub on client:
$.connection.hub.qs = { "sessionId": $("#lblSessionId").text() };
On the server side in the hub OnConnected/OnReconnected I call this method to store the correlation of sessionId->connectionId in dictionary:
private void AddConnectedClient()
{
// read the [ASP.Net] sessionId we send from client in queryString
string sessionId = Context.Request.QueryString["sessionId"];
if (!string.IsNullOrEmpty(sessionId))
sessionIdToConnectionId.AddOrUpdate(sessionId, Context.ConnectionId, (k, v) => Context.ConnectionId);
}
Now in the Session_End I have this code (GetConnectionIdFromSessionId is a static method I added in hub to read from sessionIdToConnectionId dictionary shown in #3):
protected void Session_End(object sender, EventArgs e)
{
// look for signalR connecitonId
string connectionID = YourHub.GetConnectionIdFromSessionId(Session.SessionID);
if (connectionID != null)
GlobalHost.ConnectionManager.GetHubContext<YourHub>().Clients.Client(connectionID).onSessionExpired();
}

Call SignalR Client Method on Startup

I want to call a client side method from my SignalR hub class only once, when the client first loads up the webpage. How would I go about doing this?
In your BlahBlahHub class, you have access to all of the connection methods that you do on the Client. Hint: Look at the base Hub class.
With that being said, here's what the code would look like:
Hub
[HubName("messageHub")]
public class MessageHub : Hub
{
public override Task OnConnected()
{
Clients.Caller.alertUser(88);
return base.OnConnected();
}
}
Client
var sender = $.connection.messageHub;
$.connection.hub.start().done(function () {
}).fail(function (reason) {
console.log("SignalR connection failed: " + reason);
});
sender.client.alertUser = function (test) {
alert(test);
};

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);
}

SignalR client in asp.net

I created a server hub in an asp.net application like below
public class Calc : Hub
{
public void CalculateSomething(int a, int b)
{
// start working in a new thread
var task = Task.Factory.StartNew(() => DoCalculate(a, b));
// attach a continuation task to notify
// the client when the work is done
task.ContinueWith(t =>
{
System.Threading.Thread.Sleep(2000);
Clients.addMessage(t.Result);
Caller.notifyCalculateResult(t.Result);
System.Threading.Thread.Sleep(2000);
Caller.notifyCalculateResult("Completed");
Clients.addMessage("Completed");
});
}
private int DoCalculate(int p1, int p2)
{
// do some slow work on the input,
// e.g. call webservice or I/O.
int result = p1 + p2;
//int result = DoSlowWork(p1, p2);
return result;
}
}
Now in another asp.net application I created a client using SiganlR client. But it's not working correctly. I am looking to get data from server as it pushes to client
using System.Threading.Tasks;
using SignalR;
using SignalR.Client;
using SignalR.Client.Hubs;
namespace WebApplication2
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Connect to the service
var hubConnection = new HubConnection("http://localhost:3119/");
// Create a proxy to the chat service
var chat = hubConnection.CreateProxy("Calc");
// Print the message when it comes in
chat.On("addMessage", message =>Print(message));
// Start the connection
hubConnection.Start().Wait();
// Send a message to the server
chat.Invoke("CalculateSomething", 1, 2).Wait();
}
private async void Print(object message)
{
Response.Write(message);
}
}
}
The console client application works fine. The main problem is with asp.net beacause it fails to the handle call back from server.
Looks like you calling the server side method wrongly, try this
chat.Invoke("CalculateSomething", 1, 2).ContinueWith(task =>
{
Console.WriteLine("Value from server {0}", task.Result);
});

Resources