SignalR .Net Client can't establish conection (404 not found) - asp.net

I have a solution in which i am using SignalR. There is a Hub in one of the projects and SignalR.Client in the others that are connecting to that Hub.
This solution is hosted on two servers, and I have a strange problem. In one server everything works fine, but in the other i get an 404 not found error when I am trying to establish the connection from the SignalR.Client.
Hub Code:
public class GlobalHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
public void Notify(string user,NotificationViewModel model)
{
Clients.Group(user).notify(model);
}
public override System.Threading.Tasks.Task OnConnected()
{
string name = Env.UserId().ToString();
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
Global.asax Hub Map:
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
EnableJavaScriptProxies = true
};
RouteTable.Routes.MapHubs("/signalr",hubConfiguration);
The connection attempt:
string portal = CommonHelper.GetPortalUrl("user");
if(portal.Contains(":50150"))
{
portal = portal.Replace(":50150", "");
}
var connection = new HubConnection(portal+"signalr",false);
IHubProxy myHub = connection.CreateHubProxy("GlobalHub");
connection.Start().Wait();
myHub.Invoke("Notify", userID.ToString(), result2);
I am pretty sure that my connection url is correct, I checked it 50 times.
Error occurs on this line:
onnection.Start().Wait();
SS of Error:
Thanks

