private async Task <TResponsePacket> SendAndReceiveAsync <TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket { cancellationToken.ThrowIfCancellationRequested(); _sendTracker.Restart(); ushort identifier = 0; if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue) { identifier = packetWithIdentifier.PacketIdentifier.Value; } var packetAwaiter = _packetDispatcher.AddPacketAwaiter <TResponsePacket>(identifier); try { await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false); var respone = await Common.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false); return((TResponsePacket)respone); } catch (MqttCommunicationTimedOutException) { _logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Namespace); throw; } finally { _packetDispatcher.RemovePacketAwaiter <TResponsePacket>(identifier); } }
private async Task <TResponsePacket> SendAndReceiveAsync <TResponsePacket>(Packet requestPacket, CancellationToken cancellationToken) where TResponsePacket : Packet { cancellationToken.ThrowIfCancellationRequested(); ushort identifier = 0; if (requestPacket is PacketWithId packetWithId) { identifier = packetWithId.PacketId; } var awaiter = _packetDispatcher.AddPacketAwaiter <TResponsePacket>(identifier); try { await _clientChannel.WriteAndFlushAsync(requestPacket); //var respone = await Extensions.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, _options.Timeout, cancellationToken); //return (TResponsePacket)respone; using (var timeoutCts = new CancellationTokenSource(_options.Timeout)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)) { linkedCts.Token.Register(() => { if (!awaiter.Task.IsCompleted && !awaiter.Task.IsFaulted && !awaiter.Task.IsCanceled) { awaiter.TrySetCanceled(); } }); try { var result = await awaiter.Task.ConfigureAwait(false); timeoutCts.Cancel(false); return((TResponsePacket)result); } catch (OperationCanceledException exception) { if (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { throw new MqttTimeoutException(exception); } else { throw; } } } } catch (Exception) { throw; } finally { _packetDispatcher.RemovePacketAwaiter <TResponsePacket>(identifier); } }
private async Task SendPendingPacketsAsync(CancellationToken cancellationToken) { MqttQueuedApplicationMessage queuedApplicationMessage = null; MqttPublishPacket publishPacket = null; try { while (!cancellationToken.IsCancellationRequested) { queuedApplicationMessage = await Session.ApplicationMessagesQueue.TakeAsync(cancellationToken).ConfigureAwait(false); if (queuedApplicationMessage == null) { return; } if (cancellationToken.IsCancellationRequested) { return; } publishPacket = _dataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage); publishPacket.QualityOfServiceLevel = queuedApplicationMessage.QualityOfServiceLevel; // Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9]. publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage; if (publishPacket.QualityOfServiceLevel > 0) { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); } if (_serverOptions.ClientMessageQueueInterceptor != null) { var context = new MqttClientMessageQueueInterceptorContext( queuedApplicationMessage.SenderClientId, ClientId, queuedApplicationMessage.ApplicationMessage); if (_serverOptions.ClientMessageQueueInterceptor != null) { await _serverOptions.ClientMessageQueueInterceptor.InterceptClientMessageQueueEnqueueAsync(context).ConfigureAwait(false); } if (!context.AcceptEnqueue || context.ApplicationMessage == null) { return; } publishPacket.Topic = context.ApplicationMessage.Topic; publishPacket.Payload = context.ApplicationMessage.Payload; publishPacket.QualityOfServiceLevel = context.ApplicationMessage.QualityOfServiceLevel; } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) { await SendAsync(publishPacket).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { var awaiter = _packetDispatcher.AddPacketAwaiter <MqttPubAckPacket>(publishPacket.PacketIdentifier); await SendAsync(publishPacket).ConfigureAwait(false); await awaiter.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { using (var awaiter1 = _packetDispatcher.AddPacketAwaiter <MqttPubRecPacket>(publishPacket.PacketIdentifier)) using (var awaiter2 = _packetDispatcher.AddPacketAwaiter <MqttPubCompPacket>(publishPacket.PacketIdentifier)) { await SendAsync(publishPacket).ConfigureAwait(false); await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); await SendAsync(new MqttPubRelPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false); await awaiter2.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } _logger.Verbose("Queued application message sent (ClientId: {0}).", ClientId); // TODO: //Interlocked.Increment(ref _sentPacketsCount); } } catch (Exception exception) { if (exception is MqttCommunicationTimedOutException) { _logger.Warning(exception, "Sending publish packet failed: Timeout (ClientId: {0}).", ClientId); } else if (exception is MqttCommunicationException) { _logger.Warning(exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", ClientId); } else if (exception is OperationCanceledException && _cancellationToken.Token.IsCancellationRequested) { // The cancellation was triggered externally. } else { _logger.Error(exception, "Sending publish packet failed (ClientId: {0}).", ClientId); } if (publishPacket?.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { queuedApplicationMessage.IsDuplicate = true; Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage); } if (!_cancellationToken.Token.IsCancellationRequested) { await StopAsync().ConfigureAwait(false); } } }