Reputation: 2550
I am just starting to explore signalR and I would like to able to send messages from the server to all clients.
Here is my Hub
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SignalR;
using SignalR.Hubs;
using SignalR.Hosting.Common;
using SignalR.Hosting.AspNet;
using System.Threading.Tasks;
namespace MvcApplication1
{
public class Chat : Hub
{
public void Send(String message)
{
// Call the addMessage methods on all clients
Clients.addMessage(message);
}
}
}
Here is my client Page
<script type="text/javascript">
$(function () {
//Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
$("#messages").append("<li>" + message + "</li>");
};
$("#broadcast").click(function () {
// call the chat method on the server
chat.send($("#msg").val());
});
$.connection.hub.start();
});
</script>
<input type="text" id="msg" />
<input type="button" id="broadcast" value="broadcast" />
<ul id="messages" class="round">
</ul>
This all works perfectly; I am able to "chat" between 2 different browsers.
The next thing I want to do is initiate a message from the server to all clients.
So I tried this:
using SignalR;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System;
using System.Web.Routing;
using SignalR;
using SignalR.Hubs;
namespace MvcApplication1
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var aTimer = new System.Timers.Timer(1000);
aTimer.Elapsed += aTimer_Elapsed;
aTimer.Interval = 3000;
aTimer.Enabled = true;
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
void aTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.Send("Hello");
}
}
}
This doesn't seem to work. The Timer works, The "aTimer_Elapsed" event handeler runs every 3 seconds but the "Send" method on the chat hub is never run.
Any ideas?
Upvotes: 27
Views: 62947
Reputation: 5656
In case you come across this question many years after it was asked, and you happen to use .NET 5.0 (or similar), the following may be useful as you might be using a framework that no longer offers the class GlobalHost
.
.NET 5.0 (and similar) make heavy use of dependency injection (DI) and it's not a surprise it is used here as well. The following sample code shows how to do this. It doesn't use the class GlobalHost
.
Given the ChatHub
class as follows:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message).ConfigureAwait(false);
}
}
in class Startup
add a line as indicated in the following code snippet:
public class Startup
{
// other code omitted for brevity
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// other code omitted for brevity
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chatHub"); // <====== add this ======
});
// other code omitted for brevity
}
}
Then in a controller where you want to send messages to SignalR message, add the ChatHub as a dependency. The following example demonstrates how to do that for constructor injection:
public class WeatherForecastController : ControllerBase
{
private readonly IHubContext<ChatHub> _hubContext;
public WeatherForecastController(
IHubContext<ChatHub> hubContext, // <============ add this ==========
ILogger<WeatherForecastController> logger)
{
_hubContext = hubContext;
_logger = logger;
}
[HttpGet]
public virtual async Task<IEnumerable<WeatherForecastModel>> Get()
{
var rng = new Random();
WeatherForecastModel[] weatherForecastModels = Enumerable.Range(1, 5).Select(index => new WeatherForecastModel
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)],
}).ToArray();
// Notify connected SignalR clients with some data:
await _hubContext.Clients.All.SendAsync("ReceiveMessage", "the weatherman", $" The temperature will be {weatherForecastModels[0].TemperatureC}").ConfigureAwait(false);
return weatherForecastModels;
}
// other code omitted for brevity
}
This example also shows how to then send messages in the Get()
method by using await _hubContext.Clients.All.SendAsync( ... )
For more information see Microsoft's documentation.
Upvotes: 36
Reputation: 95
Yes you must set that line to:
void aTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.All.addMessage("Hello");
}
However this is only half way and still wont work.
In your Js you need to write:
$(function () {
//Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.client.addMessage = function (message) {
$("#messages").append("<li>" + message + "</li>");
};
$("#broadcast").click(function () {
// call the chat method on the server
chat.client.addMessage($("#msg").val());
});
$.connection.hub.start();
});
I added the chat.client
this will add a client-side hub method that the server will call.
Upvotes: 1
Reputation: 2124
I think it should be
void aTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.All.addMessage("Hello");
}
instead. With Send you are calling the method used by the client to call the server...
Upvotes: 38