The problem may be that when hosting a SignalR project on two servers, clients connected to one can only be connected to other clients connected to the same server. That is because SignalR doesn't automatically broadcast a message through all servers.
http://www.asp.net/signalr/overview/performance/scaleout-in-signalr
Try have a look here and I hope it is helpful to you. One of the proposed solution is to use the Redis Pub/Sub (http://redis.io/topics/pubsub) solution, or Azure Service Bus (http://azure.microsoft.com/en-us/services/service-bus/) - both of whom are use as a backplane (when a server receives a message, it is broadcast to all of them and the one that needs it can use it).

Thanks I solved the problem. It turned out that it was a problem with the server. The server itself couldnt recognize that url (throws 404 on the url).
After that problem was fixed, SignalR started working

Related

Signalr core client - call server methode from Razor component

How can I send a message to my server from, let's say a Razor component?
The situation:
I have a working SignalR (Core) connection with my server.
My client code:
public class StartMySignalR
{
HubConnection connection;
public async void StartSignalRHub()
{
connection = new HubConnectionBuilder()
.WithUrl(new Uri("https://myurl.my/LogOnHub"))
.WithAutomaticReconnect()
.Build();
connection.On<string>("ReceiveMessage", (message) =>
{
//Do some stuff
ii.InsertIntoLog("INFO", "SignalR ID = " + message);
});
//Start SignalR client
await connection.StartAsync();
//Send message to server (test connection).
await connection.InvokeAsync("WelcomeMessage", connection.ConnectionId);
I send a test message to my server, that works fine. I can also send a message back from my server,.. so far so good. But now I want to do that from a Razor component in my OnInitializedAsync() Task. So when my page loads, the test message is sent to my server. There I am stuck. When I try to send the message from my Razor component I receive an error (System.NullReferenceExeption - Object reference not set to an instance of an object) -> connection was null error.
Can somebody put me in the right direction?
When I set the Hubconnection to static it works.
public static HubConnection connection;

Can you run a asp.net core 3.0 gRPC CLIENT in IIS? (possibly on Azure?)

I've read a lot of conflicting information about this and it seems people are not 100% clear on what is possible and what is not. I am certain that you cannot host a gRPC server app in IIS due to the HTTP/2 limitations. The documentation is pretty clear. However, I want to use IIS as a reverse proxy, with the internal side communicating using gRPC. So the client would be in IIS, not the server. I assumed that since the communication at this point (i.e. the back end) was not funneled through IIS, there would be no issue with this. However, I keep seeing mixed answers.
I have created a dumb webapp that is hosted in IIS Express and can successfully post to my service running on Kestrel with gRPC.
Client code sample below. The SubmitButton is just a form post on the razor page.
public async void OnPostSubmitButton()
{
// The port number(5001) must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Server code is the boilerplate template for gRPC but looks like this:
namespace grpcGreeter
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
namespace grpcGreeter
{
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
}
}
This works. But, because I keep seeing mixed information saying it that it won't, I am not certain that once I go to deploy the client code (i.e. the reverse proxy), if I will run into problems. I would like to use a host like Azure...but don't know if it's possible or not.
Any clarity on the subject would be greatly appreciated.
As far as I know, we could use asp.net core mvc or razor page application as the client to call the grpc server.
But gRPC client requires the service to have a trusted certificate when you hosted the application on remote server IIS.
If you don't have the permission to install the certificate, you should uses HttpClientHandler.ServerCertificateCustomValidationCallback to allow calls without a trusted certificate.
Notice: this will make the call not security.
Additional configuration is required to call insecure gRPC services with the .NET Core client. The gRPC client must set the System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport switch to true and use http in the server address.
Code as below:
AppContext.SetSwitch(
"System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var httpClientHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpClientHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress("https://localhost:5001",
new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });

SignalR .NET client not firing events when network connection is lost

I have WPF application that connects to WebAPI, which runs SignalR. Everything works fine, until Internet connection is lost by client.
When it happens, SignalR does not fire any events on client side (StateChanged, Error, Reconnecting, Closed etc.)
Code is pretty straightforward
public HubConnection _hubConnection;
IHubProxy _proxy;
public async Task ConnectToHub(string hubUrl, string hubName)
{
_hubConnection = new HubConnection(HubURL);
_hubConnection.Reconnecting += hubConnection_Reconnecting;
_hubConnection.Closed += _hubConnection_Closed;
_hubConnection.StateChanged += _hubConnection_StateChanged;
proxy = hubConnection.CreateHubProxy(hubName);
await _hubConnection.Start();
}
void _hubConnection_StateChanged(Microsoft.AspNet.SignalR.Client.StateChange obj)
{
throw new NotImplementedException();
}
void _hubConnection_Closed()
{
throw new NotImplementedException();
}
void _hubConnection_Reconnectig()
{
throw new NotImplementedException();
}
SignalR version 2.2.0
Thanks for help
Try subscribing to the Error event. Depending on "how" the connection is lost, I don't think some of the other events will get fired.
_hubConnection.Error += (e=>{ ... });
Transport type on SignalR was set automatically to ServerSentEvents instead of WebSockets (Server admin didn't turn it on). Turned out that only with Websockets we can get connection-related events on .Net-client, when connection is lost.
According to http://www.asp.net/signalr/overview/getting-started/introduction-to-signalr
WebSocket is the only transport that establishes a true persistent, two-way connection between client and server.

SignalR Error when renaming hub

I am encountering an issue when attempting to rename a SignalR hub. Please note that I'm not using generated proxies with SignalR.
I have a SignalR Hub that was previously defined as:
[HubName("WidgetHub")
public WidgetHub : Hub
{
...
}
Creating the proxy on the client is done using this code:
this.proxy = connection.createHubProxy('WidgetHub');
this.proxy.on('WidgetUpdated', function() {
$rootScope.$emit('refreshWidget');
});
return connection.start().then(function(connectionObj) {
return connectionObj;
}, function(error) {
console.log(error)
return error.message;
});
};
This is working correctly, however, I now want to rename the hub. I figured I could easily do this by specifying a new name in the [HubName()] attribute:
[HubName("CloudHub")
public WidgetHub : Hub
{
...
}
And the updating the client proxy creation to reference the new hub name:
this.proxy = connection.createHubProxy('CloudHub');
However, when I do this, the client connection errors out with the following message being return:
Error: SignalR: Connection must be started before data can be sent.
Call .start() before .send()
I suspected that I have a caching issue somewhere, because even after specifying the new hub name, if I reference the old hub name, it works correctly.
I have tried cleaning up IIS and all the browser caches, but to no avail. Is there something else that I'm missing here on why changing the HubName attribute is not working for me?
try to make your hubName as follows
[HubName("cloudHub")]
public WidgetHub : Hub
{
...
}
and create proxy like
this.proxy = connection.createHubProxy('cloudHub');
then try to start hub by calling method Start();
$.connection.hub.start().done(function () {
....
....
});

How to set http cookies (headers) in HTTP request

I'm trying to set up a WCF service hosted in IIS in ASP.Net compatibility mode that is protected via Forms Authentication and accessed via a .Net User Control in IE. (see Secure IIS hosted WCF service for access via IE hosted user control).
The User Control in IE is needed because it uses a specific third-party control for which there doesn't exist anything comparable in Silverlight or AJAX.
So I need the UserControl to set the authentication and session id cookies in the http request headers before it accesses the WCF service. My approach is to set up a Message inspector that does this.
So I've defined the Message Inspector:
public class CookieInspector : IClientMessageInspector {
public void AfterReceiveReply(ref Message reply, object correlationState) {
}
public object BeforeSendRequest(
ref Message request,
IClientChannel channel) {
HttpRequestMessageProperty messageProperty;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) {
messageProperty = (HttpRequestMessageProperty) request.Properties[
HttpRequestMessageProperty.Name
];
}
else {
messageProperty = new HttpRequestMessageProperty();
request.Properties.Add(
HttpRequestMessageProperty.Name,
messageProperty
);
}
// Set test headers for now...
messageProperty.Headers.Add(HttpRequestHeader.Cookie, "Bob=Great");
messageProperty.Headers.Add("x-chris", "Beard");
return null;
}
}
and an Endpoint behaviour:
public class CookieBehavior : IEndpointBehavior {
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) {
}
public void ApplyClientBehavior(
ServiceEndpoint endpoint,
ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new CookieInspector());
}
public void ApplyDispatchBehavior(
ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) {
}
public void Validate(ServiceEndpoint endpoint) {
}
}
and I configure and create my channel and WCF client in code:
var ea = new EndpointAddress("http://.../MyService.svc");
// EDIT: Http cookies can't be set with WSHttpBinding :-(
// var binding = WSHttpBinding();
var binding = new BasicHttpBinding();
// Disable automatically managed cookies (which enables user cookies)
binding.AllowCookies = false;
binding.MaxReceivedMessageSize = 5000000;
binding.ReaderQuotas.MaxStringContentLength = 5000000;
var cf = new ChannelFactory<ITranslationServices>(binding, ea);
cf.Endpoint.Behaviors.Add(new CookieBehavior());
ITranslationServices service = cf.CreateChannel();
However when I look at my request with Fiddler, the http header and cookie aren't set, and I have no clue why. I've read various articles on the Net, Stackoverflow etc that basically say that it should work, but it doesn't. Either I'm missing something obvious, or there's a bug in WCF or something else?
Well I figured it out, if I use a basicHttpBinding instead of a WSHttpBinding it works. No idea why though...
WSHttpBinding may be composed of more than one physical message to one logical message. So when successive physical calls are made, they may not be carrying the cookie appropriately

Resources