MQTTnet icon indicating copy to clipboard operation
MQTTnet copied to clipboard

Use a different timeout mechanism to handle Xamarin timeout

Open kwende opened this issue 3 years ago • 5 comments

This is in reference to https://github.com/chkr1011/MQTTnet/issues/966.

I think the nature of the problem is that on Android, SO_TIMEOUT is set to 0 by default, meaning infinity. As a result, a call to Connect for a socket will hang indefinitely on this platform unless setSoTimeout explicitly specifies something else. However, on Windows I believe the timeout isn't infinity by default and so the ConnectAsync call will eventually fail and the retry/reconnect loop will proceed as normal.

What I've done here is essentially wrap the synchronous Connect call in a Task which can be waited on and, in the event the socket fails to connect within a specific timeframe, the socket can be disposed.

I got this error on Xamarin and was able to fix it and have all things proceed as normal with this patch.

kwende avatar Dec 28 '21 20:12 kwende

No worries 😄 Please check the usage of the passed cancellation token (starting the ConnectAsync method from the MqttClient). It can be already used for timeouts. In my opinion we should attach a handler to that token instead. Also the value of 10 seconds is not configurable now (there is already a value at the Client options for this which should be used).

chkr1011 avatar Jan 08 '22 13:01 chkr1011

I was having the same problem until I realized I had to run the connect on a separate thread

....
 Dim thread As New Thread(Sub()

                                     MQTT_Connect(My.Settings.MQTTBroker, My.Settings.MQTTPort, My.Settings.MQTTUser, My.Settings.MQTTPassword).Wait()

                                 End Sub
)
thread.Start()


 ...

Private Async Function MQTT_Connect(Server As String, Port As Integer, User As String, Password As String) As Task

    Try

        MQTTClient = CType(MQTTFactory.CreateMqttClient(), MqttClient)

        MQTTClient.UseApplicationMessageReceivedHandler(AddressOf MessageRecieved)
        MQTTClient.UseDisconnectedHandler(AddressOf ConnectionClosed)
        MQTTClient.UseConnectedHandler(AddressOf ConnectionOpened)

        Dim Options As New Options.MqttClientOptionsBuilder
        Options.WithClientId(Guid.NewGuid().ToString()).WithTcpServer(Server, Port).WithCredentials(User,Password).WithCommunicationTimeout(TimeSpan.FromSeconds(10))
        Await MQTTClient.ConnectAsync(Options.Build).ConfigureAwait(False)
        Log("MQTT connected")

    Catch ex As Exception

        Log("MQTT connection issue:" & vbCrLf & ex.Message.ToString)

    End Try


End Function

roblatour avatar Jan 24 '22 17:01 roblatour

CLA assistant check
All CLA requirements met.

dnfadmin avatar May 24 '22 16:05 dnfadmin

@chkr1011 I apologize for being so delayed on getting back to this PR. I am now trying to get it across the finish line (and maybe another one or two written up) soon.

A bit of background on what's going on with this: I think the underlying issue here might actually be a Xamarin/Mono problem, and not something specific to any of the code in MQTTNet nor Android/Linux as I originally thought. I just today wrote up the following Stack Overflow post using a slimmed down example app to show off the problem: https://stackoverflow.com/questions/72397435/await-hangs-forever-on-socket-connectasync-when-android-is-disconnected and I am likely going to write up a bug report with Xamarin as well in hopes it'd get some more attention. Short of walking through mono, I'm not sure what else to do. So, in summary: this might be a Xamarin thing.

To get to the point as far as MQTTNet is concerned: we could replace the async call with a synchronous call as I did already as that appears to sidestep the issue. We could then continue using the cancellation register routine to call dispose. However, it's a shame to have to do that as your code as-is is certainly cleaner looking.

I guess I'm looking to you for any opinions on how to proceed.

kwende avatar May 26 '22 20:05 kwende

Update: Xamarin issue was created.

https://github.com/xamarin/Xamarin.Forms/issues/15388

kwende avatar May 29 '22 15:05 kwende

Please see PR #1545. I found a way to avoid new timeouts at all. The code will make use of the passed cancellation token so that the user has full control about cancellation. Thanks for your effort and inspiration for the fix.

chkr1011 avatar Oct 19 '22 18:10 chkr1011