If I execute the following code on an Android device with no network connectivity, the await call will never complete nor will any exceptions be thrown by ConnectAsync
even if Close
or Dispose
, etc. are invoked on the socket object (the catch handlers in this sample will never be reached, in other words):
CancellationToken token = _tokenSource.Token;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
using (token.Register(OnCancel))
{
await _socket.ConnectAsync("176.222.222.222", 1234);
}
}
catch(ObjectDisposedException)
{
}
catch (Exception)
{
}
However, this is not the behavior on Windows or Linux: on those platforms, ConnectAsync will immediately toss a "System.Net.Sockets.SocketException (10065): A socket operation was attempted to an unreachable host." exception if they're disconnected from any network, and if I choose to call either Close
or Dispose
on the socket object whilst it's attempting to connect when network is available, I'll get an exception about the I/O attempt being aborted. This is all expected behavior.
Is there any reason short of a bug in Mono that'd cause this Android-specific behavior? I have a working example here: https://github.com/kwende/MqttNetExampleIssues.
Edit:
It's possible my example isn't clear enough. Here is an example block of code using a timeout that's much simpler to read. The effect is the same, but perhaps this is a bit clearer. In the event of a timeout, the Register
call should result in OnCancel
being called, which should Close/Dispose of the socket. Doing so does nothing as far as continuation is concerned: the await never returns. I'm limited to button click events to show this off as I'm in Xamarin. The full code is available here: https://github.com/kwende/MqttNetExampleIssues/blob/main/Android/AndroidHang2/AndroidHang2/AndroidHang2/MainPage.xaml.cs. It can be downloaded and ran to show off the issue:
private void OnCancel()
{
_socket?.Close();
_socket?.Dispose();
_socket = null;
}
private async void Button_Clicked(object sender, EventArgs e)
{
await Task.Run(async () =>
{
using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
using(cancellationTokenSource.Token.Register(OnCancel))
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
await _socket.ConnectAsync("172.222.222.222", 1234);
}
catch(Exception ex)
{
// if your network is off, the timeout will occur, OnCancel will be called,
// close and dispose will be called, but this will never get hit.
}
}
}
});
}