How to get something from server using SignalR - c#

I need to create method, which can request to server and return answer. But if I use SignalR, I can run server's method and server will run client's method. But how I can create something like this template?
public Response Request()
{
//???
}

if I use SignalR, I can run server's method and server will run client's method.
If you'd like to invoke hub method from .NET client application in c#, you can refer to the following code snippet.
try
{
HubConnection connection = new HubConnectionBuilder()
.WithUrl("http://localhost:61262/chatHub")
.Build();
await connection.StartAsync();
var mes = "hello";
await connection.InvokeAsync("SendMessage", "Consloe Client", mes);
// await connection.StopAsync();
}
catch (Exception ex)
{
Console.WriteLine("Can not communicate with server now, please retry later.");
}
Hub method
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Besides, to setup Hub and client, please refer to the following articles.
Use hubs in SignalR for ASP.NET Core
ASP.NET Core SignalR .NET Client

Related

How to wait for all clients to answer to a request?

I have an ASP.NET core WebApi project which also uses SignalR to communicate with clients. This app has an action that is called by a third-party service and requires that all clients currently connected should send some info back.
The SignalR infrastructure is already being used between the server and clients, so I added this particular action:
public async Task<ActionResult> GetClientInfo()
{
await hubContext.Clients.All.GetClientInfo();
//var infos...
return Ok(infos);
}
So basically, this is what should happen:
The third-party service calls the action
The server asks all clients to send their info
The server returns OK with all the client info
Is it possible to somehow wait and make sure that all clients sent their info before returning OK?
I implemented the code as suggested like this:
public async Task<ActionResult> GetClientInfo()
{
try
{
var tasks = new List<Task<IEnumerable<ClientInfo>>>();
foreach (var client in cache.Clients.Values)
{
tasks.Add(Task.Run(async () =>
{
return await hubContext.Clients.Client(client.Id).GetClientInfo();
}));
}
var list = await Task.WhenAll(tasks);
return Ok(list);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
cache is MemoryCache implementation that is available throughout the whole app and is based on code similar to this SO answer.

Client IP Listing and Blocking in SignalR

In Asp.net (core) .net 5, on the SignalR side, how can we list (receive) client IPs and block or allow certain IP's connections and requests by IP?
In my research, I saw that only Azure SingalR was mentioned.
How can we tell SignalR Hub to request Client IPs and accept (whitelist) or block (blacklist) public connection requests from specific IPs by coding in the application?
Is it possible ?
In SignalR you can get the ip of the user with the HttpContext.
public class AppHub : Hub
{
public string Ip => Context.GetHttpContext().Connection.RemoteIpAddress.ToString();
public override async Task OnConnectedAsync()
{
if (Ip == "::1")
{
await Clients.All.SendAsync("ReceiveMessage", "Server", "Welcome to the server!");
}
else
{
throw new Exception("You are not allowed to connect to this server!");
}
await base.OnConnectedAsync();
}
}
Another solution would be to create a hub filter wich you can find the documentation here :
https://learn.microsoft.com/en-us/aspnet/core/signalr/hub-filters?view=aspnetcore-6.0

Not able to connect with broker using MQTTNet library .net core

I need an help, I can't connect with the broker.
I'm using MQTTNet library into my api project .net core
this is my code:
// GET: api/<SendCommandController>
[HttpGet]
public void Get()
{
var options = new MqttClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com", 1883)
.Build();
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
mqttClient.ConnectAsync(options, CancellationToken.None);
var message = new MqttApplicationMessageBuilder()
.WithTopic("Test/Mqtt")
.WithPayload("Hello World")
.WithExactlyOnceQoS()
.WithRetainFlag()
.Build();
mqttClient.PublishAsync(message, CancellationToken.None);
}
so I follow the tutorial but can't connect to broker hivemq and I can't connect to my personal broker.
So, I tested hivemq broker with mqtt.fx and works.
Only in the code the return is connected = false.
Any ideas? the error is "the client is not connected"
C# is not a language I've done much with, but I assume you are missing an await before mqttClient.ConnectAsync(options, CancellationToken.None); so the rest of the code waits for the connection to complete before trying to send the message

Can't connect secured Asp.Net Core Web Socket hosted on Azure Web App (using TLS)

I spent a whole day searching for a solution but I didn't solve my problem. As you can see from the title, I implemented a basic Web Socket using Asp.Net Core (3.1) framework and I deployed it on Azure (on a Web App service). I successfully make it works without the TLS protocol (so I think the ws has been configured in a good manner) but when I try to connect using wss I receive this errors on the client side:
System.Net.WebSockets.WebSocketException : Unable to connect to the remote server
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
I tried to switch the "HTTPS Only" trigger on azure portal but it keep refusing any client to connect.
Do you have any idea how to let wss works with Azure Web App? Do I need to configure a certificate? I read that azure provide a certificate if the user didn't have one. Thanks and regards
UPDATE: the code has been "copied" from this great Les Jackson's tutorial and is available on git hub at this precise address. The important part of the code is here:
/* THIS IS THE SERVER PART WHERE THE CONNECTION IS ACCEPTED */
namespace WebSocketServer.Middleware
{
public class WebSocketServerMiddleware
{
private readonly RequestDelegate _next;
private WebSocketServerConnectionManager _manager;
public WebSocketServerMiddleware(RequestDelegate next, WebSocketServerConnectionManager manager)
{
_next = next;
_manager = manager;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Receive(webSocket, async (result, buffer) =>
{
if (result.MessageType == WebSocketMessageType.Text)
{
Console.WriteLine($"Receive->Text");
return;
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await sock.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
});
}
else
{
await _next(context);
}
}
}
}
/* THIS IS THE STARTUP FILE*/
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketServerConnectionManager();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseWebSockets();
app.UseWebSocketServer();
}
}
/* THIS IS THE CLIENT (WRITTEN IN NET FULLFRAMEWORK) */
Console.Write("Connecting....");
var cts = new CancellationTokenSource();
var socket = new ClientWebSocket();
string wsUri = "wss://testingwebsocket123123.azurewebsites.net";
await socket.ConnectAsync(new Uri(wsUri), cts.Token);
Console.WriteLine(socket.State);
await Task.Factory.StartNew(
async () =>
{
var rcvBytes = new byte[1024 * 1024];
var rcvBuffer = new ArraySegment<byte>(rcvBytes);
while (true)
{
WebSocketReceiveResult rcvResult = await socket.ReceiveAsync(rcvBuffer, cts.Token);
byte[] msgBytes = rcvBuffer.Skip(rcvBuffer.Offset).Take(rcvResult.Count).ToArray();
string rcvMsg = Encoding.UTF8.GetString(msgBytes);
Console.WriteLine("Received: {0}", rcvMsg);
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Thank you for reading
As disussed over the comments, it works fine with the provided javascript client in the sample. The error in .net client happens because of TLS version when you connect from c# client with full framework. The screenshot of your Azure web app enforces min TLS 1.2. Set that in the .net client like below:
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

AWS Api Gateway Web Socket Post to $default forbidden

I'm trying to get my lambda to post to my aws web socket api and I am getting a Forbidden error message on the post. I have the following setup:
A public api configured using proxy+ to point to a lambda that handles all my routes.
A public web socket api that calls the above lambda.
I am testing out connectivity and my lambda correctly receives the $connect, $default and $disconnect events from the web socket api. I am trying to send a test message back to the client on open but receive the Forbidden error on the Post command.
My code is here:
public async Task<ITrWebsocketsContext> SendMessageAsync(UserDefinition userDefinition, string message)
{
var connectionIds = GetConnectionIds(userDefinition);
if (connectionIds != null)
{
foreach (var connectionId in connectionIds)
{
try
{
Console.WriteLine($"Sending to {connectionId}");
var response = await PostAsync($"https://{suppliedServiceUrl}/#connections/{connectionId}", message);
Console.WriteLine($"Sent to {connectionId}: {response}");
}
catch (ForbiddenException ex)
{
Console.WriteLine($"Exception: {SerialisableException.Serialise(ex)}");
}
catch (AmazonServiceException ex)
{
Console.WriteLine($"Exception: {SerialisableException.Serialise(ex)}");
RemoveConnection(connectionId);
}
catch (Exception ex)
{
throw;
}
}
}
return this;
}
The call to PostAsync fails.
I think this is a VPC issue as the Lambda is behind a VPC. The VPC is configured with 2 private subnets and 1 public one, with an Internet Gateway. The lambda can access the internet, so the VPC seems to be setup correctly. I also have an endpoint pointing to execute api in my region.
Any ideas on where to go? Do I need a VPC link configured for my lambda to connect to my AWS web sockets api?

Categories