I have implemented web socket server using web API and Microsoft.WebSockets package.
public class MyWSHandler : Microsoft.Web.WebSockets.WebSocketHandler
{
public string _username;
public MyWSHandler(string username)
{
_username = username;
}
public override void OnOpen()
{
System.Diagnostics.Debug.Write("Socket Opened!");
if (!Module._clients.Any(x => ((MyWSHandler)x)._username == this._username)) {
Module._clients.Add(this);
}
Module._clients.Broadcast("User "+this._username+" has connected!");
}
}
I can connect to the server from JavaScript client code. Then, I try to connect to the server from a desktop application using System.Net.WebSockets.WebSocketClient class with C#.
try
{
var websocket = new ClientWebSocket();
await websocket.ConnectAsync(new Uri(#"ws://localhost:5587/api/ws?username=admin"), CancellationToken.None);
await Task.WhenAll(Receive(websocket), Send(websocket));
}
catch(Exception ex){
Console.WriteLine("Exception: {0}", ex);
}
However, the remote server throws an error (404). Is there anything I missed?
Related
I am trying to create a chat application using signalR. Instead of using the javascript client I want to use the dotnet client.
This is my class (refer docs https://learn.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-6.0&tabs=visual-studio)
using System;
using System.Windows;
using EO.WebBrowser;
using EO.WebBrowser.DOM;
using Microsoft.AspNetCore.SignalR.Client;
using System.Threading;
using System.Threading.Tasks;
namespace ChatApp.Hubs
{
public class MainWindow : Window
{
HubConnection connection;
private string URL = "https://localhost:7131/chatHub";
public MainWindow()
{
connection = new HubConnectionBuilder().WithUrl(this.URL).Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
}
public async void EstablistConnection()
{
try
{
await connection.StartAsync();
App.Debugger("test", "TEST", "Client", "Connection", $"Connection started at {DateTime.Now}");
}
catch (Exception ex)
{
App.Debugger("exception", "ERROR", "Client", "Connection", ex.Message);
}
}
public async void OnMessage()
{
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
App.Debugger("test", "TEST", "Client", "Connection", $"{user} sends {message} at {DateTime.Now}");
});
}
public async void Send(string user, string message)
{
try
{
await connection.InvokeAsync("SendMessage",user,"Hello, world");
Console.WriteLine("Message sent!!");
}
catch (Exception ex)
{
App.Debugger("exception", "ERROR", "Client", "Send", ex.Message);
}
}
}
}
On running the application I checked the browser's console and I got the following message:
Information: Normalizing '/chatHub' to
'https://localhost:7137/chatHub'. Information: WebSocket connected to
wss://localhost:7137/chatHub?id=WQQv8OC0imuPlGfoZwwJKQ.
But I got an exception that said
"No connection could be made because the target machine actively
refused it. (localhost:7131)"
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 am trying to set up a WebSocket connection using the .net SignalR and React app as a client to be able to send private messages.
Here is my code on the client side:
const setUpSignalRConnection = async () => {
const connection = new HubConnectionBuilder()
.withUrl("http://localhost:5000/messaginghub")
.build();
setConnection(connection);
connection.on("ReceiveMessage", (message: string) => {
console.log("Recieved Message", message);
setChatMessages((oldArray) => [...oldArray, message]);
});
try {
await connection.start();
} catch (err) {
console.log("Errors", err);
}
return connection;
};
const SendMessage = async () => {
if (connection) {
try {
console.log("sending message");
await connection.send("SendPrivateMessage", user.user.email, message);
} catch (e) {
console.log("Errors sending message", e);
}
} else {
alert("No connection to server yet.");
}
};
and my server side code
public async Task SendPrivateMessage(string userEmail, string message)
{
var RecivingMessageUser = _unitOfWork.UserRepository.GetByEmail(userEmail);
var currUserEmail = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var sender = _unitOfWork.UserRepository.GetByEmail(currUserEmail);
var newMessage = new MessagesDto
{
FromId = sender.UserId,
ToId = RecivingMessageUser.UserId,
MessageBody = message,
SentAt = DateTime.UtcNow,
};
await Clients.Group(userEmail).SendAsync("ReceiveMessage", message);
_unitOfWork.MessagingRepository.Insert(_mapper.Map<MessagesDto, Messages>(newMessage));
_unitOfWork.SaveChanges();
}
public override Task OnConnectedAsync()
{
var groupName = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
Groups.AddToGroupAsync(Context.ConnectionId, groupName);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception ex)
{
Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.FindFirstValue(ClaimTypes.NameIdentifier));
return base.OnDisconnectedAsync(ex);
}
With console.logs I see that I am sending a message once and the message is stored in DB once but somehow on the other end, I am getting two received messages.
I am testing it on my local machine in two separate browsers.
What am I doing wrong?
Which method on your back-end is calling twice?
You are telling your message saved in to the DB once so it shouldn't be the SendPrivateMessage method which is calling towice.
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.