I want to broadcast message using SignalR from server to all clients at particular server time. This broadcast message should be automatic where all clients keep listening to server method and once we reach the scheduled time on server, server should broadcast the message.
But I don't find a way to this. I was successful by invoking from a client and broadcasting it to all clients but not automatically.
How this can be achieved.
First you need some sort of hub:
public class MyHub : Hub {}
You then can connect to this hub with clients or whatever you want to do. Then you need some type of looping class that sends to your hub (you could also do this within the hub as a static timer but we'll do this as a separate class).
public class Looper
{
public Timer Interval
public Looper(TimeSpan interval)
{
Interval = new Timer(Loop, null, TimeSpan.Zero, interval);
}
private static void Loop(object state)
{
GlobalHost.ConnectionManager.GetHubContext<ChatHub>().Clients.All.executeYourMethod();
}
}
Now we need to instantiate this class somewhere, we'll do that inside our hub as a static:
public class MyHub : Hub
{
public static Looper MyInterval = new Looper(TimeSpan.FromSeconds(1)); // Send every 1 second
}
Hope this helps!
You can just add a timer to your hub class and broadcast to each client at the required intervals.
Related
I want to send messages from the server (from a class, not a controller) via the SignalR Hub.
The hub works for messages originating from the client but not for messages from the server.
I've tried multiple methods of which non seem to work. For example, I tried retrieving the hub context using:
GlobalHost.ConnectionManager.GetHubContext<MyHub>()
with no success.
What is the best and up-to-date method of doing this in .NET Core?
Temporary Solution:
Having a websocket client inside the host api. Then making it connect to itself. This is not an ideal solution but works as a temporary fix.
You can inject the context in your class as service. Your class must be initialized via DI and added as a service. There is no difference between class or controller.
public class SomeClass
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public SomeClass(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
You can also get service like following by injecting IHttpContextAccessor
var _strongChatHubContext = httpContextAccessor.HttpContext.RequestServices.GetRequiredService<IHubContext<ChatHub, IChatClient>>()
reference:
https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.1
First you need to somewhere instantiate your hub (normally when your app is bootstrapping).
MyHub myHub = new MyHub();
Then on your class inject the context:
private readonly IHubContext<NotifyHub, ITypedHubClient> hubContext;
And in your class method just call the hub:
hubContext.Clients.All.yourHubMethod(yourPayload);
I am trying to use the Caller method outside my Hub Context. I have a helper class which works fine when broadcasting a message to all users like so:
hub.Clients.All.newLessonAlert(notif);
It won't allow me to use the Caller method within this class but this works fine in the hub context class. Why is this? I have also tried to move all of my functions inside the context class but I now get this unhanded exception:
Using a Hub instance not created by the HubPipeline is unsupported
Is there a straightforward way to continue to use my helper class and identify connections to the hub?
I solved this in the following way:
I created a OnConnected method in my Hub class. This assigned the currently connected user to a group.
[HubName("NotificationsHub")]
public class NotificationHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
public override Task OnConnected()
{
string userid = Context.Request.User.Identity.GetUserId();
Groups.Add(Context.ConnectionId, userid);
return base.OnConnected();
}
}
Modified my HubHelper class to now broadcast this alert to the currently connected user (specified by the user's ID)
public void HighScoreAlert(int gameid, int score, string userID)
{
string message = "High Score achieved on " + gameid;
hub.Clients.Group(userID).score(message);
}
For the controller action I pass in the user's ID and then call the HubHelper method featured above.
Hope this helps someone
I'd like to know if an Hub method called from js client is
executed completely also if client disconnected during execution
For example..
public virtual void Join(int userId)
{
using (var context = new HubConnectionContext(this, RoomId, userId))
{
T workflow = GetWorkflow(context);
workflow.OnUserJoin();
}
}
I can be sure that the Dispose Method of HubConnectionContext is called also if client disconnect during using block?
I want to create a new system to send real time trade execution messages to users using SignalR. In the old system, each client connects to the trading server using Java Applet TCP connection.
I use the following tutorial as reference
http://www.asp.net/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr
There is a line of code in the StockTicker constructor to update the stock prices:
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
However, I need to update trade execution in real time instead of updating stock prices per 250ms.
Is it okay to create TCP connection to my trading server per client in the constructor? It seems that in the sample code, the constructor of StockTicker (i.e. my TradingManager) will be called one time only. But in my design, I want to create a TCP connection per client. How should I change the code to do this?
Here is my code:
TradingHub.cs
public class TradingHub : Hub
{
private readonly TradingManager _tradingManager;
public TradingHub() : this(TradingManager.Instance) { }
public TradingHub(TradingManager tradingManager)
{
_tradingManager = tradingManager;
}
...
}
TradingManager.cs
public class TradingManager
{
// Singleton instance
private readonly static Lazy<TradingManager> _instance = new Lazy<TradingManager>(
() => new TradingManager());
...
public static TradingManager Instance{ get{ return _instance.Value; } }
public TradingManager()
{
...
this.apiConnector.MessageReceived += new CustomEventHandler(this.api_MessageReceived);
init();
}
private IHubConnectionContext<dynamic> Clients { get; set; }
private void init()
{
TradingSession tradingSession = getLoginSession(user);
// connect to trading server using TCP connection
this.apiConnector.ensureConnected(host, port, tradingSession);
// send keep alive message to trading server periodically
_timer = new Timer(sendKeepAlive, null, _updateInterval, _updateInterval);
}
private void api_MessageReceived(object sender, CustomEventArgs e)
{
// when web server receives trade execution from server, send out the message immediately
Clients.Caller.SendTradeExecutionMessage(......);
}
public static TradingSession getLoginSession(string user)
{
...
}
private void sendKeepAlive(object state)
{
...
}
}
If you were to make a new TradingManager in your Hub constructor instead of referencing a singleton, you would be creating more than one TradingManager per SignalR connection. Hubs are reinstantiated per method call. Every time you invoke a hub method or a hub event is called (e.g. OnConnected, OnReconnected, OnDisconnected), your constructor will be called.
However, OnConnected is only called once per SignalR connection. By the way, SignalR connections are completely orthogonal to TCP connections. With the long polling transport, for example, a new HTTP request is sent each time a message is received.
I think you want to create a new TradingManager instance each time OnConnected is called and potentially associate it with the client's Context.ConnectionId and store it (perhaps in a ConcurrentDictionary) so you can retrieve it using the connection id when your Hub methods are called. You can then dereference the stored TradingManager instance for a given connection id in OnDisconnected.
You can learn more about SignalR connections at:
http://www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events
You can learn more about the Hub API and the On* methods at:
http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server#connectionlifetime
I am using SignalR to broadcast messages to all my clients. I need to trigger the broadcasting outside of my hub class i.e. something like below:
var broadcast = new chatHub();
broadcast.Send("Admin","stop the chat");
I am getting error message as:
Using a Hub instance not created by the HubPipeline is unsupported.
You need to use GetHubContext:
var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>();
context.Clients.All.Send("Admin", "stop the chat");
This is described in more detail at http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub.
A small update for those who might be wondering where the GlobalHost has gone. SignalR has been completely rewritten for .net core. So if you are using the SignalR.Core package (Difference between SignalR versions), you get an instance of SignalR hub context by injecting it into your service:
public class MyNeedyService
{
private readonly IHubContext<MyHub> ctx;
public MyNeedyService(IHubContext<MyHub> ctx)
{
this.ctx = ctx;
}
public async Task MyMethod()
{
await this.ctx.All.SendAsync("clientCall");
}
}
And in Startup.cs:
services.AddSignalR()/*.AddAzureSignalR("...")*/;
Microsoft docu is here: Send SignalR messages from outside the hub.