Ejemplo n.º 1
0
        public async Task Session_Takeover()
        {
            using (var testEnvironment = new TestEnvironment(TestContext))
            {
                await testEnvironment.StartServer();

                var options = new MqttClientOptionsBuilder()
                              .WithCleanSession(false)
                              .WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
                              .WithClientId("a");

                var client1 = await testEnvironment.ConnectClient(options);

                await Task.Delay(500);

                MqttClientDisconnectReason disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
                client1.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(c => { disconnectReason = c.Reason; });

                var client2 = await testEnvironment.ConnectClient(options);

                await Task.Delay(500);

                Assert.IsFalse(client1.IsConnected);
                Assert.IsTrue(client2.IsConnected);

                Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
            }
        }
Ejemplo n.º 2
0
        public async Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken)
        {
            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            ThrowIfDisposed();

            var disconnectIsPending = DisconnectIsPending();

            try
            {
                _disconnectReason         = MqttClientDisconnectReason.NormalDisconnection;
                _cleanDisconnectInitiated = true;

                if (!disconnectIsPending && _isConnected)
                {
                    var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
                    await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                if (!disconnectIsPending)
                {
                    await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
                }
            }
        }
Ejemplo n.º 3
0
 public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reasonCode)
 {
     ClientWasConnected = clientWasConnected;
     Exception          = exception;
     AuthenticateResult = authenticateResult;
     ReasonCode         = reasonCode;
 }
        public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientConnectResult connectResult, MqttClientDisconnectReason reason)
        {
            ClientWasConnected = clientWasConnected;
            Exception          = exception;
            ConnectResult      = connectResult;
            Reason             = reason;

            ReasonCode = reason;
        }
Ejemplo n.º 5
0
        Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
        {
            _disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);

            // Also dispatch disconnect to waiting threads to generate a proper exception.
            _packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));

            return(DisconnectInternalAsync(_packetReceiverTask, null, null));
        }
Ejemplo n.º 6
0
        Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
        {
            _disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);

            // Also dispatch disconnect to waiting threads to generate a proper exception.
            _packetDispatcher.Dispatch(disconnectPacket);

            if (!DisconnectIsPending())
            {
                return(DisconnectInternalAsync(_packetReceiverTask, null, null));
            }

            return(PlatformAbstractionLayer.CompletedTask);
        }
Ejemplo n.º 7
0
        public async Task StopAsync(MqttClientDisconnectReason reason)
        {
            IsRunning = false;

            if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout)
            {
                // Is is very important to send the DISCONNECT packet here BEFORE cancelling the
                // token because the entire connection is closed (disposed) as soon as the cancellation
                // token is cancelled. To there is no chance that the DISCONNECT packet will ever arrive
                // at the client!
                await TrySendDisconnectPacket(reason).ConfigureAwait(false);
            }

            StopInternal();
        }
        public static Task DisconnectAsync(this MqttClient client, MqttClientDisconnectReason reason = MqttClientDisconnectReason.NormalDisconnection, string reasonString = null)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            return(client.DisconnectAsync(
                       new MqttClientDisconnectOptions
            {
                Reason = reason,
                ReasonString = reasonString
            },
                       CancellationToken.None));
        }
Ejemplo n.º 9
0
        async Task TrySendDisconnectPacket(MqttClientDisconnectReason reason)
        {
            try
            {
                var disconnectOptions = new MqttClientDisconnectOptions
                {
                    ReasonCode   = reason,
                    ReasonString = reason.ToString()
                };

                var disconnectPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(disconnectOptions);

                using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
                {
                    await SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
                }
            }
            catch (Exception exception)
            {
                _logger.Warning(exception, "Client '{{0}}': Error while sending DISCONNECT packet (Reason = {1}).", ClientId, reason);
            }
        }
Ejemplo n.º 10
0
        public async Task StopAsync(MqttClientDisconnectReason reason)
        {
            Status            = MqttClientConnectionStatus.Finalizing;
            _disconnectReason = reason;

            if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout)
            {
                // Is is very important to send the DISCONNECT packet here BEFORE cancelling the
                // token because the entire connection is closed (disposed) as soon as the cancellation
                // token is cancelled. To there is no chance that the DISCONNECT packet will ever arrive
                // at the client!
                try
                {
                    var disconnectOptions = new MqttClientDisconnectOptions
                    {
                        ReasonCode   = reason,
                        ReasonString = reason.ToString()
                    };

                    var disconnectPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(disconnectOptions);

                    using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
                    {
                        await _channelAdapter.SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
                    }
                }
                catch (Exception exception)
                {
                    _logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet after takeover.", ClientId);
                }
            }

            StopInternal();

            await(_packageReceiverTask ?? PlatformAbstractionLayer.CompletedTask);
        }
