Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconnect issue when connection lost #297

Closed
BajakiGabesz opened this issue Aug 16, 2021 · 11 comments
Closed

Reconnect issue when connection lost #297

BajakiGabesz opened this issue Aug 16, 2021 · 11 comments

Comments

@BajakiGabesz
Copy link
Contributor

Hi,

I use dotNetify in a Xamarin Forms application and I find an issue when the connection has been lost.
In this case, we cannot communicate with the remote view model anymore, meaning you are not able to dispatch any command and not get any updates from the server.
I tried to create my own mechanism that just monitoring the connection and if it's terminated then I try to reconnect:

private void ConnectivityCheck_Tick(object state)
{
    if (ForceReconnectRequested
        || (ServerConnectionState != HubConnectionState.Connected
            && NetworkAccess == NetworkAccess.Internet))
    {
        ((IDisposable)this.dotnetifyHubProxy).Dispose();
        this.dotnetifyHubProxy.StartAsync().ConfigureAwait(false);
        SpinWait.SpinUntil(() => ServerConnectionState != HubConnectionState.Connected, ConstantsSystem.connectionTimeout);
        Reconnected?.Invoke();
        ForceReconnectRequested = false;
    }
}

The page view model looks like this:

public class SomePageViewModel
{
	private IDotNetifyClient? dotnetify;

	public SomePageViewModel()
	{
		ConnectAsync().ConfigureAwait(false);
	}

	~SomePageViewModel()
	{
		Dispose();
	}
	
	public async Task ConnectAsync()
	{
		if (dotnetify != null)
			await dotnetify.DisposeAsync();
				
		this.dotnetify = ViewModelLocator.ResolveService<IDotNetifyClient>();

		await dotnetify.ConnectAsync(
		   "something",
		   this
		);
	}

	protected virtual void OnReconnected()
	{
		ConnectAsync().ConfigureAwait(false);
	}
	
	public override void Dispose()
	{
		dotnetify?.DisposeAsync();
		base.Dispose();
	}
}

The backend side looks like this:

public class Something : RemoteVMBase
{
	public Something()
	{
	}

	public override async Task OnCreatedAsync()
	{
		// Getting data from services here...
		await base.OnCreatedAsync();
	}
}

So, the main problem is, even if I trying to reconnect manually or keep everything as it is, the communication between the client and the backend totally lost, and not able to work with the data anymore. When opening a new ViewModel then also not able to get any data from the server. Maybe I just "overlook" something and you have an idea why is it happening.

What is the right way to handle the situation when the client connection to the server is totally terminated? Should it connect back automatically or do I need to add some extra argument to do so or is there a workaround for this?

Thanks for your kind help!

dsuryd pushed a commit that referenced this issue Aug 18, 2021
@dsuryd
Copy link
Owner

dsuryd commented Aug 18, 2021

I made a PR to address the reconnection issue. Let me know if it works.

@BajakiGabesz
Copy link
Contributor Author

Thanks for your effort, I will check it tonight.
If you don't mind and I find any issues with it that I could recognize, then I could contribute to resolving it.

@BajakiGabesz
Copy link
Contributor Author

I was tried to test it but I got an error message when I just tried to connect to the server:

System.ArgumentNullException: Value cannot be null.
Parameter name: inputType
at System.Text.Json.JsonSerializer.Serialize (System.Text.Json.Utf8JsonWriter writer, System.Object value, System.Type inputType, System.Text.Json.JsonSerializerOptions options) [0x00009] in <3e024d1008104aac9fe12198f4c68344>:0
at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.WriteArguments (System.Object[] arguments, System.Text.Json.Utf8JsonWriter writer) [0x00062] in <98d61c04d1b44672b33d354f73c63eae>:0
at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.WriteInvocationMessage (Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage message, System.Text.Json.Utf8JsonWriter writer) [0x0001f] in <98d61c04d1b44672b33d354f73c63eae>:0
at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.WriteMessageCore (Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Buffers.IBufferWriter1[T] stream) [0x00080] in <98d61c04d1b44672b33d354f73c63eae>:0 at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.WriteMessage (Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Buffers.IBufferWriter1[T] output) [0x00000] in <98d61c04d1b44672b33d354f73c63eae>:0
at Microsoft.AspNetCore.SignalR.Client.HubConnection.SendHubMessage (Microsoft.AspNetCore.SignalR.Client.HubConnection+ConnectionState connectionState, Microsoft.AspNetCore.SignalR.Protocol.HubMessage hubMessage, System.Threading.CancellationToken cancellationToken) [0x00035] in <82f2bec582b048f7a648dfe730be82f1>:0
at Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsyncCore (System.String methodName, System.Object[] args, System.Threading.CancellationToken cancellationToken) [0x00169] in <82f2bec582b048f7a648dfe730be82f1>:0
at System.Threading.Tasks.ForceAsyncAwaiter.GetResult () [0x0000c] in <82f2bec582b048f7a648dfe730be82f1>:0
at Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsync (System.String methodName, System.Object[] args, System.Threading.CancellationToken cancellationToken) [0x00097] in <82f2bec582b048f7a648dfe730be82f1>:0
at DotNetify.Client.DotNetifyHubProxy.Request_VM (System.String vmId, System.Collections.Generic.Dictionary`2[TKey,TValue] vmArg) [0x00052] in /Users/gabor/Documents/Sources/AppName/Dotnetify/DotNetifyLib.SignalR/Client/DotNetifyHubProxy.cs:162
at DotNetify.Client.DotNetifyClient.ConnectAsync (System.String vmId, DotNetify.Client.IViewState viewState, DotNetify.Client.VMConnectOptions options) [0x00227] in /Users/gabor/Documents/Sources/AppName/Dotnetify/DotNetifyLib.Core/Client/DotNetifyClient.cs:153
at DotNetify.Client.DotNetifyClient.ConnectAsync (System.String vmId, System.ComponentModel.INotifyPropertyChanged view, DotNetify.Client.VMConnectOptions options) [0x00042] in /Users/gabor/Documents/Sources/AppName/Dotnetify/DotNetifyLib.Core/Client/DotNetifyClient.cs:121
at AppName.Models.General.RemoteViewModelBase.ConnectAsync () [0x0012a] in /Users/gabor/Documents/Sources/AppName/AppName/AppName/Models/General/RemoteViewModelBase.cs:91

I also updated the server to work with the same version of dotNetify which is in the PR, but it's not working properly anymore. I was on version 4.1.1 right before I moved to this version. Is it possible that I need to do something to be able to work with this workaround what the PR contains?

Side note: I have a React admin page for the same application and it's working with the current version but Xamarin cannot connect anymore.

@dsuryd
Copy link
Owner

dsuryd commented Aug 19, 2021

I'm not seeing the error with the DotNetClient demo. Can you try to reproduce it there? Make sure both client and server are on the same .NET version. You can also try setting the serialization method to Newtonsoft.

@BajakiGabesz
Copy link
Contributor Author

BajakiGabesz commented Aug 21, 2021

Actually, I could only use .NET Standard 2.0 or 2.1 in Xamarin. I tried both but I saw the DotNetifyLib.Core project uses 2.0. So, I just created a small application which just could connect to the server and get a list of objects. The same thing happened. I couldn't test the demo client, because I'm on macOS and not able to build those projects because it's incompatible with my OS.

The problem happens when it's trying to call the ConnectAsync method which never returns. It calls in DotNetifyHubProxy.cs:162 the Request_VM method which calls HubConnection.SendCoreAsync with the desired parameters but it's never returned. While the application runs the ConnectAsync looks like never happens and not returning as well.
I just tried to use Wireshark to dig a bit deeper and I saw the standard communication happens properly, but a bit later in the initialization process, the communication contains the next message:
{"type":7,"error":"Connection closed with an error.","allowReconnect":true}\036
It looks like the sending the request message is also delayed exactly 15 seconds:
{"type":1,"target":"Request_VM","arguments":["vmName"{"type":6}\036
And also this message differs from the React variant:
{"type":1,"target":"Response_VM","arguments":[["vmName"
When monitoring the server part in DotNetifyHub we receive messages from the client when I use React DotNetifyHub.cs:125
But when Xamarin project do the same, it gets never fired.

Is this maybe a SignalR issue?
How can I change the serialization method to Newtonsoft?

I can send you a sample project if you need it, as I already created it, just let me know.

Thanks for your help!

@dsuryd
Copy link
Owner

dsuryd commented Aug 22, 2021

I can take a look if you put a sample project in a public repo. BTW, your client, is it still on version 4? If so, you'll need to upgrade it too. Here's the info on how to switch to Newtonsoft.

@BajakiGabesz
Copy link
Contributor Author

I will check the communication with Newtonsoft as well.
The client and the server also use the latest update from this branch.

@BajakiGabesz
Copy link
Contributor Author

I just created a sample application to be able to test it on your side.
You can find it here.

@dsuryd
Copy link
Owner

dsuryd commented Aug 28, 2021

For some reason, I wasn't able to get your sample application to show up on my android emulator (pixel 3). But I could create a Xamarin client demo from scratch (source code added to PR).

The connection retry logic in the PR is working, but there is a long delay of 2 minutes after a server restart until the Xamarin client re-establish the connection. This does not happen with the desktop client. I reduced all the timeout settings exposed by the SignalR connection API to no effect. So perhaps it's another setting that the SignalR API doesn't expose, or it has something to do with the Android itself. At this point, I feel it's no longer an issue with dotNetify, and you should take it up in the SignalR forum.

@BajakiGabesz
Copy link
Contributor Author

Thank you for your effort. It's really close to that solution what I did on my side. I will check it, but I think we were faced with the same issue, because sometimes I had the feeling that I could reconnect or the SignalR could, but it happened only once when I have tested it.
If you don't mind, I make some tests on my side, I could create the UI side of the Xamarin demo application because in this case, others couldn't use it for an upcoming solution, because they don't see how they can bind it to the UI.
Please give me some time before closing this issue to be able to test it deeper!
Thank you!

dsuryd added a commit that referenced this issue Oct 1, 2021
@dsuryd
Copy link
Owner

dsuryd commented Oct 1, 2021

I'm closing this as the reconnection issue has been addressed with the latest release v5.2. As for the prolonged time it takes for the Xamarin client to re-establish the connection, it doesn't appear to be an issue with dotNetify.

@dsuryd dsuryd closed this as completed Oct 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants