Not logged in. · Lost password · Register
Forum: MatriX RSS
Avatar
Glassmaker #1
Member since Aug 2013 · 8 posts
Group memberships: Members
Show profile · Link to this post
Subject: Adverse internet connection conditions
In an application that is supposed to properly work under lossy and slow internet connections (e.g. mobile EDGE, 3G), following issues arise:

1. If it is critical to be sure that the stream is healthy, the StreamActive property of XmppClient is not of much help. E.g., if user turns on the "Airplane mode", StreamActive is still true in spite of client's inability to send or retrieve anything from the server. This particular problem may be mitigated with WinRT's NetworkInformation.GetInternetConnectionProfile(); but in general, it seems like there are no reliable routines to check health of the stream in Matrix.

2. Hence, an app needs to manually watch for DateTime of last received stanza, perform pings from time to time, etc.. However, when the app is through some other means is sure that the connectivity is lost, it is not always easy to re-establish it. When connection is absent, a call to XmppClient.Close() will not result in an instant abortion of the stream. The OnClosed event may be fired about 10-15 seconds after a call to Close. Furthermore, a call to Open before OnClosed often results in unpredictable behavior, leading sometimes to complete inability of an XmppClient to re-establish the connection. It would be helpful to have an additional Abort method, that would instantly (or nearly-so) close the connection, clean-up XmppClient's inner state and allow for its re-establishment.

3. Suppose a connection check is required before sending particular stanza. To achieve this, app sends a ping and expects a pong. However, when the connection is busy with downloading data, e.g. when the server writes a heavy IQ result to the stream, a pong stanza may not come in minutes. It would be helpful to have a DateTime of last received chunk of bytes from the server, so that the app could knew that the stream is active and healthy.

Ideally, something like a following API would be rather helpful:

await xmppClient.OpenAsync(); // await finishes when first bytes are received from the server
bool isConnected = await xmppClient.IsConnectedAsync(); // returns true if XmppClient is sure that the stream is healthy

There, IsConnectedAsync would ideally perform a ping if there were no bytes received from the server in a given time period (configurable).

I've implemented something like this at the app level; however, due to Matrix's hiding of the inner socket workings, this solution has many downsides and may not be entirely reliable. Workarounds with an Abort method and a DateTime property showing when some bytes were received from the server last time would be appreciated.
Avatar
Alex #2
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
sorry for the delay, I was travelling.

Quote by Glassmaker on 2013-09-12, 18:39:
1. If it is critical to be sure that the stream is healthy, the StreamActive property of XmppClient is not of much help. E.g., if user turns on the "Airplane mode", StreamActive is still true in spite of client's inability to send or retrieve anything from the server. This particular problem may be mitigated with WinRT's NetworkInformation.GetInternetConnectionProfile(); but in general, it seems like there are no reliable routines to check health of the stream in Matrix.

StreamActive is not for this purpose. It is mostly used internal, we made it only public because some customers needed it.
StreamActive == true means that an XMPP Stream was established. So the socket is connected and the XMPP Stream was started. But this does not mean that the session is authenticated and ready for data.

Quote by Glassmaker on 2013-09-12, 18:39:
2. Hence, an app needs to manually watch for DateTime of last received stanza, perform pings from time to time, etc.. However, when the app is through some other means is sure that the connectivity is lost, it is not always easy to re-establish it. When connection is absent, a call to XmppClient.Close() will not result in an instant abortion of the stream. The OnClosed event may be fired about 10-15 seconds after a call to Close. Furthermore, a call to Open before OnClosed often results in unpredictable behavior, leading sometimes to complete inability of an XmppClient to re-establish the connection. It would be helpful to have an additional Abort method, that would instantly (or nearly-so) close the connection, clean-up XmppClient's inner state and allow for its re-establishment.

The Close method is async. So depending on many factors it can take a while to properly close the session.
What many of our customers do is just throw away the old XmppClient and create a new one. This will result in the same behavior as the Abort method you mention.
The Abort method is a good idea.  I will do some brainstorming about it and see if we could implement it.

Quote by Glassmaker on 2013-09-12, 18:39:
3. Suppose a connection check is required before sending particular stanza. To achieve this, app sends a ping and expects a pong. However, when the connection is busy with downloading data, e.g. when the server writes a heavy IQ result to the stream, a pong stanza may not come in minutes.

Because of this reason you should never send big stanzas which blocks the whole XMPP session. When you have to send lots of heavy data you should split the data in smaller chunks, then your XMPP stream could also react on other events.

Quote by Glassmaker on 2013-09-12, 18:39:
It would be helpful to have a DateTime of last received chunk of bytes from the server, so that the app could knew that the stream is active and healthy.
You mean the last XMPP stanza or socket event?

Whenever MatriX detects the loss of the connection on the socket it raises the OnClose event to you. The problem is that the .NET sockets often need some seconds until they forward the connection loss or errors to the .NET socket classes.

