Search code examples
xamarinxamarin.formssignalrsignalr.clientasp.net-core-signalr

SignalR Connection Timeout in Xamarin Forms


I implemented SignalR in my Xamarin Forms app and I'm getting the connection time out error.

I'm using SignalR for two things in the app. First is a typical chat feature and the second is to notify user of important data changes in the backend triggered by other users interacting with current user.

I implemented all SignalR related methods in a service class -- see below:

public class MySignalRService : IMySignalRService
{
   private readonly url = "https://example.com/myhub";
   HubConnection _connection;

   public async Task Connect()
   {
      var accessToken = SecureStorage.GetAsync("access_token").Result;

      _connection = new HubConnectionBuilder()
         .WithUrl(_url, options =>
         {
             options.AccessTokenProvider = () => Task.FromResult(accessToken);
         })
         .Build();

      await _connection.StartAsync();

      _connection.On<string>("ReceiveMessage", async (message) =>
      {
          await UpdateChat(message);
      });

      _connection.On<string>("ReceiveDataUpdate", async (data) =>
      {
          await UpdateUserData(data);
      });
   }

   private async Task UpdateChat(message)
   {
      // Handle message
   }

   private async Task UpdateUserData(data)
   {
      // Handle data update
   }

   public async Task Disconnect()
   {
       await _connection.DisposeAsync();
   }
}

I then call the Connect() method in OnStart() and OnResume() methods in code behind for App.xaml. I also call the Disconnect() method in OnSleep() method.

My idea was to keep the connection open while my app is active on user's device but this still means periods of inactivity.

I have two questions:

  1. I understand SignalR doesn't want to sit idle. Is the idea with SignalR to connect to the hub EXACTLY when the user needs the feature and disconnect as soon as he's done? If so, how do I make user aware of updates? Am I to set up some type of long polling myself to wake up SignalR every so often which seems to go against the idea of using it in the first place?
  2. I'd also appreciate it if those who've implemented SignalR in Xamarin Forms would let me know if the service approach I took is not how I need to implement this in my app.

Solution

  • please consider the suggestion to help you understand to begin trouble shooting/isolating the issue, there are three types of disconnects.

    Understanding connection, lifetime & disconnections in the below cases will narrow down your problem, at least where is the disconnect happening.

    1. Transport disconnection
    2. Client disconnection
    3. Server disconnection

    Option 1: AutomaticReconnect

     _yourHubConnection = new HubConnectionBuilder()                 
        .WithUrl(NavigationManager.ToAbsoluteUri($"/{HubMethods.SomeResultHub.my}"))
        .WithAutomaticReconnect()  // did you try this option
        .Build();
             
      _yourHubConnection.ServerTimeout = TimeSpan.FromHours(8); // Hrs 24/8 etc
    

    MS signarlR Reference Reconnect


    Finding out the reason for disconnect using code, per Microsoft ref:

    public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
    {
        if (stopCalled)
        {
            Console.WriteLine(String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId));
        }
        else
        {
            Console.WriteLine(String.Format("Client {0} timed out .", Context.ConnectionId));
        }
                
        return base.OnDisconnected(stopCalled);
    }
    

    Armed with these two

    your Question 1/Answer

    ...If so, how do I make user aware of updates?

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // wire up your custom function to notify user.
    });
    
    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // wire up your custom function let them know its slow.
    });
    

    Question 2: on Xamarin / Android

    On mobile/Android (ios is pain though) you will need a Foreground Service running, which aids the app in keeping the connection alive. Alternatively like your original idea, long polling is another good option.

    For e.g

    [Service]
    public class MyUserChatForegroundService : Service
    {    
        // implement your foreground service to keep the connection alive.
        ...
    }
    

    Wherever you start your Activity with Intent wire this up, and lastly your Service first,

    invoke StartForeground using OnStartCommand override, otherwise the service will just get killed.

    var intent = new Intent(this, typeof(MyUserChatForegroundService ));
    StartForegroundService(intent);
    

    Intent & a Full reference Xamarin Signalr Sample

    Intent

    Hope this helps.