mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-24 22:53:04 +08:00
Refactor package object model.
This commit is contained in:
@@ -66,10 +66,10 @@ namespace MQTTnet.AspNetCore
|
|||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
public long BytesSent { get; set; }
|
public long BytesSent { get; set; }
|
||||||
|
|
||||||
public long BytesReceived { get; set; }
|
public long BytesReceived { get; set; }
|
||||||
|
|
||||||
public Action ReadingPacketStartedCallback { get; set; }
|
public bool IsReadingPacket { get; private set; }
|
||||||
public Action ReadingPacketCompletedCallback { get; set; }
|
|
||||||
|
|
||||||
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// we did receive something but the message is not yet complete
|
// we did receive something but the message is not yet complete
|
||||||
ReadingPacketStartedCallback?.Invoke();
|
IsReadingPacket = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (readResult.IsCompleted)
|
else if (readResult.IsCompleted)
|
||||||
@@ -147,14 +147,14 @@ namespace MQTTnet.AspNetCore
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// completing the cannels makes sure that there is no more data read after a protocol error
|
// completing the channel makes sure that there is no more data read after a protocol error
|
||||||
_input?.Complete(e);
|
_input?.Complete(e);
|
||||||
_output?.Complete(e);
|
_output?.Complete(e);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ReadingPacketCompletedCallback?.Invoke();
|
IsReadingPacket = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
long BytesReceived { get; }
|
long BytesReceived { get; }
|
||||||
|
|
||||||
Action ReadingPacketStartedCallback { get; set; }
|
bool IsReadingPacket { get; }
|
||||||
|
|
||||||
Action ReadingPacketCompletedCallback { get; set; }
|
|
||||||
|
|
||||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ namespace MQTTnet.Adapter
|
|||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
||||||
|
|
||||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
||||||
|
|
||||||
public Action ReadingPacketStartedCallback { get; set; }
|
public bool IsReadingPacket { get; private set; }
|
||||||
public Action ReadingPacketCompletedCallback { get; set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,7 @@ namespace MQTTnet.Adapter
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapException(exception);
|
WrapAndThrowException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ namespace MQTTnet.Adapter
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapException(exception);
|
WrapAndThrowException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ namespace MQTTnet.Adapter
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapException(exception);
|
WrapAndThrowException(exception);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -214,7 +214,7 @@ namespace MQTTnet.Adapter
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapException(exception);
|
WrapAndThrowException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -253,7 +253,7 @@ namespace MQTTnet.Adapter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadingPacketStartedCallback?.Invoke();
|
IsReadingPacket = true;
|
||||||
|
|
||||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||||
if (fixedHeader.RemainingLength == 0)
|
if (fixedHeader.RemainingLength == 0)
|
||||||
@@ -293,7 +293,7 @@ namespace MQTTnet.Adapter
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ReadingPacketCompletedCallback?.Invoke();
|
IsReadingPacket = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +304,7 @@ namespace MQTTnet.Adapter
|
|||||||
exception is MqttCommunicationException;
|
exception is MqttCommunicationException;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WrapException(Exception exception)
|
static void WrapAndThrowException(Exception exception)
|
||||||
{
|
{
|
||||||
if (exception is IOException && exception.InnerException is SocketException innerException)
|
if (exception is IOException && exception.InnerException is SocketException innerException)
|
||||||
{
|
{
|
||||||
@@ -313,11 +313,15 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
if (exception is SocketException socketException)
|
if (exception is SocketException socketException)
|
||||||
{
|
{
|
||||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
if (socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
|
||||||
{
|
{
|
||||||
throw new OperationCanceledException();
|
throw new OperationCanceledException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(socketException);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception is COMException comException)
|
if (exception is COMException comException)
|
||||||
|
|||||||
@@ -395,9 +395,9 @@ namespace MQTTnet.Client
|
|||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
ushort identifier = 0;
|
ushort identifier = 0;
|
||||||
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue)
|
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0)
|
||||||
{
|
{
|
||||||
identifier = packetWithIdentifier.PacketIdentifier.Value;
|
identifier = packetWithIdentifier.PacketIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier))
|
using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier))
|
||||||
@@ -636,7 +636,8 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
|
var publishPacketDequeueResult =
|
||||||
|
await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
|
||||||
if (!publishPacketDequeueResult.IsSuccess)
|
if (!publishPacketDequeueResult.IsSuccess)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -677,6 +678,9 @@ namespace MQTTnet.Client
|
|||||||
throw new MqttProtocolViolationException("Received a not supported QoS level.");
|
throw new MqttProtocolViolationException("Received a not supported QoS level.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error(exception, "Error while handling application message.");
|
_logger.Error(exception, "Error while handling application message.");
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using MQTTnet.Client.Connecting;
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Client.Connecting;
|
||||||
using MQTTnet.Client.Disconnecting;
|
using MQTTnet.Client.Disconnecting;
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Client.Publishing;
|
using MQTTnet.Client.Publishing;
|
||||||
using MQTTnet.Client.Subscribing;
|
using MQTTnet.Client.Subscribing;
|
||||||
using MQTTnet.Client.Unsubscribing;
|
using MQTTnet.Client.Unsubscribing;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
||||||
|
|
||||||
@@ -30,8 +32,12 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
|
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
|
||||||
|
|
||||||
|
MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult);
|
||||||
|
|
||||||
MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options);
|
MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options);
|
||||||
|
|
||||||
|
MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes);
|
||||||
|
|
||||||
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
|
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
|
||||||
|
|
||||||
MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket);
|
MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using MQTTnet.Packets;
|
|||||||
|
|
||||||
namespace MQTTnet.Formatter
|
namespace MQTTnet.Formatter
|
||||||
{
|
{
|
||||||
public class MqttPacketFormatterAdapter
|
public sealed class MqttPacketFormatterAdapter
|
||||||
{
|
{
|
||||||
IMqttPacketFormatter _formatter;
|
IMqttPacketFormatter _formatter;
|
||||||
|
|
||||||
@@ -77,17 +77,6 @@ namespace MQTTnet.Formatter
|
|||||||
UseProtocolVersion(protocolVersion);
|
UseProtocolVersion(protocolVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UseProtocolVersion(MqttProtocolVersion protocolVersion)
|
|
||||||
{
|
|
||||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("MQTT protocol version is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtocolVersion = protocolVersion;
|
|
||||||
_formatter = GetMqttPacketFormatter(protocolVersion, Writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
|
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
|
||||||
{
|
{
|
||||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||||
@@ -116,7 +105,18 @@ namespace MQTTnet.Formatter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
|
void UseProtocolVersion(MqttProtocolVersion protocolVersion)
|
||||||
|
{
|
||||||
|
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("MQTT protocol version is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolVersion = protocolVersion;
|
||||||
|
_formatter = GetMqttPacketFormatter(protocolVersion, Writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
|
||||||
{
|
{
|
||||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using MQTTnet.Packets;
|
|||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
||||||
|
|
||||||
@@ -172,6 +173,18 @@ namespace MQTTnet.Formatter.V3
|
|||||||
return subscribePacket;
|
return subscribePacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
|
||||||
|
{
|
||||||
|
var subackPacket = new MqttSubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = subscribePacket.PacketIdentifier
|
||||||
|
};
|
||||||
|
|
||||||
|
subackPacket.ReturnCodes.AddRange(subscribeResult.ReturnCodes);
|
||||||
|
|
||||||
|
return subackPacket;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
|
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
@@ -182,6 +195,15 @@ namespace MQTTnet.Formatter.V3
|
|||||||
return unsubscribePacket;
|
return unsubscribePacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
|
||||||
|
{
|
||||||
|
return new MqttUnsubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = unsubscribePacket.PacketIdentifier,
|
||||||
|
ReasonCodes = reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
|
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
|
||||||
{
|
{
|
||||||
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
|
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
var topic = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
|
var topic = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
|
||||||
|
|
||||||
ushort? packetIdentifier = null;
|
ushort packetIdentifier = 0;
|
||||||
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
|
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
|
||||||
@@ -414,12 +414,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("PubRel packet has no packet identifier.");
|
throw new MqttProtocolViolationException("PubRel packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||||
}
|
}
|
||||||
@@ -432,12 +432,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
|
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -471,36 +471,36 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
|
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("PubRec packet has no packet identifier.");
|
throw new MqttProtocolViolationException("PubRec packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("PubComp packet has no packet identifier.");
|
throw new MqttProtocolViolationException("PubComp packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||||
}
|
}
|
||||||
@@ -509,12 +509,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
{
|
{
|
||||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||||
|
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Subscribe packet has no packet identifier.");
|
throw new MqttProtocolViolationException("Subscribe packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
if (packet.TopicFilters?.Count > 0)
|
if (packet.TopicFilters?.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -530,12 +530,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("SubAck packet has no packet identifier.");
|
throw new MqttProtocolViolationException("SubAck packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
if (packet.ReturnCodes?.Any() == true)
|
if (packet.ReturnCodes?.Any() == true)
|
||||||
{
|
{
|
||||||
@@ -552,12 +552,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
{
|
{
|
||||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||||
|
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier.");
|
throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
if (packet.TopicFilters?.Any() == true)
|
if (packet.TopicFilters?.Any() == true)
|
||||||
{
|
{
|
||||||
@@ -572,12 +572,12 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier.");
|
throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Formatter.V3
|
namespace MQTTnet.Formatter.V3
|
||||||
{
|
{
|
||||||
public class MqttV311PacketFormatter : MqttV310PacketFormatter
|
public sealed class MqttV311PacketFormatter : MqttV310PacketFormatter
|
||||||
{
|
{
|
||||||
public MqttV311PacketFormatter(IMqttPacketWriter packetWriter)
|
public MqttV311PacketFormatter(IMqttPacketWriter packetWriter)
|
||||||
: base(packetWriter)
|
: base(packetWriter)
|
||||||
|
|||||||
@@ -200,6 +200,18 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
|
||||||
|
{
|
||||||
|
var subackPacket = new MqttSubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = subscribePacket.PacketIdentifier
|
||||||
|
};
|
||||||
|
|
||||||
|
subackPacket.ReasonCodes.AddRange(subscribeResult.ReasonCodes);
|
||||||
|
|
||||||
|
return subackPacket;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
|
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
@@ -215,6 +227,15 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
|
||||||
|
{
|
||||||
|
return new MqttUnsubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = unsubscribePacket.PacketIdentifier,
|
||||||
|
ReasonCodes = reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
|
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
|
||||||
{
|
{
|
||||||
var packet = new MqttDisconnectPacket();
|
var packet = new MqttDisconnectPacket();
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Formatter.V5
|
namespace MQTTnet.Formatter.V5
|
||||||
{
|
{
|
||||||
public class MqttV500PacketEncoder
|
public sealed class MqttV500PacketEncoder
|
||||||
{
|
{
|
||||||
private readonly IMqttPacketWriter _packetWriter;
|
readonly IMqttPacketWriter _packetWriter;
|
||||||
|
|
||||||
public MqttV500PacketEncoder()
|
public MqttV500PacketEncoder()
|
||||||
: this(new MqttPacketWriter())
|
: this(new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttV500PacketEncoder(IMqttPacketWriter packetWriter)
|
public MqttV500PacketEncoder(IMqttPacketWriter packetWriter)
|
||||||
@@ -21,7 +20,6 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter = packetWriter;
|
_packetWriter = packetWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
@@ -52,7 +50,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.FreeBuffer();
|
_packetWriter.FreeBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
switch (packet)
|
switch (packet)
|
||||||
{
|
{
|
||||||
@@ -76,7 +74,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||||
@@ -176,7 +174,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||||
@@ -221,7 +219,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||||
@@ -235,12 +233,12 @@ namespace MQTTnet.Formatter.V5
|
|||||||
|
|
||||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
|
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -287,12 +285,12 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||||
|
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
if (packet.PacketIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
|
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
|
||||||
}
|
}
|
||||||
@@ -302,7 +300,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
throw new MqttProtocolViolationException("PubAck packet must contain a reason code.");
|
throw new MqttProtocolViolationException("PubAck packet must contain a reason code.");
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -320,9 +318,9 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
if (!packet.ReasonCode.HasValue)
|
if (!packet.ReasonCode.HasValue)
|
||||||
{
|
{
|
||||||
@@ -336,7 +334,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
|
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
|
||||||
{
|
{
|
||||||
@@ -347,9 +345,9 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
if (!packet.ReasonCode.HasValue)
|
if (!packet.ReasonCode.HasValue)
|
||||||
{
|
{
|
||||||
@@ -363,7 +361,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
|
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
|
||||||
{
|
{
|
||||||
@@ -374,16 +372,16 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
if (!packet.ReasonCode.HasValue)
|
if (!packet.ReasonCode.HasValue)
|
||||||
{
|
{
|
||||||
ThrowReasonCodeNotSetException();
|
ThrowReasonCodeNotSetException();
|
||||||
}
|
}
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -401,13 +399,13 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||||
|
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -448,13 +446,13 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||||
|
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -473,13 +471,13 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||||
|
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -497,13 +495,13 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||||
|
|
||||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
|
||||||
|
|
||||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
packetWriter.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||||
if (packet.Properties != null)
|
if (packet.Properties != null)
|
||||||
@@ -522,7 +520,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
if (!packet.ReasonCode.HasValue)
|
if (!packet.ReasonCode.HasValue)
|
||||||
{
|
{
|
||||||
@@ -545,17 +543,17 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePingReqPacket()
|
static byte EncodePingReqPacket()
|
||||||
{
|
{
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodePingRespPacket()
|
static byte EncodePingRespPacket()
|
||||||
{
|
{
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte EncodeAuthPacket(MqttAuthPacket packet, IMqttPacketWriter packetWriter)
|
static byte EncodeAuthPacket(MqttAuthPacket packet, IMqttPacketWriter packetWriter)
|
||||||
{
|
{
|
||||||
packetWriter.Write((byte)packet.ReasonCode);
|
packetWriter.Write((byte)packet.ReasonCode);
|
||||||
|
|
||||||
@@ -573,14 +571,16 @@ namespace MQTTnet.Formatter.V5
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ThrowReasonCodeNotSetException()
|
static void ThrowReasonCodeNotSetException()
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5.");
|
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ThrowIfPacketIdentifierIsInvalid(IMqttPacketWithIdentifier packet)
|
static void ThrowIfPacketIdentifierIsInvalid(ushort packetIdentifier, MqttBasePacket packet)
|
||||||
{
|
{
|
||||||
if (!packet.PacketIdentifier.HasValue)
|
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH(in cases where QoS > 0) Control Packets MUST contain a non-zero 16 - bit Packet Identifier[MQTT - 2.3.1 - 1].
|
||||||
|
|
||||||
|
if (packetIdentifier == 0)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
|
throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Formatter.V5
|
namespace MQTTnet.Formatter.V5
|
||||||
{
|
{
|
||||||
public class MqttV500PropertiesWriter
|
public sealed class MqttV500PropertiesWriter
|
||||||
{
|
{
|
||||||
// TODO: Consider lazy init on first write to avoid useless allocations.
|
readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
|
||||||
|
|
||||||
public int Length => _packetWriter.Length;
|
public int Length => _packetWriter.Length;
|
||||||
|
|
||||||
@@ -178,7 +177,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
Write(MqttPropertyId.ResponseInformation, value);
|
Write(MqttPropertyId.ResponseInformation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Write(MqttPropertyId id, bool? value)
|
void Write(MqttPropertyId id, bool? value)
|
||||||
{
|
{
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -189,7 +188,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
|
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Write(MqttPropertyId id, byte? value)
|
void Write(MqttPropertyId id, byte? value)
|
||||||
{
|
{
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -200,7 +199,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.Write(value.Value);
|
_packetWriter.Write(value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Write(MqttPropertyId id, ushort? value)
|
void Write(MqttPropertyId id, ushort? value)
|
||||||
{
|
{
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -211,7 +210,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.Write(value.Value);
|
_packetWriter.Write(value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
|
void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
|
||||||
{
|
{
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -222,7 +221,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.WriteVariableLengthInteger(value.Value);
|
_packetWriter.WriteVariableLengthInteger(value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
|
void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
|
||||||
{
|
{
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
@@ -236,7 +235,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.Write((byte)value.Value);
|
_packetWriter.Write((byte)value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Write(MqttPropertyId id, string value)
|
void Write(MqttPropertyId id, string value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
@@ -247,7 +246,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_packetWriter.WriteWithLengthPrefix(value);
|
_packetWriter.WriteWithLengthPrefix(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Write(MqttPropertyId id, byte[] value)
|
void Write(MqttPropertyId id, byte[] value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Runtime.ExceptionServices;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Exceptions;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
@@ -75,7 +76,7 @@ namespace MQTTnet.Implementations
|
|||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var networkStream = socket.GetStream();
|
var networkStream = socket.GetStream();
|
||||||
|
|
||||||
if (_options.TlsOptions?.UseTls == true)
|
if (_options.TlsOptions?.UseTls == true)
|
||||||
{
|
{
|
||||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||||
@@ -144,7 +145,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
throw new ObjectDisposedException(nameof(stream));
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
return await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
@@ -185,6 +186,10 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// This subsequent call is required to check whether the socket is still connected.
|
||||||
|
// Without this call a broken connection is only recognized at the next call.
|
||||||
|
await stream.WriteAsync(PlatformAbstractionLayer.EmptyByteArray, 0, 0, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
throw new ArgumentException("TLS certificate is not set.");
|
throw new ArgumentException("TLS certificate is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
|
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
|
||||||
if (!tlsCertificate.HasPrivateKey)
|
if (!tlsCertificate.HasPrivateKey)
|
||||||
{
|
{
|
||||||
@@ -73,16 +73,22 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
void Cleanup()
|
void Cleanup()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource?.Cancel(false);
|
try
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
foreach (var listener in _listeners)
|
|
||||||
{
|
{
|
||||||
listener.Dispose();
|
_cancellationTokenSource?.Cancel(false);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
_listeners.Clear();
|
foreach (var listener in _listeners)
|
||||||
|
{
|
||||||
|
listener.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_listeners.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
|
void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
_socket.Bind(_localEndPoint);
|
_socket.Bind(_localEndPoint);
|
||||||
_socket.Listen(_options.ConnectionBacklog);
|
_socket.Listen(_options.ConnectionBacklog);
|
||||||
|
|
||||||
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).Forget(_logger);
|
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).Forget(_logger);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -152,9 +152,7 @@ namespace MQTTnet.Implementations
|
|||||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||||
|
|
||||||
clientSocket.NoDelay = _options.NoDelay;
|
clientSocket.NoDelay = _options.NoDelay;
|
||||||
|
|
||||||
stream = clientSocket.GetStream();
|
stream = clientSocket.GetStream();
|
||||||
|
|
||||||
X509Certificate2 clientCertificate = null;
|
X509Certificate2 clientCertificate = null;
|
||||||
|
|
||||||
if (_tlsCertificate != null)
|
if (_tlsCertificate != null)
|
||||||
@@ -208,17 +206,17 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
stream?.Dispose();
|
stream?.Dispose();
|
||||||
clientSocket?.Dispose();
|
clientSocket?.Dispose();
|
||||||
|
|
||||||
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
|
|
||||||
remoteEndPoint,
|
|
||||||
_localEndPoint,
|
|
||||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
|
||||||
}
|
}
|
||||||
catch (Exception disposeException)
|
catch (Exception disposeException)
|
||||||
{
|
{
|
||||||
_logger.Error(disposeException, "Error while cleaning up client connection");
|
_logger.Error(disposeException, "Error while cleaning up client connection");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
|
||||||
|
remoteEndPoint,
|
||||||
|
_localEndPoint,
|
||||||
|
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
using System.Threading.Tasks;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public static class PlatformAbstractionLayer
|
public static class PlatformAbstractionLayer
|
||||||
{
|
{
|
||||||
public static Task CompletedTask
|
public static readonly byte[] EmptyByteArray = new byte[0];
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
#if NET452
|
|
||||||
return Task.FromResult(0);
|
|
||||||
#else
|
|
||||||
return Task.CompletedTask;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#if NET452
|
||||||
|
public static Task CompletedTask => Task.FromResult(0);
|
||||||
|
#else
|
||||||
|
public static Task CompletedTask => Task.CompletedTask;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static void Sleep(TimeSpan timeout)
|
||||||
|
{
|
||||||
|
#if NET452 || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1
|
||||||
|
System.Threading.Thread.Sleep(timeout);
|
||||||
|
#else
|
||||||
|
Task.Delay(timeout).Wait();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
public abstract class Disposable : IDisposable
|
public abstract class Disposable : IDisposable
|
||||||
{
|
{
|
||||||
protected bool IsDisposed { get; private set; } = false;
|
protected bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
protected void ThrowIfDisposed()
|
protected void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
@@ -18,13 +18,6 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
|
||||||
// ~Disposable()
|
|
||||||
// {
|
|
||||||
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
|
||||||
// Dispose(false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// This code added to correctly implement the disposable pattern.
|
// This code added to correctly implement the disposable pattern.
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
public class TestMqttChannel : IMqttChannel
|
public class TestMqttChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
private readonly MemoryStream _stream;
|
readonly MemoryStream _stream;
|
||||||
|
|
||||||
public TestMqttChannel(MemoryStream stream)
|
public TestMqttChannel(MemoryStream stream)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ namespace MQTTnet.PacketDispatcher
|
|||||||
{
|
{
|
||||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||||
{
|
{
|
||||||
timeoutToken.Token.Register(() => Fail(new MqttCommunicationTimedOutException()));
|
using (timeoutToken.Token.Register(() => Fail(new MqttCommunicationTimedOutException())))
|
||||||
|
{
|
||||||
var packet = await _taskCompletionSource.Task.ConfigureAwait(false);
|
var packet = await _taskCompletionSource.Task.ConfigureAwait(false);
|
||||||
return (TPacket)packet;
|
return (TPacket)packet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ namespace MQTTnet.PacketDispatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
ushort identifier = 0;
|
ushort identifier = 0;
|
||||||
if (packet is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue)
|
if (packet is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0)
|
||||||
{
|
{
|
||||||
identifier = packetWithIdentifier.PacketIdentifier.Value;
|
identifier = packetWithIdentifier.PacketIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = packet.GetType();
|
var type = packet.GetType();
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
public interface IMqttPacketWithIdentifier
|
public interface IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
ushort? PacketIdentifier { get; set; }
|
ushort PacketIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace MQTTnet.Packets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Added in MQTTv5.0.0.
|
/// Added in MQTTv5.0.0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MqttAuthPacket : MqttBasePacket
|
public sealed class MqttAuthPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public MqttAuthenticateReasonCode ReasonCode { get; set; }
|
public MqttAuthenticateReasonCode ReasonCode { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttAuthPacketProperties
|
public sealed class MqttAuthPacketProperties
|
||||||
{
|
{
|
||||||
public string AuthenticationMethod { get; set; }
|
public string AuthenticationMethod { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
|
||||||
{
|
|
||||||
public ushort? PacketIdentifier { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnAckPacket : MqttBasePacket
|
public sealed class MqttConnAckPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public MqttConnectReturnCode? ReturnCode { get; set; }
|
public MqttConnectReturnCode? ReturnCode { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnAckPacketProperties
|
public sealed class MqttConnAckPacketProperties
|
||||||
{
|
{
|
||||||
public uint? SessionExpiryInterval { get; set; }
|
public uint? SessionExpiryInterval { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnectPacket : MqttBasePacket
|
public sealed class MqttConnectPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public string ClientId { get; set; }
|
public string ClientId { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnectPacketProperties
|
public sealed class MqttConnectPacketProperties
|
||||||
{
|
{
|
||||||
public uint? WillDelayInterval { get; set; }
|
public uint? WillDelayInterval { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttDisconnectPacket : MqttBasePacket
|
public sealed class MqttDisconnectPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttDisconnectPacketProperties
|
public sealed class MqttDisconnectPacketProperties
|
||||||
{
|
{
|
||||||
public uint? SessionExpiryInterval { get; set; }
|
public uint? SessionExpiryInterval { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPingReqPacket : MqttBasePacket
|
public sealed class MqttPingReqPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
|
// This is a minor performance improvement.
|
||||||
|
public static MqttPingReqPacket Instance = new MqttPingReqPacket();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "PingReq";
|
return "PingReq";
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPingRespPacket : MqttBasePacket
|
public sealed class MqttPingRespPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
|
// This is a minor performance improvement.
|
||||||
|
public static MqttPingRespPacket Instance = new MqttPingRespPacket();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "PingResp";
|
return "PingResp";
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubAckPacket : MqttBasePublishPacket
|
public sealed class MqttPubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
public MqttPubAckReasonCode? ReasonCode { get; set; }
|
public MqttPubAckReasonCode? ReasonCode { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubAckPacketProperties
|
public sealed class MqttPubAckPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubCompPacket : MqttBasePublishPacket
|
public sealed class MqttPubCompPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
public MqttPubCompReasonCode? ReasonCode { get; set; }
|
public MqttPubCompReasonCode? ReasonCode { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubCompPacketProperties
|
public sealed class MqttPubCompPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRecPacket : MqttBasePublishPacket
|
public sealed class MqttPubRecPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
public MqttPubRecReasonCode? ReasonCode { get; set; }
|
public MqttPubRecReasonCode? ReasonCode { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRecPacketProperties
|
public sealed class MqttPubRecPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRelPacket : MqttBasePublishPacket
|
public sealed class MqttPubRelPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
public MqttPubRelReasonCode? ReasonCode { get; set; }
|
public MqttPubRelReasonCode? ReasonCode { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRelPacketProperties
|
public sealed class MqttPubRelPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPublishPacket : MqttBasePublishPacket
|
public sealed class MqttPublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
public bool Retain { get; set; }
|
public bool Retain { get; set; }
|
||||||
|
|
||||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttPublishPacketProperties
|
public sealed class MqttPublishPacketProperties
|
||||||
{
|
{
|
||||||
public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; }
|
public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort? PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
public List<MqttSubscribeReturnCode> ReturnCodes { get; set; } = new List<MqttSubscribeReturnCode>();
|
public List<MqttSubscribeReturnCode> ReturnCodes { get; set; } = new List<MqttSubscribeReturnCode>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubAckPacketProperties
|
public sealed class MqttSubAckPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort? PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
public List<MqttTopicFilter> TopicFilters { get; set; } = new List<MqttTopicFilter>();
|
public List<MqttTopicFilter> TopicFilters { get; set; } = new List<MqttTopicFilter>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubscribePacketProperties
|
public sealed class MqttSubscribePacketProperties
|
||||||
{
|
{
|
||||||
public uint? SubscriptionIdentifier { get; set; }
|
public uint? SubscriptionIdentifier { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ using MQTTnet.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort? PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
#region Added in MQTTv5
|
#region Added in MQTTv5
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubAckPacketProperties
|
public sealed class MqttUnsubAckPacketProperties
|
||||||
{
|
{
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort? PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
public List<string> TopicFilters { get; set; } = new List<string>();
|
public List<string> TopicFilters { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubscribePacketProperties
|
public sealed class MqttUnsubscribePacketProperties
|
||||||
{
|
{
|
||||||
public List<MqttUserProperty> UserProperties { get; set; }
|
public List<MqttUserProperty> UserProperties { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Packets
|
namespace MQTTnet.Packets
|
||||||
{
|
{
|
||||||
public class MqttUserProperty
|
public sealed class MqttUserProperty
|
||||||
{
|
{
|
||||||
public MqttUserProperty(string name, string value)
|
public MqttUserProperty(string name, string value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace MQTTnet.Server
|
|||||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||||
|
|
||||||
TimeSpan DefaultCommunicationTimeout { get; }
|
TimeSpan DefaultCommunicationTimeout { get; }
|
||||||
|
TimeSpan KeepAliveMonitorInterval { get; }
|
||||||
|
|
||||||
IMqttServerConnectionValidator ConnectionValidator { get; }
|
IMqttServerConnectionValidator ConnectionValidator { get; }
|
||||||
IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; }
|
IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; }
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ namespace MQTTnet.Server
|
|||||||
readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
|
readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
|
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
|
||||||
readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
|
|
||||||
readonly MqttClientSessionsManager _sessionsManager;
|
readonly MqttClientSessionsManager _sessionsManager;
|
||||||
|
|
||||||
readonly IMqttNetScopedLogger _logger;
|
readonly IMqttNetScopedLogger _logger;
|
||||||
@@ -36,7 +35,6 @@ namespace MQTTnet.Server
|
|||||||
readonly DateTime _connectedTimestamp;
|
readonly DateTime _connectedTimestamp;
|
||||||
|
|
||||||
volatile Task _packageReceiverTask;
|
volatile Task _packageReceiverTask;
|
||||||
DateTime _lastPacketReceivedTimestamp;
|
|
||||||
DateTime _lastNonKeepAlivePacketReceivedTimestamp;
|
DateTime _lastNonKeepAlivePacketReceivedTimestamp;
|
||||||
|
|
||||||
long _receivedPacketsCount;
|
long _receivedPacketsCount;
|
||||||
@@ -69,17 +67,19 @@ namespace MQTTnet.Server
|
|||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
_logger = logger.CreateScopedLogger(nameof(MqttClientConnection));
|
_logger = logger.CreateScopedLogger(nameof(MqttClientConnection));
|
||||||
|
|
||||||
_keepAliveMonitor = new MqttClientKeepAliveMonitor(ConnectPacket.ClientId, () => StopAsync(), logger);
|
|
||||||
|
|
||||||
_connectedTimestamp = DateTime.UtcNow;
|
_connectedTimestamp = DateTime.UtcNow;
|
||||||
_lastPacketReceivedTimestamp = _connectedTimestamp;
|
LastPacketReceivedTimestamp = _connectedTimestamp;
|
||||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
_lastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttConnectPacket ConnectPacket { get; }
|
public MqttConnectPacket ConnectPacket { get; }
|
||||||
|
|
||||||
public string ClientId => ConnectPacket.ClientId;
|
public string ClientId => ConnectPacket.ClientId;
|
||||||
|
|
||||||
|
public bool IsReadingPacket => _channelAdapter.IsReadingPacket;
|
||||||
|
|
||||||
|
public DateTime LastPacketReceivedTimestamp { get; private set; }
|
||||||
|
|
||||||
public MqttClientSession Session { get; }
|
public MqttClientSession Session { get; }
|
||||||
|
|
||||||
public Task StopAsync(bool isTakeover = false)
|
public Task StopAsync(bool isTakeover = false)
|
||||||
@@ -115,7 +115,7 @@ namespace MQTTnet.Server
|
|||||||
status.SentPacketsCount = Interlocked.Read(ref _sentPacketsCount);
|
status.SentPacketsCount = Interlocked.Read(ref _sentPacketsCount);
|
||||||
|
|
||||||
status.ConnectedTimestamp = _connectedTimestamp;
|
status.ConnectedTimestamp = _connectedTimestamp;
|
||||||
status.LastPacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
status.LastPacketReceivedTimestamp = LastPacketReceivedTimestamp;
|
||||||
status.LastNonKeepAlivePacketReceivedTimestamp = _lastNonKeepAlivePacketReceivedTimestamp;
|
status.LastNonKeepAlivePacketReceivedTimestamp = _lastNonKeepAlivePacketReceivedTimestamp;
|
||||||
|
|
||||||
status.BytesSent = _channelAdapter.BytesSent;
|
status.BytesSent = _channelAdapter.BytesSent;
|
||||||
@@ -140,16 +140,10 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
_logger.Info("Client '{0}': Session started.", ClientId);
|
_logger.Info("Client '{0}': Session started.", ClientId);
|
||||||
|
|
||||||
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted;
|
|
||||||
_channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted;
|
|
||||||
|
|
||||||
Session.WillMessage = ConnectPacket.WillMessage;
|
Session.WillMessage = ConnectPacket.WillMessage;
|
||||||
|
|
||||||
Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token).Forget(_logger);
|
Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token).Forget(_logger);
|
||||||
|
|
||||||
// TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor.
|
|
||||||
_keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token);
|
|
||||||
|
|
||||||
await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext)).ConfigureAwait(false);
|
await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext)).ConfigureAwait(false);
|
||||||
|
|
||||||
Session.IsCleanSession = false;
|
Session.IsCleanSession = false;
|
||||||
@@ -164,15 +158,13 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref _sentPacketsCount);
|
Interlocked.Increment(ref _sentPacketsCount);
|
||||||
_lastPacketReceivedTimestamp = DateTime.UtcNow;
|
LastPacketReceivedTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
|
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
|
||||||
{
|
{
|
||||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
_lastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
_keepAliveMonitor.PacketReceived();
|
|
||||||
|
|
||||||
if (packet is MqttPublishPacket publishPacket)
|
if (packet is MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
await HandleIncomingPublishPacketAsync(publishPacket).ConfigureAwait(false);
|
await HandleIncomingPublishPacketAsync(publishPacket).ConfigureAwait(false);
|
||||||
@@ -252,9 +244,6 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
_packetDispatcher.Reset();
|
_packetDispatcher.Reset();
|
||||||
|
|
||||||
_channelAdapter.ReadingPacketStartedCallback = null;
|
|
||||||
_channelAdapter.ReadingPacketCompletedCallback = null;
|
|
||||||
|
|
||||||
_packageReceiverTask = null;
|
_packageReceiverTask = null;
|
||||||
|
|
||||||
if (_isTakeover)
|
if (_isTakeover)
|
||||||
@@ -302,10 +291,10 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
async Task HandleIncomingSubscribePacketAsync(MqttSubscribePacket subscribePacket)
|
async Task HandleIncomingSubscribePacketAsync(MqttSubscribePacket subscribePacket)
|
||||||
{
|
{
|
||||||
// TODO: Let the channel adapter create the packet.
|
|
||||||
var subscribeResult = await Session.SubscriptionsManager.SubscribeAsync(subscribePacket, ConnectPacket).ConfigureAwait(false);
|
var subscribeResult = await Session.SubscriptionsManager.SubscribeAsync(subscribePacket, ConnectPacket).ConfigureAwait(false);
|
||||||
|
var subAckPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateSubAckPacket(subscribePacket, subscribeResult);
|
||||||
|
|
||||||
await SendAsync(subscribeResult.ResponsePacket).ConfigureAwait(false);
|
await SendAsync(subAckPacket).ConfigureAwait(false);
|
||||||
|
|
||||||
if (subscribeResult.CloseConnection)
|
if (subscribeResult.CloseConnection)
|
||||||
{
|
{
|
||||||
@@ -318,9 +307,10 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
async Task HandleIncomingUnsubscribePacketAsync(MqttUnsubscribePacket unsubscribePacket)
|
async Task HandleIncomingUnsubscribePacketAsync(MqttUnsubscribePacket unsubscribePacket)
|
||||||
{
|
{
|
||||||
// TODO: Let the channel adapter create the packet.
|
var reasonCodes = await Session.SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false);
|
||||||
var unsubscribeResult = await Session.SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false);
|
var unsubAckPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateUnsubAckPacket(unsubscribePacket, reasonCodes);
|
||||||
await SendAsync(unsubscribeResult).ConfigureAwait(false);
|
|
||||||
|
await SendAsync(unsubAckPacket).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||||
@@ -389,7 +379,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
queuedApplicationMessage = await Session.ApplicationMessagesQueue.TakeAsync(cancellationToken).ConfigureAwait(false);
|
queuedApplicationMessage = await Session.ApplicationMessagesQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (queuedApplicationMessage == null)
|
if (queuedApplicationMessage == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -503,15 +493,5 @@ namespace MQTTnet.Server
|
|||||||
Interlocked.Increment(ref _receivedApplicationMessagesCount);
|
Interlocked.Increment(ref _receivedApplicationMessagesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnAdapterReadingPacketCompleted()
|
|
||||||
{
|
|
||||||
_keepAliveMonitor?.Resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnAdapterReadingPacketStarted()
|
|
||||||
{
|
|
||||||
_keepAliveMonitor?.Pause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
|
||||||
{
|
|
||||||
public sealed class MqttClientKeepAliveMonitor
|
|
||||||
{
|
|
||||||
readonly Stopwatch _lastPacketReceivedTracker = new Stopwatch();
|
|
||||||
|
|
||||||
readonly string _clientId;
|
|
||||||
readonly Func<Task> _keepAliveElapsedCallback;
|
|
||||||
readonly IMqttNetScopedLogger _logger;
|
|
||||||
|
|
||||||
bool _isPaused;
|
|
||||||
|
|
||||||
public MqttClientKeepAliveMonitor(string clientId, Func<Task> keepAliveElapsedCallback, IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
|
||||||
_keepAliveElapsedCallback = keepAliveElapsedCallback ?? throw new ArgumentNullException(nameof(keepAliveElapsedCallback));
|
|
||||||
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
_logger = logger.CreateScopedLogger(nameof(MqttClientKeepAliveMonitor));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start(int keepAlivePeriod, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (keepAlivePeriod == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken), cancellationToken).Forget(_logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause()
|
|
||||||
{
|
|
||||||
_isPaused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Resume()
|
|
||||||
{
|
|
||||||
_isPaused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PacketReceived()
|
|
||||||
{
|
|
||||||
_lastPacketReceivedTracker.Restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_lastPacketReceivedTracker.Restart();
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// Values described here: [MQTT-3.1.2-24].
|
|
||||||
// If the client sends 5 sec. the server will allow up to 7.5 seconds.
|
|
||||||
// If the client sends 1 sec. the server will allow up to 1.5 seconds.
|
|
||||||
if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds >= keepAlivePeriod * 1.5D)
|
|
||||||
{
|
|
||||||
_logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", _clientId);
|
|
||||||
await _keepAliveElapsedCallback().ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The server checks the keep alive timeout every 50 % of the overall keep alive timeout
|
|
||||||
// because the server allows 1.5 times the keep alive value. This means that a value of 5 allows
|
|
||||||
// up to 7.5 seconds. With an interval of 2.5 (5 / 2) the 7.5 is also affected. Waiting the whole
|
|
||||||
// keep alive time will hit at 10 instead of 7.5 (but only one time instead of two times).
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(keepAlivePeriod * 0.5D), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientId);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSession
|
public sealed class MqttClientSession
|
||||||
{
|
{
|
||||||
readonly IMqttNetScopedLogger _logger;
|
readonly IMqttNetScopedLogger _logger;
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSessionApplicationMessagesQueue : Disposable
|
public sealed class MqttClientSessionApplicationMessagesQueue : IDisposable
|
||||||
{
|
{
|
||||||
private readonly AsyncQueue<MqttQueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttQueuedApplicationMessage>();
|
readonly AsyncQueue<MqttQueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttQueuedApplicationMessage>();
|
||||||
|
|
||||||
private readonly IMqttServerOptions _options;
|
readonly IMqttServerOptions _options;
|
||||||
|
|
||||||
public MqttClientSessionApplicationMessagesQueue(IMqttServerOptions options)
|
public MqttClientSessionApplicationMessagesQueue(IMqttServerOptions options)
|
||||||
{
|
{
|
||||||
@@ -32,22 +32,6 @@ namespace MQTTnet.Server
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_messageQueue.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<MqttQueuedApplicationMessage> TakeAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
if (!dequeueResult.IsSuccess)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dequeueResult.Item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage)
|
public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage)
|
||||||
{
|
{
|
||||||
if (queuedApplicationMessage == null) throw new ArgumentNullException(nameof(queuedApplicationMessage));
|
if (queuedApplicationMessage == null) throw new ArgumentNullException(nameof(queuedApplicationMessage));
|
||||||
@@ -71,14 +55,25 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public async Task<MqttQueuedApplicationMessage> DequeueAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (disposing)
|
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
if (!dequeueResult.IsSuccess)
|
||||||
{
|
{
|
||||||
_messageQueue.Dispose();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
return dequeueResult.Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_messageQueue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_messageQueue?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using MQTTnet.Server.Status;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -63,6 +64,11 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MqttClientConnection> GetConnections()
|
||||||
|
{
|
||||||
|
return _connections.Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter)
|
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter)
|
||||||
{
|
{
|
||||||
if (clientAdapter is null) throw new ArgumentNullException(nameof(clientAdapter));
|
if (clientAdapter is null) throw new ArgumentNullException(nameof(clientAdapter));
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using MQTTnet.Packets;
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSubscribeResult
|
public sealed class MqttClientSubscribeResult
|
||||||
{
|
{
|
||||||
public MqttSubAckPacket ResponsePacket { get; set; }
|
public List<MqttSubscribeReturnCode> ReturnCodes { get; } = new List<MqttSubscribeReturnCode>();
|
||||||
|
|
||||||
|
public List<MqttSubscribeReasonCode> ReasonCodes { get; } = new List<MqttSubscribeReasonCode>();
|
||||||
|
|
||||||
public bool CloseConnection { get; set; }
|
public bool CloseConnection { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSubscriptionsManager
|
public sealed class MqttClientSubscriptionsManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, MqttTopicFilter> _subscriptions = new Dictionary<string, MqttTopicFilter>();
|
readonly Dictionary<string, MqttTopicFilter> _subscriptions = new Dictionary<string, MqttTopicFilter>();
|
||||||
private readonly MqttClientSession _clientSession;
|
readonly MqttClientSession _clientSession;
|
||||||
private readonly IMqttServerOptions _serverOptions;
|
readonly IMqttServerOptions _serverOptions;
|
||||||
private readonly MqttServerEventDispatcher _eventDispatcher;
|
readonly MqttServerEventDispatcher _eventDispatcher;
|
||||||
|
|
||||||
public MqttClientSubscriptionsManager(MqttClientSession clientSession, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions)
|
public MqttClientSubscriptionsManager(MqttClientSession clientSession, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions)
|
||||||
{
|
{
|
||||||
@@ -28,15 +28,7 @@ namespace MQTTnet.Server
|
|||||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||||
if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
|
if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
|
||||||
|
|
||||||
var result = new MqttClientSubscribeResult
|
var result = new MqttClientSubscribeResult();
|
||||||
{
|
|
||||||
ResponsePacket = new MqttSubAckPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = subscribePacket.PacketIdentifier
|
|
||||||
},
|
|
||||||
|
|
||||||
CloseConnection = false
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var originalTopicFilter in subscribePacket.TopicFilters)
|
foreach (var originalTopicFilter in subscribePacket.TopicFilters)
|
||||||
{
|
{
|
||||||
@@ -46,13 +38,13 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
if (finalTopicFilter == null || string.IsNullOrEmpty(finalTopicFilter.Topic) || !interceptorContext.AcceptSubscription)
|
if (finalTopicFilter == null || string.IsNullOrEmpty(finalTopicFilter.Topic) || !interceptorContext.AcceptSubscription)
|
||||||
{
|
{
|
||||||
result.ResponsePacket.ReturnCodes.Add(MqttSubscribeReturnCode.Failure);
|
result.ReturnCodes.Add(MqttSubscribeReturnCode.Failure);
|
||||||
result.ResponsePacket.ReasonCodes.Add(MqttSubscribeReasonCode.UnspecifiedError);
|
result.ReasonCodes.Add(MqttSubscribeReasonCode.UnspecifiedError);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.ResponsePacket.ReturnCodes.Add(ConvertToSubscribeReturnCode(finalTopicFilter.QualityOfServiceLevel));
|
result.ReturnCodes.Add(ConvertToSubscribeReturnCode(finalTopicFilter.QualityOfServiceLevel));
|
||||||
result.ResponsePacket.ReasonCodes.Add(ConvertToSubscribeReasonCode(finalTopicFilter.QualityOfServiceLevel));
|
result.ReasonCodes.Add(ConvertToSubscribeReasonCode(finalTopicFilter.QualityOfServiceLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interceptorContext.CloseConnection)
|
if (interceptorContext.CloseConnection)
|
||||||
@@ -98,21 +90,18 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttUnsubAckPacket> UnsubscribeAsync(MqttUnsubscribePacket unsubscribePacket)
|
public async Task<List<MqttUnsubscribeReasonCode>> UnsubscribeAsync(MqttUnsubscribePacket unsubscribePacket)
|
||||||
{
|
{
|
||||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||||
|
|
||||||
var unsubAckPacket = new MqttUnsubAckPacket
|
var reasonCodes = new List<MqttUnsubscribeReasonCode>();
|
||||||
{
|
|
||||||
PacketIdentifier = unsubscribePacket.PacketIdentifier
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||||
{
|
{
|
||||||
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
|
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
|
||||||
if (!interceptorContext.AcceptUnsubscription)
|
if (!interceptorContext.AcceptUnsubscription)
|
||||||
{
|
{
|
||||||
unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.ImplementationSpecificError);
|
reasonCodes.Add(MqttUnsubscribeReasonCode.ImplementationSpecificError);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,11 +109,11 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
if (_subscriptions.Remove(topicFilter))
|
if (_subscriptions.Remove(topicFilter))
|
||||||
{
|
{
|
||||||
unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.Success);
|
reasonCodes.Add(MqttUnsubscribeReasonCode.Success);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.NoSubscriptionExisted);
|
reasonCodes.Add(MqttUnsubscribeReasonCode.NoSubscriptionExisted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +123,7 @@ namespace MQTTnet.Server
|
|||||||
await _eventDispatcher.SafeNotifyClientUnsubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
|
await _eventDispatcher.SafeNotifyClientUnsubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return unsubAckPacket;
|
return reasonCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
||||||
@@ -184,7 +173,7 @@ namespace MQTTnet.Server
|
|||||||
return CreateSubscriptionResult(qosLevel, qosLevels);
|
return CreateSubscriptionResult(qosLevel, qosLevels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MqttSubscribeReturnCode ConvertToSubscribeReturnCode(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
static MqttSubscribeReturnCode ConvertToSubscribeReturnCode(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
switch (qualityOfServiceLevel)
|
switch (qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
@@ -195,7 +184,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MqttSubscribeReasonCode ConvertToSubscribeReasonCode(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
static MqttSubscribeReasonCode ConvertToSubscribeReasonCode(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
switch (qualityOfServiceLevel)
|
switch (qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
@@ -206,7 +195,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttSubscriptionInterceptorContext> InterceptSubscribeAsync(MqttTopicFilter topicFilter)
|
async Task<MqttSubscriptionInterceptorContext> InterceptSubscribeAsync(MqttTopicFilter topicFilter)
|
||||||
{
|
{
|
||||||
var context = new MqttSubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
|
var context = new MqttSubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
|
||||||
if (_serverOptions.SubscriptionInterceptor != null)
|
if (_serverOptions.SubscriptionInterceptor != null)
|
||||||
@@ -217,7 +206,7 @@ namespace MQTTnet.Server
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttUnsubscriptionInterceptorContext> InterceptUnsubscribeAsync(string topicFilter)
|
async Task<MqttUnsubscriptionInterceptorContext> InterceptUnsubscribeAsync(string topicFilter)
|
||||||
{
|
{
|
||||||
var context = new MqttUnsubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
|
var context = new MqttUnsubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
|
||||||
if (_serverOptions.UnsubscriptionInterceptor != null)
|
if (_serverOptions.UnsubscriptionInterceptor != null)
|
||||||
@@ -228,7 +217,7 @@ namespace MQTTnet.Server
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CheckSubscriptionsResult CreateSubscriptionResult(MqttQualityOfServiceLevel qosLevel, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
|
static CheckSubscriptionsResult CreateSubscriptionResult(MqttQualityOfServiceLevel qosLevel, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
|
||||||
{
|
{
|
||||||
MqttQualityOfServiceLevel effectiveQoS;
|
MqttQualityOfServiceLevel effectiveQoS;
|
||||||
if (subscribedQoSLevels.Contains(qosLevel))
|
if (subscribedQoSLevels.Contains(qosLevel))
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public sealed class MqttServerKeepAliveMonitor
|
||||||
|
{
|
||||||
|
readonly IMqttServerOptions _options;
|
||||||
|
readonly MqttClientSessionsManager _sessionsManager;
|
||||||
|
readonly IMqttNetScopedLogger _logger;
|
||||||
|
|
||||||
|
public MqttServerKeepAliveMonitor(IMqttServerOptions options, MqttClientSessionsManager sessionsManager, IMqttNetLogger logger)
|
||||||
|
{
|
||||||
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
||||||
|
|
||||||
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
|
_logger = logger.CreateScopedLogger(nameof(MqttServerKeepAliveMonitor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// The keep alive monitor spawns a real new thread (LongRunning) because it does not
|
||||||
|
// support async/await. Async etc. is avoided here because the thread will usually check
|
||||||
|
// the connections every few milliseconds and thus the context changes (due to async) are
|
||||||
|
// only consuming resources. Also there is just 1 thread for the entire server which is fine at all!
|
||||||
|
Task.Factory.StartNew(_ => DoWork(cancellationToken), cancellationToken, TaskCreationOptions.LongRunning).Forget(_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoWork(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Info("Starting keep alive monitor.");
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
TryMaintainConnections();
|
||||||
|
PlatformAbstractionLayer.Sleep(_options.KeepAliveMonitorInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Unhandled exception while checking keep alive timeouts.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_logger.Verbose("Stopped checking keep alive timeout.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryMaintainConnections()
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
foreach (var connection in _sessionsManager.GetConnections())
|
||||||
|
{
|
||||||
|
TryMaintainConnection(connection, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryMaintainConnection(MqttClientConnection connection, DateTime now)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//if (connection.IsStopped)
|
||||||
|
//{
|
||||||
|
// // The connection is already dead so there is no need to check it.
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (connection.ConnectPacket.KeepAlivePeriod == 0)
|
||||||
|
{
|
||||||
|
// The keep alive feature is not used by the current connection.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection.IsReadingPacket)
|
||||||
|
{
|
||||||
|
// The connection is currently reading a (large) packet. So it is obviously
|
||||||
|
// doing something and thus "connected".
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values described here: [MQTT-3.1.2-24].
|
||||||
|
// If the client sends 5 sec. the server will allow up to 7.5 seconds.
|
||||||
|
// If the client sends 1 sec. the server will allow up to 1.5 seconds.
|
||||||
|
var maxDurationWithoutPacket = connection.ConnectPacket.KeepAlivePeriod * 1.5D;
|
||||||
|
|
||||||
|
var secondsWithoutPackage = (now - connection.LastPacketReceivedTimestamp).TotalSeconds;
|
||||||
|
if (secondsWithoutPackage < maxDurationWithoutPacket)
|
||||||
|
{
|
||||||
|
// A packet was received before the timeout is affected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", connection.ClientId);
|
||||||
|
|
||||||
|
// Execute the disconnection in background so that the keep alive monitor can continue
|
||||||
|
// with checking other connections.
|
||||||
|
Task.Run(() => connection.StopAsync());
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Client {0}: Unhandled exception while checking keep alive timeouts.", connection.ClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
|
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
|
public TimeSpan KeepAliveMonitorInterval { get; set; } = TimeSpan.FromMilliseconds(500);
|
||||||
|
|
||||||
public IMqttServerConnectionValidator ConnectionValidator { get; set; }
|
public IMqttServerConnectionValidator ConnectionValidator { get; set; }
|
||||||
|
|
||||||
public IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; set; }
|
public IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; set; }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
@@ -9,7 +10,65 @@ namespace MQTTnet.Tests
|
|||||||
public class AsyncLock_Tests
|
public class AsyncLock_Tests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void AsyncLock()
|
public async Task Lock_Serial_Calls()
|
||||||
|
{
|
||||||
|
var sum = 0;
|
||||||
|
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
for (var i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
using (await @lock.WaitAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
sum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(100, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(TaskCanceledException))]
|
||||||
|
public async Task Test_Cancellation()
|
||||||
|
{
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// This call will never "release" the lock due to missing _using_.
|
||||||
|
await @lock.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//[TestMethod]
|
||||||
|
//public async Task Test_Cancellation_With_Later_Access()
|
||||||
|
//{
|
||||||
|
// var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// var releaser = await @lock.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
// {
|
||||||
|
// await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (OperationCanceledException)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// releaser.Dispose();
|
||||||
|
|
||||||
|
// using (await @lock.WaitAsync().ConfigureAwait(false))
|
||||||
|
// {
|
||||||
|
// // When the method finished, the thread got access.
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Lock_10_Parallel_Tasks()
|
||||||
{
|
{
|
||||||
const int ThreadsCount = 10;
|
const int ThreadsCount = 10;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
@@ -36,9 +37,22 @@ namespace MQTTnet.Tests
|
|||||||
Assert.AreEqual(3, queue.Count);
|
Assert.AreEqual(3, queue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Cancellation()
|
||||||
|
{
|
||||||
|
var queue = new AsyncQueue<int>();
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
success = (await queue.TryDequeueAsync(cancellationTokenSource.Token)).IsSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(false, success);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Preserve_ProcessAsync()
|
public async Task Process_Async()
|
||||||
{
|
{
|
||||||
var queue = new AsyncQueue<int>();
|
var queue = new AsyncQueue<int>();
|
||||||
|
|
||||||
@@ -50,7 +64,37 @@ namespace MQTTnet.Tests
|
|||||||
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
queue.Enqueue(1);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
queue.Enqueue(2);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
queue.Enqueue(3);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.AreEqual(6, sum);
|
||||||
|
Assert.AreEqual(TaskStatus.RanToCompletion, worker.Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Process_Async_With_Initial_Delay()
|
||||||
|
{
|
||||||
|
var queue = new AsyncQueue<int>();
|
||||||
|
|
||||||
|
var sum = 0;
|
||||||
|
var worker = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (sum < 6)
|
||||||
|
{
|
||||||
|
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// This line is the diff to test _Process_Async_
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
queue.Enqueue(1);
|
queue.Enqueue(1);
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using System;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.LowLevelClient;
|
using MQTTnet.LowLevelClient;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -34,7 +36,7 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServerAsync();
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
var factory = new MqttFactory();
|
var factory = new MqttFactory();
|
||||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||||
@@ -50,7 +52,7 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServerAsync();
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
var factory = new MqttFactory();
|
var factory = new MqttFactory();
|
||||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||||
@@ -71,7 +73,7 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServerAsync();
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
var factory = new MqttFactory();
|
var factory = new MqttFactory();
|
||||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||||
@@ -89,6 +91,39 @@ namespace MQTTnet.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Loose_Connection()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
testEnvironment.ServerPort = 8364;
|
||||||
|
var server = await testEnvironment.StartServerAsync();
|
||||||
|
var client = await testEnvironment.ConnectLowLevelClientAsync(o => o.WithCommunicationTimeout(TimeSpan.Zero));
|
||||||
|
|
||||||
|
await Authenticate(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await server.StopAsync();
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (MqttCommunicationException exception)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(exception.InnerException is SocketException);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Assert.Fail("Wrong exception type thrown.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("This MUST fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
||||||
{
|
{
|
||||||
await client.SendAsync(new MqttConnectPacket
|
await client.SendAsync(new MqttConnectPacket
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ using MQTTnet.Server;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.LowLevelClient;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
@@ -100,6 +102,25 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
return ConnectClientAsync(new MqttClientOptionsBuilder());
|
return ConnectClientAsync(new MqttClientOptionsBuilder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<ILowLevelMqttClient> ConnectLowLevelClientAsync()
|
||||||
|
{
|
||||||
|
return ConnectLowLevelClientAsync(o => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ILowLevelMqttClient> ConnectLowLevelClientAsync(Action<MqttClientOptionsBuilder> optionsBuilder)
|
||||||
|
{
|
||||||
|
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder();
|
||||||
|
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||||
|
optionsBuilder.Invoke(options);
|
||||||
|
|
||||||
|
var client = new MqttFactory().CreateLowLevelMqttClient();
|
||||||
|
await client.ConnectAsync(options.Build(), CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IMqttClient> ConnectClientAsync(MqttClientOptionsBuilder options)
|
public async Task<IMqttClient> ConnectClientAsync(MqttClientOptionsBuilder options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
@@ -111,7 +132,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IMqttClient> ConnectClientAsync(IMqttClientOptions options)
|
public async Task<IMqttClient> ConnectClientAsync(IMqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
{
|
{
|
||||||
public class TestMqttCommunicationAdapter : IMqttChannelAdapter
|
public class TestMqttCommunicationAdapter : IMqttChannelAdapter
|
||||||
{
|
{
|
||||||
private readonly BlockingCollection<MqttBasePacket> _incomingPackets = new BlockingCollection<MqttBasePacket>();
|
readonly BlockingCollection<MqttBasePacket> _incomingPackets = new BlockingCollection<MqttBasePacket>();
|
||||||
|
|
||||||
public TestMqttCommunicationAdapter Partner { get; set; }
|
public TestMqttCommunicationAdapter Partner { get; set; }
|
||||||
|
|
||||||
@@ -26,8 +26,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
public long BytesSent { get; }
|
public long BytesSent { get; }
|
||||||
public long BytesReceived { get; }
|
public long BytesReceived { get; }
|
||||||
|
|
||||||
public Action ReadingPacketStartedCallback { get; set; }
|
public bool IsReadingPacket { get; }
|
||||||
public Action ReadingPacketCompletedCallback { get; set; }
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using MQTTnet.Server.Status;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public class MqttKeepAliveMonitor_Tests
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
public async Task KeepAlive_Timeout()
|
|
||||||
{
|
|
||||||
var counter = 0;
|
|
||||||
|
|
||||||
var monitor = new MqttClientKeepAliveMonitor("", () =>
|
|
||||||
{
|
|
||||||
counter++;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
},
|
|
||||||
new MqttNetLogger());
|
|
||||||
|
|
||||||
Assert.AreEqual(0, counter);
|
|
||||||
|
|
||||||
monitor.Start(1, CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.AreEqual(0, counter);
|
|
||||||
|
|
||||||
await Task.Delay(2000); // Internally the keep alive timeout is multiplied with 1.5 as per protocol specification.
|
|
||||||
|
|
||||||
Assert.AreEqual(1, counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task KeepAlive_NoTimeout()
|
|
||||||
{
|
|
||||||
var counter = 0;
|
|
||||||
|
|
||||||
var monitor = new MqttClientKeepAliveMonitor("", () =>
|
|
||||||
{
|
|
||||||
counter++;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
},
|
|
||||||
new MqttNetLogger());
|
|
||||||
|
|
||||||
Assert.AreEqual(0, counter);
|
|
||||||
|
|
||||||
monitor.Start(1, CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.AreEqual(0, counter);
|
|
||||||
|
|
||||||
// Simulate traffic.
|
|
||||||
await Task.Delay(1000); // Internally the keep alive timeout is multiplied with 1.5 as per protocol specification.
|
|
||||||
monitor.PacketReceived();
|
|
||||||
await Task.Delay(1000);
|
|
||||||
monitor.PacketReceived();
|
|
||||||
await Task.Delay(1000);
|
|
||||||
|
|
||||||
Assert.AreEqual(0, counter);
|
|
||||||
|
|
||||||
await Task.Delay(2000);
|
|
||||||
|
|
||||||
Assert.AreEqual(1, counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestClientSession : IMqttClientSession
|
|
||||||
{
|
|
||||||
public string ClientId { get; }
|
|
||||||
|
|
||||||
public int StopCalledCount { get; private set; }
|
|
||||||
|
|
||||||
public void FillStatus(MqttClientStatus status)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnqueueApplicationMessage(MqttClientConnection senderClientSession, MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearPendingApplicationMessages()
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RunAsync(MqttApplicationMessage willMessage, int keepAlivePeriod, IMqttChannelAdapter adapter)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync()
|
|
||||||
{
|
|
||||||
StopCalledCount++;
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SubscribeAsync(IList<MqttTopicFilter> topicFilters)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UnsubscribeAsync(IList<string> topicFilters)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Server_Connection_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Close_Idle_Connection_On_Connect()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||||
|
|
||||||
|
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
||||||
|
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
||||||
|
|
||||||
|
// Don't send anything. The server should close the connection.
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||||
|
if (receivedBytes == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("Receive should throw an exception.");
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Send_Garbage()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||||
|
|
||||||
|
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
|
||||||
|
// forever. This is security related.
|
||||||
|
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
||||||
|
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
||||||
|
|
||||||
|
var buffer = Encoding.UTF8.GetBytes("Garbage");
|
||||||
|
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||||
|
if (receivedBytes == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("Receive should throw an exception.");
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Server_KeepAlive_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Disconnect_Client_DueTo_KeepAlive()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectLowLevelClientAsync(o => o.WithCommunicationTimeout(TimeSpan.FromSeconds(2))).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await client.SendAsync(new MqttConnectPacket
|
||||||
|
{
|
||||||
|
CleanSession = true,
|
||||||
|
ClientId = "abc",
|
||||||
|
KeepAlivePeriod = 1,
|
||||||
|
}, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
Assert.IsTrue(response is MqttConnAckPacket);
|
||||||
|
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||||
|
await Task.Delay(500);
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||||
|
await Task.Delay(500);
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||||
|
await Task.Delay(500);
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||||
|
|
||||||
|
// If we reach this point everything works as expected (server did not close the connection
|
||||||
|
// due to proper ping messages.
|
||||||
|
// Now we will wait 1.2 seconds because the server MUST wait 1.5 seconds in total (See spec).
|
||||||
|
|
||||||
|
await Task.Delay(1200);
|
||||||
|
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||||
|
|
||||||
|
// Now we will wait longer than 1.5 so that the server will close the connection.
|
||||||
|
|
||||||
|
await Task.Delay(3000);
|
||||||
|
|
||||||
|
await server.StopAsync();
|
||||||
|
|
||||||
|
await client.ReceiveAsync(CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ using MQTTnet.Tests.Mockups;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
@@ -63,7 +62,6 @@ namespace MQTTnet.Tests
|
|||||||
var clientStatus = await server.GetClientStatusAsync();
|
var clientStatus = await server.GetClientStatusAsync();
|
||||||
|
|
||||||
Assert.AreEqual(1, clientStatus.Count);
|
Assert.AreEqual(1, clientStatus.Count);
|
||||||
|
|
||||||
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId));
|
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId));
|
||||||
|
|
||||||
await clientStatus.First().DisconnectAsync();
|
await clientStatus.First().DisconnectAsync();
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ using MQTTnet.Client.Disconnecting;
|
|||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Client.Receiving;
|
using MQTTnet.Client.Receiving;
|
||||||
using MQTTnet.Client.Subscribing;
|
using MQTTnet.Client.Subscribing;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -21,7 +19,7 @@ using System.Threading.Tasks;
|
|||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class Server_Tests
|
public sealed class Server_Tests
|
||||||
{
|
{
|
||||||
public TestContext TestContext { get; set; }
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
@@ -331,7 +329,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClientAsync();
|
var c2 = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||||
@@ -1219,68 +1217,6 @@ namespace MQTTnet.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Close_Idle_Connection()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
|
||||||
|
|
||||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
|
||||||
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
|
||||||
|
|
||||||
// Don't send anything. The server should close the connection.
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
|
||||||
if (receivedBytes == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Fail("Receive should throw an exception.");
|
|
||||||
}
|
|
||||||
catch (SocketException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Send_Garbage()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
|
||||||
|
|
||||||
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
|
|
||||||
// forever. This is security related.
|
|
||||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
|
||||||
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
|
||||||
|
|
||||||
var buffer = Encoding.UTF8.GetBytes("Garbage");
|
|
||||||
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
|
||||||
if (receivedBytes == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Fail("Receive should throw an exception.");
|
|
||||||
}
|
|
||||||
catch (SocketException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Do_Not_Send_Retained_Messages_For_Denied_Subscription()
|
public async Task Do_Not_Send_Retained_Messages_For_Denied_Subscription()
|
||||||
{
|
{
|
||||||
@@ -1471,7 +1407,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
await client.PublishAsync(new MqttApplicationMessage{ Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
await client.PublishAsync(new MqttApplicationMessage { Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user