Ejemplo n.º 11
0
        public async Task <MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.ChannelOptions == null)
            {
                throw new ArgumentException("ChannelOptions are not set.");
            }

            ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");

            ThrowIfDisposed();

            MqttClientAuthenticateResult authenticateResult = null;

            try
            {
                Options = options;

                _packetIdentifierProvider.Reset();
                _packetDispatcher.Cancel();

                _backgroundCancellationTokenSource = new CancellationTokenSource();
                var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;

                _isDisconnectPending = 0;
                var adapter = _adapterFactory.CreateClientAdapter(options);
                _adapter = adapter;

                using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
                {
                    _logger.Verbose("Trying to connect with server '{0}' (Timeout={1}).", options.ChannelOptions, options.CommunicationTimeout);
                    await adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false);

                    _logger.Verbose("Connection with server established.");

                    _publishPacketReceiverQueue = new AsyncQueue <MqttPublishPacket>();
                    _publishPacketReceiverTask  = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);

                    _packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);

                    authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
                }

                _sendTracker.Restart();
                _receiveTracker.Restart();

                if (Options.KeepAlivePeriod != TimeSpan.Zero)
                {
                    _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
                }

                _isConnected = true;

                _logger.Info("Connected.");

                var connectedHandler = ConnectedHandler;
                if (connectedHandler != null)
                {
                    await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false);
                }

                return(authenticateResult);
            }
            catch (Exception exception)
            {
                _disconnectReason = MqttClientDisconnectReason.UnspecifiedError;

                _logger.Error(exception, "Error while connecting with server.");

                if (!DisconnectIsPending())
                {
                    await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false);
                }

                throw;
            }
        }
Ejemplo n.º 12
0
        async Task TryProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
        {
            try
            {
                _receiveTracker.Restart();

                if (packet is MqttPublishPacket publishPacket)
                {
                    EnqueueReceivedPublishPacket(publishPacket);
                }
                else if (packet is MqttPubRelPacket pubRelPacket)
                {
                    await SendAsync(new MqttPubCompPacket
                    {
                        PacketIdentifier = pubRelPacket.PacketIdentifier,
                        ReasonCode       = MqttPubCompReasonCode.Success
                    }, cancellationToken).ConfigureAwait(false);
                }
                else if (packet is MqttPingReqPacket)
                {
                    await SendAsync(new MqttPingRespPacket(), cancellationToken).ConfigureAwait(false);
                }
                else if (packet is MqttDisconnectPacket disconnectPacket)
                {
                    _disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);

                    // Also dispatch disconnect to waiting threads to generate a proper exception.
                    _packetDispatcher.Dispatch(packet);

                    if (!DisconnectIsPending())
                    {
                        await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
                    }
                }
                else if (packet is MqttAuthPacket authPacket)
                {
                    var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
                    if (extendedAuthenticationExchangeHandler != null)
                    {
                        await extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this)).ConfigureAwait(false);
                    }
                }
                else
                {
                    _packetDispatcher.Dispatch(packet);
                }
            }
            catch (Exception exception)
            {
                if (_cleanDisconnectInitiated)
                {
                    return;
                }

                if (exception is OperationCanceledException)
                {
                }
                else if (exception is MqttCommunicationException)
                {
                    _logger.Warning(exception, "Communication error while receiving packets.");
                }
                else
                {
                    _logger.Error(exception, "Error while receiving packets.");
                }

                _packetDispatcher.Dispatch(exception);

                if (!DisconnectIsPending())
                {
                    await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
                }
            }
        }
Ejemplo n.º 13
0
 public static MqttClientConnectionEventArgs Disconnected(string clientId, MqttClientDisconnectReason reason) =>
 new MqttClientConnectionEventArgs(clientId, reason)
 {
     DisconnectionTime = DateTime.Now,
     IsReConnected     = false
 };
Ejemplo n.º 14
0
 public MqttClientConnectionEventArgs(string clientId, MqttClientDisconnectReason reason)
     : this(clientId)
 {
     DisconnectReason = reason;
 }
Ejemplo n.º 15
0
 public MqttClientDisconnectOptionsBuilder WithReason(MqttClientDisconnectReason value)
 {
     _reason = value;
     return(this);
 }