mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-17 01:33:03 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54a0265ef9 | ||
|
|
8f96acaec4 | ||
|
|
cea984cc2f |
@@ -2,7 +2,7 @@
|
|||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MQTTnet.AspNetCore</id>
|
<id>MQTTnet.AspNetCore</id>
|
||||||
<version>2.7.1</version>
|
<version>2.7.3</version>
|
||||||
<authors>Christian Kratky</authors>
|
<authors>Christian Kratky</authors>
|
||||||
<owners>Christian Kratky</owners>
|
<owners>Christian Kratky</owners>
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||||
@@ -10,13 +10,13 @@
|
|||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||||
<releaseNotes>* Updated to MQTTnet 2.7.1.
|
<releaseNotes>* Updated to MQTTnet 2.7.3.
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="netstandard2.0">
|
<group targetFramework="netstandard2.0">
|
||||||
<dependency id="MQTTnet" version="2.7.1" />
|
<dependency id="MQTTnet" version="2.7.3" />
|
||||||
</group>
|
</group>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MQTTnet</id>
|
<id>MQTTnet</id>
|
||||||
<version>2.7.2</version>
|
<version>2.7.3</version>
|
||||||
<authors>Christian Kratky</authors>
|
<authors>Christian Kratky</authors>
|
||||||
<owners>Christian Kratky</owners>
|
<owners>Christian Kratky</owners>
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||||
@@ -10,11 +10,14 @@
|
|||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||||
<releaseNotes>* [Client] Added the subprotocol "mqtt" as default for web socket based connections.
|
<releaseNotes>* [Core] Add several new extension methods.
|
||||||
* [Client] Added a new client setting called "KeepAliveSendInterval". It allows configuring the effective interval for sending ping requests.
|
* [Client] Fixed an issue in _ManagedMqttClientOptionsBuilder_ when using _WithClientOptions_ and an options builder.
|
||||||
* [Client] The client will no longer send ping requests if other packets are sent within the configured interval.
|
* [Client] Added the "IsStarted" property for the managed client.
|
||||||
* [Server] The server now generates a valid packet identifier when disaptching publish packets to clients.
|
* [Client] Optimized stream buffer for UWP apps.
|
||||||
* [Core] Add several new extension methods.
|
* [Client] Added the _BufferSize_ to the TCP options.
|
||||||
|
* [Client] Fixed a race condition which leads to exceptions when reconnecting rapidly.
|
||||||
|
* [Server] Fixed a race condition which leads to exceptions when clients are reconnecting rapidly.
|
||||||
|
* [Core] Fixed some issues in stream and socket handling.
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using MQTTnet.Serializer;
|
|||||||
|
|
||||||
namespace MQTTnet.Adapter
|
namespace MQTTnet.Adapter
|
||||||
{
|
{
|
||||||
public interface IMqttChannelAdapter
|
public interface IMqttChannelAdapter : IDisposable
|
||||||
{
|
{
|
||||||
IMqttPacketSerializer PacketSerializer { get; }
|
IMqttPacketSerializer PacketSerializer { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ using MQTTnet.Serializer;
|
|||||||
|
|
||||||
namespace MQTTnet.Adapter
|
namespace MQTTnet.Adapter
|
||||||
{
|
{
|
||||||
public class MqttChannelAdapter : IMqttChannelAdapter
|
public sealed class MqttChannelAdapter : IMqttChannelAdapter
|
||||||
{
|
{
|
||||||
private const uint ErrorOperationAborted = 0x800703E3;
|
private const uint ErrorOperationAborted = 0x800703E3;
|
||||||
private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
|
private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
|
||||||
|
|
||||||
|
private bool _isDisposed;
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||||
private readonly IMqttNetLogger _logger;
|
private readonly IMqttNetLogger _logger;
|
||||||
private readonly IMqttChannel _channel;
|
private readonly IMqttChannel _channel;
|
||||||
@@ -34,6 +35,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public Task ConnectAsync(TimeSpan timeout)
|
public Task ConnectAsync(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
_logger.Trace<MqttChannelAdapter>("Connecting [Timeout={0}]", timeout);
|
_logger.Trace<MqttChannelAdapter>("Connecting [Timeout={0}]", timeout);
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
|
return ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
|
||||||
@@ -41,6 +43,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public Task DisconnectAsync(TimeSpan timeout)
|
public Task DisconnectAsync(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
_logger.Trace<MqttChannelAdapter>("Disconnecting [Timeout={0}]", timeout);
|
_logger.Trace<MqttChannelAdapter>("Disconnecting [Timeout={0}]", timeout);
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
|
return ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
|
||||||
@@ -48,6 +51,8 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
public Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(async () =>
|
return ExecuteAndWrapExceptionAsync(async () =>
|
||||||
{
|
{
|
||||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@@ -55,6 +60,11 @@ namespace MQTTnet.Adapter
|
|||||||
{
|
{
|
||||||
foreach (var packet in packets)
|
foreach (var packet in packets)
|
||||||
{
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (packet == null)
|
if (packet == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -65,10 +75,20 @@ namespace MQTTnet.Adapter
|
|||||||
var chunks = PacketSerializer.Serialize(packet);
|
var chunks = PacketSerializer.Serialize(packet);
|
||||||
foreach (var chunk in chunks)
|
foreach (var chunk in chunks)
|
||||||
{
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _channel.SendStream.WriteAsync(chunk.Array, chunk.Offset, chunk.Count, cancellationToken).ConfigureAwait(false);
|
await _channel.SendStream.WriteAsync(chunk.Array, chunk.Offset, chunk.Count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (timeout > TimeSpan.Zero)
|
if (timeout > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||||
@@ -87,6 +107,8 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
MqttBasePacket packet = null;
|
MqttBasePacket packet = null;
|
||||||
await ExecuteAndWrapExceptionAsync(async () =>
|
await ExecuteAndWrapExceptionAsync(async () =>
|
||||||
{
|
{
|
||||||
@@ -214,5 +236,20 @@ namespace MQTTnet.Adapter
|
|||||||
throw new MqttCommunicationException(exception);
|
throw new MqttCommunicationException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_isDisposed = true;
|
||||||
|
_semaphore?.Dispose();
|
||||||
|
_channel?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(MqttChannelAdapter));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MQTTnet.Channel
|
namespace MQTTnet.Channel
|
||||||
{
|
{
|
||||||
public interface IMqttChannel
|
public interface IMqttChannel : IDisposable
|
||||||
{
|
{
|
||||||
Stream SendStream { get; }
|
Stream SendStream { get; }
|
||||||
Stream ReceiveStream { get; }
|
Stream ReceiveStream { get; }
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||||
private readonly Stopwatch _sendTracker = new Stopwatch();
|
private readonly Stopwatch _sendTracker = new Stopwatch();
|
||||||
|
private readonly SemaphoreSlim _disconnectLock = new SemaphoreSlim(1, 1);
|
||||||
private readonly IMqttClientAdapterFactory _adapterFactory;
|
private readonly IMqttClientAdapterFactory _adapterFactory;
|
||||||
private readonly MqttPacketDispatcher _packetDispatcher;
|
private readonly MqttPacketDispatcher _packetDispatcher;
|
||||||
private readonly IMqttNetLogger _logger;
|
private readonly IMqttNetLogger _logger;
|
||||||
@@ -24,6 +25,8 @@ namespace MQTTnet.Client
|
|||||||
private IMqttClientOptions _options;
|
private IMqttClientOptions _options;
|
||||||
private bool _isReceivingPackets;
|
private bool _isReceivingPackets;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private Task _packetReceiverTask;
|
||||||
|
private Task _keepAliveMessageSenderTask;
|
||||||
private IMqttChannelAdapter _adapter;
|
private IMqttChannelAdapter _adapter;
|
||||||
|
|
||||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
||||||
@@ -60,7 +63,7 @@ namespace MQTTnet.Client
|
|||||||
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
||||||
_logger.Trace<MqttClient>("Connection with server established.");
|
_logger.Trace<MqttClient>("Connection with server established.");
|
||||||
|
|
||||||
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false);
|
await StartReceivingPacketsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
|
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
|
||||||
_logger.Trace<MqttClient>("MQTT connection with server established.");
|
_logger.Trace<MqttClient>("MQTT connection with server established.");
|
||||||
@@ -69,7 +72,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
|
StartSendingKeepAliveMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
@@ -94,7 +97,10 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
|
if (!_cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -153,7 +159,7 @@ namespace MQTTnet.Client
|
|||||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||||
{
|
{
|
||||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||||
await SendAsync(qosGroup.ToArray()).ConfigureAwait(false);
|
await SendAsync((MqttPublishPacket[])qosGroup.ToArray()).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
@@ -194,6 +200,9 @@ namespace MQTTnet.Client
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
|
_adapter?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
|
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
|
||||||
@@ -229,23 +238,31 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
private async Task DisconnectInternalAsync(Exception exception)
|
private async Task DisconnectInternalAsync(Exception exception)
|
||||||
{
|
{
|
||||||
|
await _disconnectLock.WaitAsync();
|
||||||
var clientWasConnected = IsConnected;
|
var clientWasConnected = IsConnected;
|
||||||
IsConnected = false;
|
|
||||||
|
|
||||||
var cts = _cancellationTokenSource;
|
|
||||||
if (cts == null || cts.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cts.Cancel(false);
|
|
||||||
cts.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
|
||||||
|
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancellationTokenSource.Cancel(false);
|
||||||
|
|
||||||
|
if (_packetReceiverTask != null)
|
||||||
|
{
|
||||||
|
Task.WaitAll(_packetReceiverTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_keepAliveMessageSenderTask != null)
|
||||||
|
{
|
||||||
|
Task.WaitAll(_keepAliveMessageSenderTask);
|
||||||
|
}
|
||||||
|
|
||||||
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
||||||
_logger.Info<MqttClient>("Disconnected from adapter.");
|
_logger.Trace<MqttClient>("Disconnected from adapter.");
|
||||||
}
|
}
|
||||||
catch (Exception adapterException)
|
catch (Exception adapterException)
|
||||||
{
|
{
|
||||||
@@ -253,6 +270,14 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_adapter?.Dispose();
|
||||||
|
_adapter = null;
|
||||||
|
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
|
_disconnectLock.Release();
|
||||||
|
|
||||||
_logger.Info<MqttClient>("Disconnected.");
|
_logger.Info<MqttClient>("Disconnected.");
|
||||||
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected, exception));
|
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected, exception));
|
||||||
}
|
}
|
||||||
@@ -311,6 +336,11 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket)
|
private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
|
if (_cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
FireApplicationMessageReceivedEvent(publishPacket);
|
||||||
@@ -363,54 +393,48 @@ namespace MQTTnet.Client
|
|||||||
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
|
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
private async Task SendKeepAliveMessagesAsync()
|
||||||
{
|
{
|
||||||
_logger.Info<MqttClient>("Start sending keep alive packets.");
|
_logger.Info<MqttClient>("Start sending keep alive packets.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
TimeSpan keepAliveSendInterval;
|
var keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
|
||||||
if (_options.KeepAliveSendInterval.HasValue)
|
if (_options.KeepAliveSendInterval.HasValue)
|
||||||
{
|
{
|
||||||
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
|
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sendTracker.Elapsed > keepAliveSendInterval)
|
if (_sendTracker.Elapsed > keepAliveSendInterval)
|
||||||
{
|
{
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
|
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(keepAliveSendInterval, cancellationToken).ConfigureAwait(false);
|
await Task.Delay(keepAliveSendInterval, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException exception)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Warning<MqttClient>(exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
|
||||||
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Warning<MqttClient>(exception, "Unhandled exception while sending/receiving keep alive packets.");
|
if (_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception is MqttCommunicationException)
|
||||||
|
{
|
||||||
|
_logger.Warning<MqttClient>(exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Warning<MqttClient>(exception, "Unhandled exception while sending/receiving keep alive packets.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -419,48 +443,46 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
|
private async Task ReceivePacketsAsync()
|
||||||
{
|
{
|
||||||
_logger.Info<MqttClient>("Start receiving packets.");
|
_logger.Info<MqttClient>("Start receiving packets.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_isReceivingPackets = true;
|
_isReceivingPackets = true;
|
||||||
|
|
||||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
|
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StartProcessReceivedPacket(packet, cancellationToken);
|
StartProcessReceivedPacket(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException exception)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Warning<MqttClient>(exception, "MQTT communication exception while receiving packets.");
|
|
||||||
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error<MqttClient>(exception, "Unhandled exception while receiving packets.");
|
if (_cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception is MqttCommunicationException)
|
||||||
|
{
|
||||||
|
_logger.Warning<MqttClient>(exception, "MQTT communication exception while receiving packets.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error<MqttClient>(exception, "Unhandled exception while receiving packets.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
await DisconnectInternalAsync(exception).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -469,38 +491,26 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken)
|
private void StartProcessReceivedPacket(MqttBasePacket packet)
|
||||||
{
|
{
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
Task.Run(() => ProcessReceivedPacketAsync(packet), _cancellationTokenSource.Token);
|
||||||
Task.Run(
|
|
||||||
async () => await ProcessReceivedPacketAsync(packet).ConfigureAwait(false),
|
|
||||||
cancellationToken).ConfigureAwait(false);
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartReceivingPacketsAsync(CancellationToken cancellationToken)
|
private async Task StartReceivingPacketsAsync()
|
||||||
{
|
{
|
||||||
_isReceivingPackets = false;
|
_isReceivingPackets = false;
|
||||||
|
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
_packetReceiverTask = Task.Run(ReceivePacketsAsync, _cancellationTokenSource.Token);
|
||||||
Task.Run(
|
|
||||||
async () => await ReceivePacketsAsync(cancellationToken).ConfigureAwait(false),
|
|
||||||
cancellationToken).ConfigureAwait(false);
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
|
|
||||||
while (!_isReceivingPackets && !cancellationToken.IsCancellationRequested)
|
while (!_isReceivingPackets && !_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromMilliseconds(100), _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
|
private void StartSendingKeepAliveMessages()
|
||||||
{
|
{
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
_keepAliveMessageSenderTask = Task.Run(SendKeepAliveMessagesAsync, _cancellationTokenSource.Token);
|
||||||
Task.Run(
|
|
||||||
async () => await SendKeepAliveMessagesAsync(cancellationToken).ConfigureAwait(false),
|
|
||||||
cancellationToken).ConfigureAwait(false);
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
public int? Port { get; set; }
|
public int? Port { get; set; }
|
||||||
|
|
||||||
|
public int BufferSize { get; set; } = 20 * 4096;
|
||||||
|
|
||||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,21 @@ using MQTTnet.Client;
|
|||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public sealed class MqttTcpChannel : IMqttChannel, IDisposable
|
public sealed class MqttTcpChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||||
|
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
||||||
|
|
||||||
|
private readonly int _bufferSize = BufferSize;
|
||||||
private readonly MqttClientTcpOptions _options;
|
private readonly MqttClientTcpOptions _options;
|
||||||
|
|
||||||
private StreamSocket _socket;
|
private StreamSocket _socket;
|
||||||
|
|
||||||
public MqttTcpChannel(MqttClientTcpOptions options)
|
public MqttTcpChannel(MqttClientTcpOptions options)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_bufferSize = _options.BufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttTcpChannel(StreamSocket socket)
|
public MqttTcpChannel(StreamSocket socket)
|
||||||
@@ -69,14 +76,56 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_socket?.Dispose();
|
try
|
||||||
_socket = null;
|
{
|
||||||
|
SendStream?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SendStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReceiveStream?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReceiveStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_socket?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_socket = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStreams()
|
private void CreateStreams()
|
||||||
{
|
{
|
||||||
SendStream = _socket.OutputStream.AsStreamForWrite();
|
SendStream = _socket.OutputStream.AsStreamForWrite(_bufferSize);
|
||||||
ReceiveStream = _socket.InputStream.AsStreamForRead();
|
ReceiveStream = _socket.InputStream.AsStreamForRead(_bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Certificate LoadCertificate(MqttClientTcpOptions options)
|
private static Certificate LoadCertificate(MqttClientTcpOptions options)
|
||||||
|
|||||||
@@ -12,17 +12,20 @@ using MQTTnet.Client;
|
|||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public sealed class MqttTcpChannel : IMqttChannel, IDisposable
|
public sealed class MqttTcpChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
private readonly MqttClientTcpOptions _options;
|
|
||||||
|
|
||||||
//todo: this can be used with min dependency NetStandard1.6
|
|
||||||
#if NET452 || NET461
|
#if NET452 || NET461
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
||||||
|
|
||||||
|
private readonly int _bufferSize = BufferSize;
|
||||||
|
#else
|
||||||
|
private readonly int _bufferSize = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private readonly MqttClientTcpOptions _options;
|
||||||
|
|
||||||
private Socket _socket;
|
private Socket _socket;
|
||||||
private SslStream _sslStream;
|
private SslStream _sslStream;
|
||||||
|
|
||||||
@@ -32,6 +35,7 @@ namespace MQTTnet.Implementations
|
|||||||
public MqttTcpChannel(MqttClientTcpOptions options)
|
public MqttTcpChannel(MqttClientTcpOptions options)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_bufferSize = options.BufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,7 +47,7 @@ namespace MQTTnet.Implementations
|
|||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
_sslStream = sslStream;
|
_sslStream = sslStream;
|
||||||
|
|
||||||
CreateStreams(socket, sslStream);
|
CreateStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
public Stream SendStream { get; private set; }
|
||||||
@@ -58,7 +62,6 @@ namespace MQTTnet.Implementations
|
|||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: else brach can be used with min dependency NET46
|
|
||||||
#if NET452 || NET461
|
#if NET452 || NET461
|
||||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
@@ -68,10 +71,10 @@ namespace MQTTnet.Implementations
|
|||||||
if (_options.TlsOptions.UseTls)
|
if (_options.TlsOptions.UseTls)
|
||||||
{
|
{
|
||||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
||||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateStreams(_socket, _sslStream);
|
CreateStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
public Task DisconnectAsync()
|
||||||
@@ -82,11 +85,70 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_socket?.Dispose();
|
var oneStreamIsUsed = SendStream != null && ReceiveStream != null && ReferenceEquals(SendStream, ReceiveStream);
|
||||||
_socket = null;
|
|
||||||
|
|
||||||
_sslStream?.Dispose();
|
try
|
||||||
_sslStream = null;
|
{
|
||||||
|
SendStream?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SendStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!oneStreamIsUsed)
|
||||||
|
{
|
||||||
|
ReceiveStream?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReceiveStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_sslStream?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sslStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_socket?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_socket = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||||
@@ -120,15 +182,15 @@ namespace MQTTnet.Implementations
|
|||||||
return _options.TlsOptions.AllowUntrustedCertificates;
|
return _options.TlsOptions.AllowUntrustedCertificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static X509CertificateCollection LoadCertificates(MqttClientTcpOptions options)
|
private X509CertificateCollection LoadCertificates()
|
||||||
{
|
{
|
||||||
var certificates = new X509CertificateCollection();
|
var certificates = new X509CertificateCollection();
|
||||||
if (options.TlsOptions.Certificates == null)
|
if (_options.TlsOptions.Certificates == null)
|
||||||
{
|
{
|
||||||
return certificates;
|
return certificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var certificate in options.TlsOptions.Certificates)
|
foreach (var certificate in _options.TlsOptions.Certificates)
|
||||||
{
|
{
|
||||||
certificates.Add(new X509Certificate2(certificate));
|
certificates.Add(new X509Certificate2(certificate));
|
||||||
}
|
}
|
||||||
@@ -136,20 +198,26 @@ namespace MQTTnet.Implementations
|
|||||||
return certificates;
|
return certificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStreams(Socket socket, Stream sslStream)
|
private void CreateStreams()
|
||||||
{
|
{
|
||||||
var stream = sslStream ?? new NetworkStream(socket);
|
Stream stream;
|
||||||
|
if (_sslStream != null)
|
||||||
|
{
|
||||||
|
stream = _sslStream;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream = new NetworkStream(_socket, true);
|
||||||
|
}
|
||||||
|
|
||||||
//todo: if branch can be used with min dependency NetStandard1.6
|
|
||||||
#if NET452 || NET461
|
#if NET452 || NET461
|
||||||
SendStream = new BufferedStream(stream, BufferSize);
|
SendStream = new BufferedStream(stream, _bufferSize);
|
||||||
ReceiveStream = new BufferedStream(stream, BufferSize);
|
ReceiveStream = new BufferedStream(stream, _bufferSize);
|
||||||
#else
|
#else
|
||||||
SendStream = stream;
|
SendStream = stream;
|
||||||
ReceiveStream = stream;
|
ReceiveStream = stream;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -96,8 +96,17 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_webSocket?.Dispose();
|
try
|
||||||
_webSocket = null;
|
{
|
||||||
|
_webSocket?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_webSocket = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ namespace MQTTnet.ManagedClient
|
|||||||
{
|
{
|
||||||
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
||||||
{
|
{
|
||||||
|
bool IsStarted { get; }
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
|
||||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ namespace MQTTnet.ManagedClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected => _mqttClient.IsConnected;
|
public bool IsConnected => _mqttClient.IsConnected;
|
||||||
|
public bool IsStarted => _connectionCancellationToken != null;
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||||
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ namespace MQTTnet.ManagedClient
|
|||||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic)
|
||||||
|
{
|
||||||
|
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||||
|
|
||||||
|
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||||
|
}
|
||||||
|
|
||||||
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
|
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
|
||||||
{
|
{
|
||||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace MQTTnet.ManagedClient
|
|||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (_clientOptionsBuilder != null)
|
if (_clientOptionsBuilder == null)
|
||||||
{
|
{
|
||||||
_clientOptionsBuilder = new MqttClientOptionsBuilder();
|
_clientOptionsBuilder = new MqttClientOptionsBuilder();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ namespace MQTTnet.Serializer
|
|||||||
|
|
||||||
public static async Task<MqttPacketHeader> ReadHeaderAsync(Stream stream, CancellationToken cancellationToken)
|
public static async Task<MqttPacketHeader> ReadHeaderAsync(Stream stream, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the next package which starts with the header. At this point there will probably
|
// Wait for the next package which starts with the header. At this point there will probably
|
||||||
// some large delay and thus the thread should be put back to the pool (await). So ReadByte()
|
// some large delay and thus the thread should be put back to the pool (await). So ReadByte()
|
||||||
// is not an option here.
|
// is not an option here.
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Server
|
|||||||
private readonly Func<Task> _timeoutCallback;
|
private readonly Func<Task> _timeoutCallback;
|
||||||
private readonly IMqttNetLogger _logger;
|
private readonly IMqttNetLogger _logger;
|
||||||
|
|
||||||
|
private Task _workerTask;
|
||||||
|
|
||||||
public MqttClientKeepAliveMonitor(string clientId, Func<Task> timeoutCallback, IMqttNetLogger logger)
|
public MqttClientKeepAliveMonitor(string clientId, Func<Task> timeoutCallback, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_clientId = clientId;
|
_clientId = clientId;
|
||||||
@@ -34,7 +36,15 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(async () => await RunAsync(keepAlivePeriod, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
|
_workerTask = Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken).ConfigureAwait(false), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitForCompletion()
|
||||||
|
{
|
||||||
|
if (_workerTask != null)
|
||||||
|
{
|
||||||
|
Task.WaitAll(_workerTask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
|
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
@@ -18,6 +19,8 @@ namespace MQTTnet.Server
|
|||||||
private readonly MqttClientSession _clientSession;
|
private readonly MqttClientSession _clientSession;
|
||||||
private readonly IMqttNetLogger _logger;
|
private readonly IMqttNetLogger _logger;
|
||||||
|
|
||||||
|
private Task _workerTask;
|
||||||
|
|
||||||
public MqttClientPendingMessagesQueue(IMqttServerOptions options, MqttClientSession clientSession, IMqttNetLogger logger)
|
public MqttClientPendingMessagesQueue(IMqttServerOptions options, MqttClientSession clientSession, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
@@ -36,7 +39,15 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(async () => await SendQueuedPacketsAsync(adapter, cancellationToken), cancellationToken).ConfigureAwait(false);
|
_workerTask = Task.Run(() => SendQueuedPacketsAsync(adapter, cancellationToken), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitForCompletion()
|
||||||
|
{
|
||||||
|
if (_workerTask != null)
|
||||||
|
{
|
||||||
|
Task.WaitAll(_workerTask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enqueue(MqttBasePacket packet)
|
public void Enqueue(MqttBasePacket packet)
|
||||||
@@ -55,7 +66,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await SendQueuedPacketAsync(adapter, cancellationToken);
|
await SendNextQueuedPacketAsync(adapter, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -67,7 +78,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SendQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
|
private async Task SendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
MqttBasePacket packet = null;
|
MqttBasePacket packet = null;
|
||||||
try
|
try
|
||||||
@@ -78,6 +89,11 @@ namespace MQTTnet.Server
|
|||||||
throw new InvalidOperationException(); // should not happen
|
throw new InvalidOperationException(); // should not happen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false);
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.Trace<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
|
_logger.Trace<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
|
||||||
@@ -110,7 +126,10 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _clientSession.StopAsync();
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await _clientSession.StopAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,14 +96,14 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cancellationTokenSource?.Cancel(false);
|
_cancellationTokenSource?.Cancel(false);
|
||||||
|
|
||||||
|
PendingMessagesQueue.WaitForCompletion();
|
||||||
|
KeepAliveMonitor.WaitForCompletion();
|
||||||
|
|
||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
if (_adapter != null)
|
_adapter = null;
|
||||||
{
|
|
||||||
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
|
||||||
_adapter = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Info<MqttClientSession>("Client '{0}': Session stopped.", ClientId);
|
_logger.Info<MqttClientSession>("Client '{0}': Session stopped.", ClientId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,11 @@ namespace MQTTnet.Server
|
|||||||
MqttClientSession clientSession = null;
|
MqttClientSession clientSession = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken)
|
||||||
|
.ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
throw new MqttProtocolViolationException(
|
||||||
|
"The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
||||||
}
|
}
|
||||||
|
|
||||||
clientId = connectPacket.ClientId;
|
clientId = connectPacket.ClientId;
|
||||||
@@ -53,10 +55,13 @@ namespace MQTTnet.Server
|
|||||||
var connectReturnCode = ValidateConnection(connectPacket);
|
var connectReturnCode = ValidateConnection(connectPacket);
|
||||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||||
{
|
{
|
||||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket
|
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[]
|
||||||
{
|
{
|
||||||
ConnectReturnCode = connectReturnCode
|
new MqttConnAckPacket
|
||||||
}}).ConfigureAwait(false);
|
{
|
||||||
|
ConnectReturnCode = connectReturnCode
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,11 +69,14 @@ namespace MQTTnet.Server
|
|||||||
var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false);
|
var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false);
|
||||||
clientSession = result.Session;
|
clientSession = result.Session;
|
||||||
|
|
||||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket
|
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[]
|
||||||
{
|
{
|
||||||
ConnectReturnCode = connectReturnCode,
|
new MqttConnAckPacket
|
||||||
IsSessionPresent = result.IsExistingSession
|
{
|
||||||
}}).ConfigureAwait(false);
|
ConnectReturnCode = connectReturnCode,
|
||||||
|
IsSessionPresent = result.IsExistingSession
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
ClientConnectedCallback?.Invoke(new ConnectedMqttClient
|
ClientConnectedCallback?.Invoke(new ConnectedMqttClient
|
||||||
{
|
{
|
||||||
@@ -78,6 +86,9 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
|
await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error<MqttClientSessionsManager>(exception, exception.Message);
|
_logger.Error<MqttClientSessionsManager>(exception, exception.Message);
|
||||||
@@ -87,10 +98,11 @@ namespace MQTTnet.Server
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||||
|
clientAdapter.Dispose();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
// ignored
|
_logger.Error<MqttClientSessionsManager>(exception, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientDisconnectedCallback?.Invoke(new ConnectedMqttClient
|
ClientDisconnectedCallback?.Invoke(new ConnectedMqttClient
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
|
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
|
||||||
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
||||||
|
|
||||||
s.PublishAsync(message).Wait();
|
await s.PublishAsync(message);
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ namespace MQTTnet.Core.Tests
|
|||||||
|
|
||||||
public IMqttPacketSerializer PacketSerializer { get; } = new MqttPacketSerializer();
|
public IMqttPacketSerializer PacketSerializer { get; } = new MqttPacketSerializer();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public Task ConnectAsync(TimeSpan timeout)
|
public Task ConnectAsync(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
@@ -43,7 +47,17 @@ namespace MQTTnet.Core.Tests
|
|||||||
{
|
{
|
||||||
ThrowIfPartnerIsNull();
|
ThrowIfPartnerIsNull();
|
||||||
|
|
||||||
return Task.Run(() => _incomingPackets.Take(), cancellationToken);
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _incomingPackets.Take(cancellationToken);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnqueuePacketInternal(MqttBasePacket packet)
|
private void EnqueuePacketInternal(MqttBasePacket packet)
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace MQTTnet.TestApp.NetCore
|
|||||||
c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString());
|
c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user