I have the below NotificationUserHub class for Signalr Hub in .Net core Api implemented using https://www.zealousweb.com/signalr-to-send-real-time-notifications-with-asp-net-core/
using Microsoft.AspNetCore.SignalR;
public class NotificationUserHub: Hub
{
}
Below is the class where Im trying to send messages to client:
public class SampleClass
{
private readonly IHubContext<NotificationUserHub> _notificationUserHubContext;
public SampleClass(IHubContext<NotificationUserHub> notificationUserHubContext)
{
_notificationUserHubContext = notificationUserHubContext ?? throw new ArgumentNullException(nameof(notificationUserHubContext));
}
public async Task<bool> SendMessageToClient(message)
{
await _notificationUserHubContext.Clients.All.SendAsync("MethodName",message);
return true;
}
}
I have a client which is a angular application is connecting to this web socket
async startSignalrConnection() {
const uname = "string";
const hc = this._hubConnection;
let that = this;
await this._hubConnection
.start()
.catch((err) =>
console.log("Error while establishing connection :("))
return this._hubConnection;
}
getSignalRConnection() {
let token = this.tokenService.getToken();
this._hubConnection = new HubConnectionBuilder()
.withUrl(
'https://localhost:44353/NotificationUserHub?' +'jwt='+ token,
{
skipNegotiation: true,
transport: HttpTransportType.WebSockets
}
)
.build();
this.startSignalrConnection().then(function (res) {
})
this._hubConnection.on(
"MethodName",
(receivedMessage: any) => {
this.messageList.push(receivedMessage);
}
);
The issue is if I open multiple tabs in browser or use multiple computer browsers and connect to same web socket, I see only some tabs are getting the message sent from the _notificationUserHubContext.Clients.All.SendAsync("MethodName",message);
Below is the browser developer tools where the messages are retrieved:
I have the below NotificationUserHub class for Signalr Hub in .Net core Api implemented using https://www.zealousweb.com/signalr-to-send-real-time-notifications-with-asp-net-core/
using Microsoft.AspNetCore.SignalR;
public class NotificationUserHub: Hub
{
}
Below is the class where Im trying to send messages to client:
public class SampleClass
{
private readonly IHubContext<NotificationUserHub> _notificationUserHubContext;
public SampleClass(IHubContext<NotificationUserHub> notificationUserHubContext)
{
_notificationUserHubContext = notificationUserHubContext ?? throw new ArgumentNullException(nameof(notificationUserHubContext));
}
public async Task<bool> SendMessageToClient(message)
{
await _notificationUserHubContext.Clients.All.SendAsync("MethodName",message);
return true;
}
}
I have a client which is a angular application is connecting to this web socket
async startSignalrConnection() {
const uname = "string";
const hc = this._hubConnection;
let that = this;
await this._hubConnection
.start()
.catch((err) =>
console.log("Error while establishing connection :("))
return this._hubConnection;
}
getSignalRConnection() {
let token = this.tokenService.getToken();
this._hubConnection = new HubConnectionBuilder()
.withUrl(
'https://localhost:44353/NotificationUserHub?' +'jwt='+ token,
{
skipNegotiation: true,
transport: HttpTransportType.WebSockets
}
)
.build();
this.startSignalrConnection().then(function (res) {
})
this._hubConnection.on(
"MethodName",
(receivedMessage: any) => {
this.messageList.push(receivedMessage);
}
);
The issue is if I open multiple tabs in browser or use multiple computer browsers and connect to same web socket, I see only some tabs are getting the message sent from the _notificationUserHubContext.Clients.All.SendAsync("MethodName",message);
Below is the browser developer tools where the messages are retrieved:
I'm a newb to SignalR. I'm trying to set up a Asp.Net Core WebAPI so that other clients can connect to it using SignalR and get real-time data.
My Hub class is:
public class TimeHub : Hub
{
public async Task UpdateTime(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
I have a relay class as follows:
public class TimeRelay : ITimeRelay
{
private readonly IHubContext<TimeHub> _timeHubContext;
public TimeRelay(IHubContext<TimeHub> context)
{
_timeHubContext = context;
Task.Factory.StartNew(async () =>
{
while (true)
{
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
});
}
}
Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseSignalR((x) =>
{
x.MapHub<TimeHub>("/timeHub");
});
app.UseMvc();
}
The client is a console application and the code is:
class Program
{
static Action<string> OnReceivedAction = OnReceived;
static void Main(string[] args)
{
Connect();
Console.ReadLine();
}
private static async void Connect()
{
var hubConnectionBuilder = new HubConnectionBuilder();
var hubConnection = hubConnectionBuilder.WithUrl("http://localhost:60211/timeHub").Build();
await hubConnection.StartAsync();
var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
Console.ReadLine();
on.Dispose();
await hubConnection.StopAsync();
}
static void OnReceived(string message)
{
System.Console.WriteLine($"{message}");
}
}
I tried debugging the application. The client got connected to the TimeHub succesfully. The no of connections in Clients.All changed from 0 to 1, when the client got connected. But, when await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString()); is executed, the UpdateTime function in TimeHub is not getting executed and the client is not getting any message.
I tried using "UpdateTime", "SendMessage", and "ReceiveMessage" as method in Clients.All.SendAsync in TimeRelay class. Nothing worked. Could someone point out my mistake in this.
For Clients, it will be null if there is no client connecting to server. For starting Asp.Net Core SignalR and Console App at the same time, the Clients may be null since Index may be called before Console App connects the signalR server.
Try steps below:
Change TimeHub
public class TimeHub: Hub
{
public async Task UpdateTime(string message)
{
if (Clients != null)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
}
Register TimeHub
services.AddSingleton<TimeHub>();
Controller
public class HomeController : Controller
{
private readonly TimeHub _timeHub;
public HomeController(TimeHub timeHub)
{
_timeHub = timeHub;
}
public IActionResult Index()
{
Task.Factory.StartNew(async () =>
{
while (true)
{
try
{
await _timeHub.UpdateTime(DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
catch (Exception ex)
{
}
}
});
return View();
}
I got it to work and thought I will answer it here. Thanks #TaoZhou for the tip.
My mistake was sending "UpdateTime" from server and waiting on "ReceiveMessage" at the client.
Ideally the code should look like the following:
SignalR Server:
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
SignalR Client:
var on = hubConnection.On("UpdateTime", OnReceivedAction);
In this case any message send from the server would be received at the client instantly.
Please refer the code provided in the question for more info.
Is there a small example of a console or winform app using signalR to send a message to a .net hub?. I have tried the .net examples and have looked at the wiki but it is not making sense to me the relationship between the hub(.net) and client(console app) (could not find an example of this). Does the app just need the address and name of hub to connect?.
If someone could provide a small tidbit of code showing the app connecting to a hub and sending "Hello World" or something that the .net hub receives?.
PS. I have a standard hub chat example which works well , if I try to assign a hub name in Cs to it , it stops working i.e [HubName("test")] , do you know the reason for this?.
Thanks.
Current Console app Code.
static void Main(string[] args)
{
//Set connection
var connection = new HubConnection("http://localhost:41627/");
//Make proxy to hub based on hub name on server
var myHub = connection.CreateProxy("chat");
//Start connection
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected");
}
}).Wait();
//connection.StateChanged += connection_StateChanged;
myHub.Invoke("Send", "HELLO World ").ContinueWith(task => {
if(task.IsFaulted)
{
Console.WriteLine("There was an error calling send: {0}",task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Send Complete.");
}
});
}
Hub Server. (different project workspace)
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.addMessage(message);
}
}
Info Wiki for this is http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-net-client
First of all, you should install SignalR.Host.Self on the server application and SignalR.Client on your client application by nuget :
PM> Install-Package SignalR.Hosting.Self -Version 0.5.2
PM> Install-Package Microsoft.AspNet.SignalR.Client
Then add the following code to your projects ;)
(run the projects as administrator)
Server console app:
using System;
using SignalR.Hubs;
namespace SignalR.Hosting.Self.Samples {
class Program {
static void Main(string[] args) {
string url = "http://127.0.0.1:8088/";
var server = new Server(url);
// Map the default hub url (/signalr)
server.MapHubs();
// Start the server
server.Start();
Console.WriteLine("Server running on {0}", url);
// Keep going until somebody hits 'x'
while (true) {
ConsoleKeyInfo ki = Console.ReadKey(true);
if (ki.Key == ConsoleKey.X) {
break;
}
}
}
[HubName("CustomHub")]
public class MyHub : Hub {
public string Send(string message) {
return message;
}
public void DoSomething(string param) {
Clients.addMessage(param);
}
}
}
}
Client console app:
using System;
using SignalR.Client.Hubs;
namespace SignalRConsoleApp {
internal class Program {
private static void Main(string[] args) {
//Set connection
var connection = new HubConnection("http://127.0.0.1:8088/");
//Make proxy to hub based on hub name on server
var myHub = connection.CreateHubProxy("CustomHub");
//Start connection
connection.Start().ContinueWith(task => {
if (task.IsFaulted) {
Console.WriteLine("There was an error opening the connection:{0}",
task.Exception.GetBaseException());
} else {
Console.WriteLine("Connected");
}
}).Wait();
myHub.Invoke<string>("Send", "HELLO World ").ContinueWith(task => {
if (task.IsFaulted) {
Console.WriteLine("There was an error calling send: {0}",
task.Exception.GetBaseException());
} else {
Console.WriteLine(task.Result);
}
});
myHub.On<string>("addMessage", param => {
Console.WriteLine(param);
});
myHub.Invoke<string>("DoSomething", "I'm doing something!!!").Wait();
Console.Read();
connection.Stop();
}
}
}
Example for SignalR 2.2.1 (May 2017)
Server
Install-Package Microsoft.AspNet.SignalR.SelfHost -Version 2.2.1
[assembly: OwinStartup(typeof(Program.Startup))]
namespace ConsoleApplication116_SignalRServer
{
class Program
{
static IDisposable SignalR;
static void Main(string[] args)
{
string url = "http://127.0.0.1:8088";
SignalR = WebApp.Start(url);
Console.ReadKey();
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
/* CAMEL CASE & JSON DATE FORMATTING
use SignalRContractResolver from
https://stackoverflow.com/questions/30005575/signalr-use-camel-case
var settings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc
};
settings.ContractResolver = new SignalRContractResolver();
var serializer = JsonSerializer.Create(settings);
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);
*/
app.MapSignalR();
}
}
[HubName("MyHub")]
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
}
}
Client
(almost the same as Mehrdad Bahrainy reply)
Install-Package Microsoft.AspNet.SignalR.Client -Version 2.2.1
namespace ConsoleApplication116_SignalRClient
{
class Program
{
private static void Main(string[] args)
{
var connection = new HubConnection("http://127.0.0.1:8088/");
var myHub = connection.CreateHubProxy("MyHub");
Console.WriteLine("Enter your name");
string name = Console.ReadLine();
connection.Start().ContinueWith(task => {
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected");
myHub.On<string, string>("addMessage", (s1, s2) => {
Console.WriteLine(s1 + ": " + s2);
});
while (true)
{
Console.WriteLine("Please Enter Message");
string message = Console.ReadLine();
if (string.IsNullOrEmpty(message))
{
break;
}
myHub.Invoke<string>("Send", name, message).ContinueWith(task1 => {
if (task1.IsFaulted)
{
Console.WriteLine("There was an error calling send: {0}", task1.Exception.GetBaseException());
}
else
{
Console.WriteLine(task1.Result);
}
});
}
}
}).Wait();
Console.Read();
connection.Stop();
}
}
}
To build on #dyslexicanaboko's answer for dotnet core, here is a client console application:
Create a helper class:
using System;
using Microsoft.AspNetCore.SignalR.Client;
namespace com.stackoverflow.SignalRClientConsoleApp
{
public class SignalRConnection
{
public async void Start()
{
var url = "http://signalr-server-url/hubname";
var connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect()
.Build();
// receive a message from the hub
connection.On<string, string>("ReceiveMessage", (user, message) => OnReceiveMessage(user, message));
var t = connection.StartAsync();
t.Wait();
// send a message to the hub
await connection.InvokeAsync("SendMessage", "ConsoleApp", "Message from the console app");
}
private void OnReceiveMessage(string user, string message)
{
Console.WriteLine($"{user}: {message}");
}
}
}
Then implement in your console app's entry point:
using System;
namespace com.stackoverflow.SignalRClientConsoleApp
{
class Program
{
static void Main(string[] args)
{
var signalRConnection = new SignalRConnection();
signalRConnection.Start();
Console.Read();
}
}
}
The Self-Host now uses Owin. Checkout http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-signalr-20-self-host to setup the server. It's compatible with the client code above.
This is for dot net core 2.1 - after a lot of trial and error I finally got this to work flawlessly:
var url = "Hub URL goes here";
var connection = new HubConnectionBuilder()
.WithUrl($"{url}")
.WithAutomaticReconnect() //I don't think this is totally required, but can't hurt either
.Build();
//Start the connection
var t = connection.StartAsync();
//Wait for the connection to complete
t.Wait();
//Make your call - but in this case don't wait for a response
//if your goal is to set it and forget it
await connection.InvokeAsync("SendMessage", "User-Server", "Message from the server");
This code is from your typical SignalR poor man's chat client. The problem that I and what seems like a lot of other people have run into is establishing a connection before attempting to send a message to the hub. This is critical, so it is important to wait for the asynchronous task to complete - which means we are making it synchronous by waiting for the task to complete.
I've just started to play around with WebSockets and ASP.NET and have run into a weird issue. I'm building a very primitive ASP.NET 4.5 WebAPI application that is supposed to function as an echo-server like so:
using Microsoft.Web.WebSockets;
// ...
namespace MyControllers
{
internal class EchoHandler : WebSocketHandler
{
public override void OnClose()
{
System.Diagnostics.Debug.Write("Close");
}
public override void OnError()
{
System.Diagnostics.Debug.Write("Error: " + this.Error.ToString());
}
public override void OnOpen()
{
System.Diagnostics.Debug.Write("Open");
}
public override void OnMessage(string message)
{
System.Diagnostics.Debug.Write("Message: " + message);
this.Send("Echo: " + message);
}
}
public class EchoController : ApiController
{
public HttpResponseMessage Get()
{
if (HttpContext.Current.IsWebSocketRequest)
{
HttpContext.Current.AcceptWebSocketRequest(new EchoHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
}
}
I'm connecting to this service using a Windows Store Application written in C#. The relevant code looks like this:
class WebsocketTest
{
private MessageWebSocket webSocket;
private DataWriter messageWriter;
private async Task Connect()
{
var server = new Uri("ws://127.0.0.1:81/");
webSocket = new MessageWebSocket();
webSocket.Control.MessageType = SocketMessageType.Utf8;
webSocket.MessageReceived += messageWebSocket_MessageReceived;
webSocket.Closed += messageWebSocket_Closed;
await webSocket.ConnectAsync(server);
messageWebSocket = webSocket;
messageWriter = new DataWriter(webSocket.OutputStream);
}
private async Task Send(string message)
{
try
{
messageWriter.WriteString(message);
await messageWriter.StoreAsync();
}
catch (Exception ex)
{
var error = WebSocketError.GetStatus(ex.GetBaseException().HResult);
}
}
}
This works well for a while, but after an arbitrary number of messages have been sent back and forth, OnError() is invoked on the server and I get the following exception: "The I/O operation has been aborted because of either a thread exit or an application request" (It's the "this.Send(...)" that seems to be causing it). If I keep sending stuff on the client, I get a "ConnectionAborted" error when calling "dataWriter.StoreAsync()".
The error occurs every time, but it takes a varying number of messages before it does. Using longer messages seems to speed up the process.
For testing, I also tried using plain AspNetWebSockets instead of a WebSocketHandler but with the same outcome.
Any ideas?
Thanks a ton in advance,
Kai
Its a bug (reported by me):
https://connect.microsoft.com/VisualStudio/feedbackdetail/view/976851/server-websocket-closed-abruptly-the-i-o-operation-has-been-aborted-because-of-either-a-thread-exit-or-an-application-request
I have been trying to find a workaround for quite some time without being successful. I'm using the HttpListener but the symptom is the same. Now I have changed implementation to a third party library and the problem seems to have been resolved.