Refactor message processing and async/await usage.

This commit is contained in:
Christian Kratky
2018-06-16 14:30:02 +02:00
parent 6192685671
commit caea7910b4
31 changed files with 605 additions and 653 deletions
+107 -69
View File
@@ -40,52 +40,84 @@ namespace MQTTnet.Adapter
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfDisposed();
_logger.Verbose("Connecting [Timeout={0}]", timeout);
return ExecuteAndWrapExceptionAsync(() =>
Internal.TaskExtensions.TimeoutAfter(ct => _channel.ConnectAsync(ct), timeout, cancellationToken));
}
public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfDisposed();
_logger.Verbose("Disconnecting [Timeout={0}]", timeout);
return ExecuteAndWrapExceptionAsync(() =>
Internal.TaskExtensions.TimeoutAfter(ct => _channel.DisconnectAsync(), timeout, cancellationToken));
}
public Task SendPacketAsync(TimeSpan timeout, MqttBasePacket packet, CancellationToken cancellationToken)
{
return ExecuteAndWrapExceptionAsync(() =>
try
{
_logger.Verbose("TX >>> {0} [Timeout={1}]", packet, timeout);
_logger.Verbose("Connecting [Timeout={0}]", timeout);
await Internal.TaskExtensions
.TimeoutAfterAsync(ct => _channel.ConnectAsync(ct), timeout, cancellationToken)
.ConfigureAwait(false);
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
WrapException(exception);
}
}
public async Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfDisposed();
try
{
_logger.Verbose("Disconnecting [Timeout={0}]", timeout);
await Internal.TaskExtensions
.TimeoutAfterAsync(ct => _channel.DisconnectAsync(), timeout, cancellationToken)
.ConfigureAwait(false);
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
WrapException(exception);
}
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
try
{
_logger.Verbose("TX >>> {0}", packet);
var packetData = PacketSerializer.Serialize(packet);
return Internal.TaskExtensions.TimeoutAfter(ct => _channel.WriteAsync(
packetData.Array,
packetData.Offset,
packetData.Count,
ct), timeout, cancellationToken);
});
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
WrapException(exception);
}
}
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfDisposed();
MqttBasePacket packet = null;
await ExecuteAndWrapExceptionAsync(async () =>
try
{
ReceivedMqttPacket receivedMqttPacket;
if (timeout > TimeSpan.Zero)
{
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfter(ct => ReceiveAsync(_channel, ct), timeout, cancellationToken).ConfigureAwait(false);
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ct => ReceiveAsync(_channel, ct), timeout, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -94,19 +126,30 @@ namespace MQTTnet.Adapter
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
{
return;
return null;
}
packet = PacketSerializer.Deserialize(receivedMqttPacket);
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Verbose("RX <<< {0}", packet);
}).ConfigureAwait(false);
return packet;
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
return packet;
WrapException(exception);
}
return null;
}
private async Task<ReceivedMqttPacket> ReceiveAsync(IMqttChannel channel, CancellationToken cancellationToken)
@@ -138,7 +181,9 @@ namespace MQTTnet.Adapter
chunkSize = bytesLeft;
}
var readBytes = await channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken) .ConfigureAwait(false);
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
// has been sent from the sender directly after the initial bytes.
var readBytes = channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).GetAwaiter().GetResult();
if (readBytes <= 0)
{
ExceptionHelper.ThrowGracefulSocketClose();
@@ -155,42 +200,6 @@ namespace MQTTnet.Adapter
}
}
private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
{
try
{
await action().ConfigureAwait(false);
}
catch (Exception exception)
{
if (exception is TaskCanceledException ||
exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException ||
exception is MqttCommunicationException)
{
throw;
}
if (exception is IOException && exception.InnerException is SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new OperationCanceledException();
}
}
if (exception is COMException comException)
{
if ((uint)comException.HResult == ErrorOperationAborted)
{
throw new OperationCanceledException();
}
}
throw new MqttCommunicationException(exception);
}
}
public void Dispose()
{
_isDisposed = true;
@@ -205,5 +214,34 @@ namespace MQTTnet.Adapter
throw new ObjectDisposedException(nameof(MqttChannelAdapter));
}
}
private static bool IsWrappedException(Exception exception)
{
return exception is TaskCanceledException ||
exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException ||
exception is MqttCommunicationException;
}
private static void WrapException(Exception exception)
{
if (exception is IOException && exception.InnerException is SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new OperationCanceledException();
}
}
if (exception is COMException comException)
{
if ((uint)comException.HResult == ErrorOperationAborted)
{
throw new OperationCanceledException();
}
}
throw new MqttCommunicationException(exception);
}
}
}