Quote by Glassmaker on 2013-09-12, 18:39:
Ideally, something like a following API would be rather helpful:

await xmppClient.OpenAsync(); // await finishes when first bytes are received from the server
bool isConnected = await xmppClient.IsConnectedAsync(); // returns true if XmppClient is sure that the stream is healthy

There, IsConnectedAsync would ideally perform a ping if there were no bytes received from the server in a given time period (configurable).

I've implemented something like this at the app level; however, due to Matrix's hiding of the inner socket workings, this solution has many downsides and may not be entirely reliable. Workarounds with an Abort method and a DateTime property showing when some bytes were received from the server last time would be appreciated.
in the latest version we have added many new async/task/await extensions. With this you can pretty easy implement a IsConnectedAsync extensions method. I will try to compile and upload a WinRT build from this code as soon as possible.

Alex
Avatar
Glassmaker #3
Member since Aug 2013 · 8 posts
Group memberships: Members
Show profile · Link to this post
What many of our customers do is just throw away the old XmppClient and create a new one.
I have been considering such an approach, but this means that I shall add to the wrapper over XmppClient all the events of an XmppClient, so that the business logic objects could subscribe to the wrapper's events instead of XmppClient's ones, if an underlying XmppClient could be thrown away at any time. If all your customers are doing this at an app level, then may be it could be a good idea to do so at the library's level :).

Because of this reason you should never send big stanzas which blocks the whole XMPP session. When you have to send lots of heavy data you should split the data in smaller chunks, then your XMPP stream could also react on other events.
Yes, this is understandable; however, the app I am working on has specific requirements for the adverse internet connection conditions, so that even a normal <message/> stanza could take few seconds to come.

You mean the last XMPP stanza or socket event?
It is possible to watch for last XMPP stanza currently through the ReceiveXml event, so yes, I meant something at a lower level. It would be useful to have a DateTime of last bytes received from the server (e.g. some network stream's ReadBytes method succeeded).

in the latest version we have added many new async/task/await extensions. With this you can pretty easy implement a IsConnectedAsync extensions method. I will try to compile and upload a WinRT build from this code as soon as possible.
Sounds great!
Avatar
Glassmaker #4
Member since Aug 2013 · 8 posts
Group memberships: Members
Show profile · Link to this post
There is another substantial problem: under adverse internet conditions (e.g. bandwidth of 200Kb/s, 420 ms latency, 20% packet loss), Windows apparently cannot suspend an app that uses Matrix within reasonable time and crashes it instead with an error "The program ... stopped interacting with Windows and was closed." A call of the Close method during app's suspension does not help.
Avatar
Alex #5
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
why are you not adding your WinRT to the lock screen to continue running it in background?
see also: http://forum.ag-software.net/thread/1436-ConnectedStandby

When you are on such bad connections then I think you app must implement acking from XEP-0198: Stream Management, Message Delivery Receipts or something else to make sure that you don't send more data than your connection can handle and fill up the queue. This extensions also guarantee delivery and you will have no end to end packet loss when you implement this logic.

An abort method you mentioned before can probably help with the Windows exception, but it does not solve the packet loss.
So please let us know how we can help you to improve your app.
Avatar
Glassmaker #6
Member since Aug 2013 · 8 posts
Group memberships: Members
Show profile · Link to this post
why are you not adding your WinRT to the lock screen to continue running it in background?
The app actually adds itself to the lock screen as it has other means of receiving data for it (more efficient than XMPP for this task). I considered adding a control channel trigger, but decided that it would not worth it in this app. I added it today for an experiment and found that it does not help: the app still crashes with "The program ... stopped interacting with Windows and was closed." during suspension process if the connection is bad (the figures I've shown in the previous post are from the connection emulation software).

As for the stream management and message delivery receipts: while the app is running, it is already pretty well up to its requirements, the only critical problem is this crash on suspension (which I am still investigating, but it seems it is not in the app's code). There are two other major problems: 1) no indication that the stream is busy with receiving data right now, so that the app can only guess when it is fine to send a ping; 2) no way to rapidly switch off the connection and re-establish it. However, for the first one I've implemented a work-around relying on app's specifics (though still, a DateTime of when any bytes were received from the server the last time would help considerably); and for the second one, the delay is tolerable, though barely - the Close method sometimes takes up to 10-15 seconds to raise the OnClose event, that's why an Abort method would be highly appreciated (otherwise I will have to wrap all the events of the XmppClient so as to make it possible to throw it away at any time).
Avatar
Glassmaker #7
Member since Aug 2013 · 8 posts
Group memberships: Members
Show profile · Link to this post
I am sorry, this crash is apparently not fault of Matrix but of the app :). I made a clean test app and could not reproduce it. Please disregard my earlier comments about this crash.
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:
Forum: MatriX RSS