I have implemented SignalR in my Silverlight 5 application and it's working fine as long as the client stays on-line. But as soon as the network connection drops for more than about 5 seconds, it stops functioning and I can't make it reconnect.
When a client loses the network connection, the Hub's event "OnDisconnected" is triggered.
But on the client side the HubConnection's Closed or StateChanged events are not triggered and the ConnectionState remains Connected. It then tries to make a call to hubproxy.Invoke(), but that will not invoke the client-side method as it would if the network connection stayed alive.
I instantiate the signalr client in App.xaml.xs:
private void Application_UserLoaded(LoadUserOperation operation)
{
//Some checks whether user is logged in
_signalRClient = new SignalRClient();
_signalRClient.RunAsync();
}
.
public class SignalRClient
{
public async void RunAsync()
{
SetHubConnection();
SetHubProxy();
await StartHubConnection();
SendTestSignal();
}
private void SetHubConnection()
{
try
{
_hubConnection = new HubConnection("https://10.1.2.3/HubWeb");
}
catch (WebException ex)
{
LoggerManager.WriteLog(LogType.ERROR, ex.ToString());
}
_hubConnection.Closed += () => TimeoutHelper.SetTimeout(5000, () => _hubConnection.Start());
_hubConnection.StateChanged += (change) => LoggerManager.WriteLog(LogType.DEBUG, String.Format("SignalR Client: Connection State Changed from {0} to {1}", change.OldState, change.NewState));
}
I tried to implement automatic reconnect, as the documentation suggests, by handling the client side Closed event and that starting the hubconnection.
But because the ConnectionState is still "Connected", this event is not triggered and I do not see a way to restart the connection from the client.
What could be the cause of the Connectionstate property of client's hubconnection not changing tot "Disconnected" and why is the Closed event not triggered?
Any help appreciated.
Related
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;
Is it's possible to throw an exception from the client to the server?
We have an open stream from the server to the client:
rpc addStream(Request) returns (stream StreamMessage) {}
When i try something like this:
throw Status.INTERNAL.withDescription(e.getMessage()).withCause(e.getCause()).asRuntimeException();
I got the exception in the StreamObserver.onError on the client, but there is no exception on the server-side.
Servers can respond with a "status" that the stub API exposes as a StatusRuntimeException. Clients, however, can only "cancel" the RPC. Servers will not know the source of the cancellation; it could be because the client cancelled or maybe the TCP connection broke.
In a client-streaming or bidi-streaming call, the client can cancel by calling observer.onError() (without ever calling onCompleted()). However, if the client called onCompleted() or the RPC has a unary request, then you need to use ClientCallStreamObserver or Context:
stub.someRpc(request, new ClientResponseObserver<Request, Response>() {
private ClientCallStreamObserver<Request> requestStream;
#Override public void beforeStart(ClientCallStreamObserver<Request> requestStream) {
this.requestStream = requestStream;
}
...
});
// And then where you want to cancel
// RequestStream is non-thread-safe. For unary requests, wait until
// stub.someRpc() returns, since it uses the stream internally.
// The string is not sent to the server. It is just "echoed"
// back to the client's `onError()` to make clear that the
// cancellation was locally caused.
requestStream.cancel("some message for yourself", null);
// For thread-safe cancellation (e.g., for client-streaming)
CancellableContext ctx = Context.current().withCancellation();
StreamObserver requestObserver = ctx.call(() ->
stub.someRpc(new StreamObserver<Response>() {
#Override public void onCompleted() {
// The ctx must be closed when done, to avoid leaks
ctx.cancel(null);
}
#Override public void onError() {
ctx.cancel(null);
}
}));
// The place you want to cancel
ctx.cancel(ex);
I have an infinitely running process that pushes events from a server to subscribed SignalR clients. There may be long periods where no events take place on the server.
Currently, the process all works fine -- for a short period of time-- but eventually, the client stops responding to events pushed by the server. I can see the events taking place on the server-side, but the client becomes unaware of the event. I am assuming this symptom means some timeout period has been reached and the client has unsubscribed from the Hub.
I added some code to reconnect if the connection was dropped, and that has helped, but the client still eventually stops seeing new events. I know there are many different timeout values that can be adjusted, but it's all pretty confusing to me and not sure if I should even be tinkering with them.
try
{
myHubConnection = new HubConnectionBuilder()
.WithUrl(hubURL, HttpTransportType.WebSockets)
.AddMessagePackProtocol()
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
})
.Build();
// Client method that can be called by server
myHubConnection.On<string>("ReceiveInfo", json =>
{
// Action performed when method called by server
pub.ShowInfo(json);
});
try
{
// connect to Hub
await myHubConnection.StartAsync();
msg = "Connected to Hub";
}
catch (Exception ex)
{
appLog.WriteError(ex.Message);
msg = "Error: " + ex.Message;
}
// Reconnect lost Hub connection
myHubConnection.Closed += async (error) =>
{
try
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await myHubConnection.StartAsync();
msg = "Reconnected to Hub";
appLog.WriteWarning(msg);
}
catch (Exception ex)
{
appLog.WriteError(ex.Message);
msg = "Error: " + ex.Message;
}
};
This all works as expected for a while, then stops without errors. Is there something I can do to (1) ensure the client NEVER unsubscribes, and (2) if the connection is lost (network outage for example) ensures the client resubscribes to the events. This client must NEVER timeout or give up trying to reconnect if required.
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.
I have setup a SignalR hub which has the following method:
public void SomeFunction(int SomeID)
{
try
{
Thread.Sleep(600000);
Clients.Caller.sendComplete("Complete");
}
catch (Exception ex)
{
// Exception Handling
}
finally
{
// Some Actions
}
m_Logger.Trace("*****Trying To Exit*****");
}
The issue I am having is that SignalR initiates and defaults to Server Sent Events and then hangs. Even though the function/method exits minutes later (10 minutes) the method is initiated again ( > 3 minutes) even when the sendComplete and hub.stop() methods are initiated/called on the client prior. Should the user stay on the page the initial "/send?" request stays open indefinitely. Any assistance is greatly appreciated.
To avoid blocking the method for so long, you could use a Taskand call the client method asynchronously.
public void SomeFunction(Int32 id)
{
var connectionId = this.Context.ConnectionId;
Task.Delay(600000).ContinueWith(t =>
{
var message = String.Format("The operation has completed. The ID was: {0}.", id);
var context = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
context.Clients.Client(connectionId).SendComplete(message);
});
}
Hubs are created when request arrives and destroyed after response is sent down the wire, so in the continuation task, you need to create a new context for yourself to be able to work with a client by their connection identifier, since the original hub instance will no longer be around to provide you with the Clients method.
Also note that you can leverage the nicer syntax that uses async and await keywords for describing asynchronous program flow. See examples at The ASP.NET Site's SignalR Hubs API Guide.