Not logged in. · Lost password · Register
Forum: MatriX and XmppDotNet RSS
Avatar
lwgga #1
Member since Apr 2018 · 3 posts
Group memberships: Members
Show profile · Link to this post
Subject: Unable to reconnect after forcible disconnect - Most likely a DotNetty issue.
Hi,

First let me say thank you for such a complete implementation of XMPP in .NET.  I've been playing with using MatriX.vNext to connect and "listen" to events on a Cisco Finesse server.  I was hoping to wrap my implementation inside of a "set it and forget it" Windows service.  Upon testing it's ability to respond to "adverse network conditions" (I unplugged my Ethernet cable which triggered the connected socket to be "forcibly closed") I found that attempting to call ConnectAsync a second time results in a never-ending wait for the socket to reconnect.  Even plugging the network back in does not allow the application to continue.  It is essentially "dead in the water."

I did go to the GitHub project for DotNetty and look into this issue and found the following issue already opened by another:
https://github.com/Azure/DotNetty/issues/245

I also added a comment to the end of the chain explaining that the recommendation of adding a ChannelHandler to catch the ChannelInactive or ExceptionCaught events and attempting to reconnect within does not work either.  The same never-ending wait appears to occur which is what leads me to believe this is a DotNetty issue and not a MatriX.vNext issue.

Has anyone else experienced this?  This is a real show stopper for me in that I cannot write a reliable service that will simply re-connect upon a temporary network issue and the service will require a manual (most likely system administrator initiated) restart to correct.
Avatar
Alex #2
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
reconnect should not be an issue.

  • You have to watch the XmppSessionStateObserver for disconnects
  • When you detect a disconnect then initaite a connection again with ConnectAsync

As of now there is nothing build in becuase different applications require different reconnect logic. But the reconnect logic can be accomplished only with some lines of code.
When you need more complex reconnect logic you can implement you can implement your own retry pattern with exponential backoff and circuit breaker patterns. Or use a library like Polly for this.
Avatar
lwgga #3
Member since Apr 2018 · 3 posts
Group memberships: Members
Show profile · Link to this post
Hi Alex,

I actually implemented exactly what you have suggested using the XmppSessionStateObserver as well as my own exponential (multiply by 2) backoff with respect to connecting.  The call to "ConnectAsync()" is never returning.


My code is very similar to the following:

  1. class MyClient : IDisposable
  2. {
  3.  
  4.     [Matrix.Attributes.Name("MyUpdate-Handler")]
  5.     public class XmppMyUpdateHandler : XmppStanzaHandler
  6.     {
  7.         public XmppMyUpdateHandler()
  8.         {
  9.             Handle(el => el.OfType<Message>()
  10.                     && null!=el.Cast<Message>().Element<
  11.                         Matrix.Xmpp.PubSub.Event.Event>(),
  12.             (context, xmppXElement) =>
  13.             {
  14.                 var evt = xmppXElement.Cast<Message>()
  15.                     .Element<Matrix.Xmpp.PubSub.Event.Event>();
  16.                 Console.WriteLine("Event received: {0}",
  17.                     evt.ToString());
  18.             });
  19.         }
  20.     }
  21.  
  22.     protected XmppClient xmppClient = null;
  23.     private bool disposed = false;
  24.  
  25.  
  26.     private XmppClient GetXmppClient()
  27.     {
  28.         return new XmppClient(
  29.                 new Action<IChannelPipeline>(pipeline =>
  30.                 {
  31.                     pipeline.AddBefore<XmppStanzaHandler>(
  32.                         new XmppMyUpdateHandler());
  33.                 }))
  34.             {
  35.                 Username = "user",
  36.                 Password = "password",
  37.                 XmppDomain = "domain.tld",
  38.                 Resource = "MyClient",
  39.                 Port = 1234,
  40.             };
  41.     }
  42.  
  43.     public MyClient()
  44.     {
  45.         this.xmppClient = GetXmppClient();
  46.     }
  47.  
  48.     private void ConnectXMPPClient()
  49.     {
  50.         if (null==xmppClient)
  51.             throw new ApplicationException(
  52.                 "Unable to start the XMPP client.  Most likely due " +
  53.                 "to a problem reading the XMPP client configuration.");
  54.  
  55.         int retryWait = 2000; //milliseconds
  56.  
  57.         while(true)
  58.         {
  59.             try
  60.             {
  61.                 if (xmppClient.ConnectAsync().Result.Active)
  62.                     break;
  63.             }
  64.             catch(AuthenticationException ex)
  65.             {
  66.                 log.Fatal(ex,
  67.                     "Failed to authenticate with the XMPP server: " +
  68.                     ex.Message);
  69.                 throw ex;
  70.             }
  71.             catch(Exception ex)
  72.             {
  73.                 log.Error(ex,"XMPP Connect Failure: " + ex.Message);
  74.             }
  75.  
  76.             log.Error("Failed to connect.  Retrying in {0} seconds",
  77.                 retryWait/1000);
  78.                
  79.             Thread.Sleep(retryWait);
  80.             retryWait = (retryWait<64000) ? retryWait*2 : retryWait;
  81.         }
  82.  
  83.         xmppClient.XmppXElementStreamObserver
  84.             .Where(el => el.OfType<Message>()
  85.                 && MessageType.Normal==el.Cast<Message>().Type)
  86.             .Subscribe(m =>
  87.             {
  88.                 MessageObserver(m.Cast<Message>());
  89.             });
  90.     }
  91.  
  92.     private void DisconnectXMPPClient()
  93.     {
  94.         try
  95.         {
  96.                 xmppClient.DisconnectAsync(true,2000).Wait();
  97.         }
  98.         catch(Exception e)
  99.         {
  100.             log.Info(e,"XMPPClient Disconnect failed.");
  101.         }
  102.  
  103.         try
  104.         {
  105.             xmppClient.Dispose();
  106.         }
  107.         catch(Exception e)
  108.         {
  109.             log.Info(e,"XMPPClient Dispose failed.");
  110.         }
  111.     }
  112.  
  113.     private void SetupSubscriptions()
  114.     {
  115.         xmppClient.XmppSessionStateObserver.Subscribe(ss =>
  116.         {
  117.             StateObserver(ss);
  118.         });
  119.  
  120.  
  121.         // Set my "presence" to Available.
  122.         xmppClient.SendAsync<Presence>(
  123.             new Presence(PresenceType.Available) { });
  124.     }
  125.  
  126.     public void Start()
  127.     {
  128.         ConnectXMPPClient();
  129.         SetupSubscriptions();
  130.     }
  131.  
  132.  
  133.     protected void MessageObserver(Message msg)
  134.     {
  135.         log.Debug("Message received: {0}", msg.ToString());
  136.     }
  137.  
  138.     protected void StateObserver(SessionState ss)
  139.     {
  140.         if (!disposed && SessionState.Disconnected==ss)
  141.         {
  142.             log.Warn("Session disconnected. Attempting to reconnect.");
  143.             ConnectXMPPClient();
  144.         }
  145.         log.Info("SessionState: {0}", ss.ToString());
  146.     }
  147.  
  148.     public void Dispose()
  149.     {
  150.         Dispose(true);
  151.         GC.SuppressFinalize(this);
  152.     }
  153.  
  154.     protected virtual void Dispose(bool disposing)
  155.     {
  156.         if (disposed) return;
  157.  
  158.         if (disposing)
  159.         {
  160.             disposed = true;
  161.             if (null!=xmppClient)
  162.             {
  163.                 DisconnectXMPPClient();
  164.             }
  165.         }
  166.     }
  167. }

The call to ConnectAsync() within the IF statement specifically is where nothing happens:
if (xmppClient.ConnectAsync().Result.Active)


I can see the call made to xmppClient.ConnectAsync() stepping through execution within the debugger.  However, once I go to "step next" on that statement the debugger waits indefinitely.


I'm really leaning on the culprit of this issue being DotNetty and not MatriX.vNext.  I just figured I would make you aware.

Hopefully I haven't done something grossly in error.

Thanks for the fast reply!
This post was edited on 2018-04-30, 18:18 by lwgga.
Avatar
Alex #4
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
I would help when you could get some logs as described here and attach them:
https://matrix-xmpp.io/docs/logging

  • Have you tried to dispose your XmppClient and create a new one?
  • Have you tried to use a timer for your reconnect? There is some cleanup required, and you should not try to reconnect immediately after getting the disconnect.
Avatar
lwgga #5
Member since Apr 2018 · 3 posts
Group memberships: Members
Show profile · Link to this post
I didn't think to delay before the reconnect.  I did try disposing and re-creating my xmppClient object and I was getting the hang upon calling xmppClient.Dispose(). 

I'll try delaying prior to the reconnect attempt and I'll try disposing once again.

Thank you again for your replies and help!
Close Smaller – Larger + Reply to this post:
Verification code: VeriCode Please enter the word from the image into the text field below. (Type the letters only, lower case is okay.)
Smileys: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Special characters: