@@ -20,12 +20,17 @@ namespace MQTTnet.AspNetCore | |||||
} | } | ||||
public string Endpoint => Connection.ConnectionId; | public string Endpoint => Connection.ConnectionId; | ||||
public bool IsSecureConnection => false; // TODO: Fix detection. | |||||
public bool IsSecureConnection => false; // TODO: Fix detection (WS vs. WSS). | |||||
public ConnectionContext Connection { get; } | public ConnectionContext Connection { get; } | ||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | ||||
public event EventHandler ReadingPacketStarted; | |||||
public event EventHandler ReadingPacketCompleted; | |||||
public long BytesSent { get; } // TODO: Fix calculation. | |||||
public long BytesReceived { get; } // TODO: Fix calculation. | |||||
public Action ReadingPacketStartedCallback { get; set; } | |||||
public Action ReadingPacketCompletedCallback { get; set; } | |||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1); | private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1); | ||||
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) | public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) | ||||
@@ -34,6 +39,7 @@ namespace MQTTnet.AspNetCore | |||||
{ | { | ||||
return tcp.StartAsync(); | return tcp.StartAsync(); | ||||
} | } | ||||
return Task.CompletedTask; | return Task.CompletedTask; | ||||
} | } | ||||
@@ -80,7 +86,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 | ||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty); | |||||
ReadingPacketStartedCallback?.Invoke(); | |||||
} | } | ||||
} | } | ||||
else if (readResult.IsCompleted) | else if (readResult.IsCompleted) | ||||
@@ -99,7 +105,7 @@ namespace MQTTnet.AspNetCore | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty); | |||||
ReadingPacketCompletedCallback?.Invoke(); | |||||
} | } | ||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
@@ -13,10 +13,14 @@ namespace MQTTnet.Adapter | |||||
bool IsSecureConnection { get; } | bool IsSecureConnection { get; } | ||||
MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | ||||
event EventHandler ReadingPacketStarted; | |||||
event EventHandler ReadingPacketCompleted; | |||||
long BytesSent { get; } | |||||
long BytesReceived { get; } | |||||
Action ReadingPacketStartedCallback { get; set; } | |||||
Action ReadingPacketCompletedCallback { get; set; } | |||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken); | Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken); | ||||
@@ -25,16 +25,19 @@ namespace MQTTnet.Adapter | |||||
private readonly MqttPacketReader _packetReader; | private readonly MqttPacketReader _packetReader; | ||||
private readonly byte[] _fixedHeaderBuffer = new byte[2]; | private readonly byte[] _fixedHeaderBuffer = new byte[2]; | ||||
private bool _isDisposed; | private bool _isDisposed; | ||||
private long _bytesReceived; | |||||
private long _bytesSent; | |||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger) | public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger) | ||||
{ | { | ||||
if (logger == null) throw new ArgumentNullException(nameof(logger)); | if (logger == null) throw new ArgumentNullException(nameof(logger)); | ||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel)); | _channel = channel ?? throw new ArgumentNullException(nameof(channel)); | ||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); | PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); | ||||
_packetReader = new MqttPacketReader(_channel); | _packetReader = new MqttPacketReader(_channel); | ||||
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter)); | _logger = logger.CreateChildLogger(nameof(MqttChannelAdapter)); | ||||
@@ -46,9 +49,12 @@ namespace MQTTnet.Adapter | |||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } | ||||
public event EventHandler ReadingPacketStarted; | |||||
public event EventHandler ReadingPacketCompleted; | |||||
public long BytesSent => Interlocked.Read(ref _bytesSent); | |||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived); | |||||
public Action ReadingPacketStartedCallback { get; set; } | |||||
public Action ReadingPacketCompletedCallback { get; set; } | |||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) | public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) | ||||
{ | { | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
@@ -62,7 +68,7 @@ namespace MQTTnet.Adapter | |||||
else | else | ||||
{ | { | ||||
await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false); | await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false); | ||||
} | |||||
} | |||||
} | } | ||||
catch (Exception exception) | catch (Exception exception) | ||||
{ | { | ||||
@@ -101,7 +107,7 @@ namespace MQTTnet.Adapter | |||||
WrapException(exception); | WrapException(exception); | ||||
} | } | ||||
} | } | ||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken) | public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken) | ||||
{ | { | ||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); | await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); | ||||
@@ -118,7 +124,9 @@ namespace MQTTnet.Adapter | |||||
await MqttTaskTimeout.WaitAsync( | await MqttTaskTimeout.WaitAsync( | ||||
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false); | t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false); | ||||
} | } | ||||
Interlocked.Add(ref _bytesReceived, packetData.Count); | |||||
PacketFormatterAdapter.FreeBuffer(); | PacketFormatterAdapter.FreeBuffer(); | ||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet); | _logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet); | ||||
@@ -159,7 +167,9 @@ namespace MQTTnet.Adapter | |||||
return null; | return null; | ||||
} | } | ||||
if (!PacketFormatterAdapter.ProtocolVersion.HasValue) | |||||
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength); | |||||
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown) | |||||
{ | { | ||||
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket); | PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket); | ||||
} | } | ||||
@@ -190,10 +200,21 @@ namespace MQTTnet.Adapter | |||||
return null; | return null; | ||||
} | } | ||||
public void ResetStatistics() | |||||
{ | |||||
Interlocked.Exchange(ref _bytesReceived, 0L); | |||||
Interlocked.Exchange(ref _bytesSent, 0L); | |||||
} | |||||
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken) | private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken) | ||||
{ | { | ||||
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false); | var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false); | ||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return null; | |||||
} | |||||
try | try | ||||
{ | { | ||||
if (readFixedHeaderResult.ConnectionClosed) | if (readFixedHeaderResult.ConnectionClosed) | ||||
@@ -201,7 +222,7 @@ namespace MQTTnet.Adapter | |||||
return null; | return null; | ||||
} | } | ||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty); | |||||
ReadingPacketStartedCallback?.Invoke(); | |||||
var fixedHeader = readFixedHeaderResult.FixedHeader; | var fixedHeader = readFixedHeaderResult.FixedHeader; | ||||
if (fixedHeader.RemainingLength == 0) | if (fixedHeader.RemainingLength == 0) | ||||
@@ -221,13 +242,7 @@ namespace MQTTnet.Adapter | |||||
chunkSize = bytesLeft; | chunkSize = bytesLeft; | ||||
} | } | ||||
#if WINDOWS_UWP | |||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false); | |||||
#else | |||||
// async/await is not used to avoid the overhead of context switches. We assume that the remaining data | |||||
// has been sent from the sender directly after the initial bytes. | |||||
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); | |||||
#endif | |||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false); | |||||
if (cancellationToken.IsCancellationRequested) | if (cancellationToken.IsCancellationRequested) | ||||
{ | { | ||||
@@ -238,7 +253,7 @@ namespace MQTTnet.Adapter | |||||
{ | { | ||||
return null; | return null; | ||||
} | } | ||||
bodyOffset += readBytes; | bodyOffset += readBytes; | ||||
} while (bodyOffset < body.Length); | } while (bodyOffset < body.Length); | ||||
@@ -247,7 +262,7 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty); | |||||
ReadingPacketCompletedCallback?.Invoke(); | |||||
} | } | ||||
} | } | ||||
@@ -20,7 +20,7 @@ namespace MQTTnet.Formatter | |||||
UseProtocolVersion(protocolVersion); | UseProtocolVersion(protocolVersion); | ||||
} | } | ||||
public MqttProtocolVersion? ProtocolVersion { get; private set; } | |||||
public MqttProtocolVersion ProtocolVersion { get; private set; } | |||||
public IMqttDataConverter DataConverter | public IMqttDataConverter DataConverter | ||||
{ | { | ||||
@@ -64,6 +64,11 @@ namespace MQTTnet.Formatter | |||||
private void UseProtocolVersion(MqttProtocolVersion protocolVersion) | private void UseProtocolVersion(MqttProtocolVersion protocolVersion) | ||||
{ | { | ||||
if (protocolVersion == MqttProtocolVersion.Unknown) | |||||
{ | |||||
throw new InvalidOperationException("MQTT protocol version is invalid."); | |||||
} | |||||
ProtocolVersion = protocolVersion; | ProtocolVersion = protocolVersion; | ||||
switch (protocolVersion) | switch (protocolVersion) | ||||
@@ -3,7 +3,6 @@ using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Channel; | using MQTTnet.Channel; | ||||
using MQTTnet.Exceptions; | using MQTTnet.Exceptions; | ||||
using MQTTnet.Internal; | |||||
namespace MQTTnet.Formatter | namespace MQTTnet.Formatter | ||||
{ | { | ||||
@@ -31,8 +30,11 @@ namespace MQTTnet.Formatter | |||||
{ | { | ||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false); | var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false); | ||||
cancellationToken.ThrowIfCancellationRequested(); | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return null; | |||||
} | |||||
if (bytesRead == 0) | if (bytesRead == 0) | ||||
{ | { | ||||
return new ReadFixedHeaderResult | return new ReadFixedHeaderResult | ||||
@@ -100,13 +102,19 @@ namespace MQTTnet.Formatter | |||||
return null; | return null; | ||||
} | } | ||||
var buffer = ReadByte(cancellationToken); | |||||
if (!buffer.HasValue) | |||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).GetAwaiter().GetResult(); | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | { | ||||
return null; | return null; | ||||
} | } | ||||
encodedByte = buffer.Value; | |||||
if (readCount == 0) | |||||
{ | |||||
return null; | |||||
} | |||||
encodedByte = _singleByteBuffer[0]; | |||||
value += (encodedByte & 127) * multiplier; | value += (encodedByte & 127) * multiplier; | ||||
multiplier *= 128; | multiplier *= 128; | ||||
@@ -114,26 +122,8 @@ namespace MQTTnet.Formatter | |||||
return value; | return value; | ||||
} | } | ||||
private byte? ReadByte(CancellationToken cancellationToken) | |||||
{ | |||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return null; | |||||
} | |||||
if (readCount == 0) | |||||
{ | |||||
return null; | |||||
} | |||||
return _singleByteBuffer[0]; | |||||
} | |||||
#else | #else | ||||
private async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken) | private async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken) | ||||
{ | { | ||||
var offset = 0; | var offset = 0; | ||||
@@ -154,13 +144,19 @@ namespace MQTTnet.Formatter | |||||
return null; | return null; | ||||
} | } | ||||
var buffer = await ReadByteAsync(cancellationToken).ConfigureAwait(false); | |||||
if (!buffer.HasValue) | |||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false); | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return null; | |||||
} | |||||
if (readCount == 0) | |||||
{ | { | ||||
return null; | return null; | ||||
} | } | ||||
encodedByte = buffer.Value; | |||||
encodedByte = _singleByteBuffer[0]; | |||||
value += (encodedByte & 127) * multiplier; | value += (encodedByte & 127) * multiplier; | ||||
multiplier *= 128; | multiplier *= 128; | ||||
@@ -168,24 +164,6 @@ namespace MQTTnet.Formatter | |||||
return value; | return value; | ||||
} | } | ||||
private async Task<byte?> ReadByteAsync(CancellationToken cancellationToken) | |||||
{ | |||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false); | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return null; | |||||
} | |||||
if (readCount == 0) | |||||
{ | |||||
return null; | |||||
} | |||||
return _singleByteBuffer[0]; | |||||
} | |||||
#endif | #endif | ||||
} | } | ||||
} | } |
@@ -2,6 +2,8 @@ | |||||
{ | { | ||||
public enum MqttProtocolVersion | public enum MqttProtocolVersion | ||||
{ | { | ||||
Unknown = 0, | |||||
V310 = 3, | V310 = 3, | ||||
V311 = 4, | V311 = 4, | ||||
V500 = 5 | V500 = 5 | ||||
@@ -8,7 +8,10 @@ namespace MQTTnet.Internal | |||||
public sealed class AsyncQueue<TItem> : IDisposable | public sealed class AsyncQueue<TItem> : IDisposable | ||||
{ | { | ||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0); | private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0); | ||||
private readonly ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>(); | |||||
private ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>(); | |||||
public int Count => _queue.Count; | |||||
public void Enqueue(TItem item) | public void Enqueue(TItem item) | ||||
{ | { | ||||
@@ -16,9 +19,9 @@ namespace MQTTnet.Internal | |||||
_semaphore.Release(); | _semaphore.Release(); | ||||
} | } | ||||
public async Task<TItem> DequeueAsync(CancellationToken cancellationToken) | |||||
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken) | |||||
{ | { | ||||
while (true) | |||||
while (!cancellationToken.IsCancellationRequested) | |||||
{ | { | ||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); | await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); | ||||
@@ -26,9 +29,26 @@ namespace MQTTnet.Internal | |||||
if (_queue.TryDequeue(out var item)) | if (_queue.TryDequeue(out var item)) | ||||
{ | { | ||||
return item; | |||||
return new AsyncQueueDequeueResult<TItem>(true, item); | |||||
} | } | ||||
} | } | ||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem)); | |||||
} | |||||
public AsyncQueueDequeueResult<TItem> TryDequeue() | |||||
{ | |||||
if (_queue.TryDequeue(out var item)) | |||||
{ | |||||
return new AsyncQueueDequeueResult<TItem>(true, item); | |||||
} | |||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem)); | |||||
} | |||||
public void Clear() | |||||
{ | |||||
Interlocked.Exchange(ref _queue, new ConcurrentQueue<TItem>()); | |||||
} | } | ||||
public void Dispose() | public void Dispose() | ||||
@@ -0,0 +1,15 @@ | |||||
namespace MQTTnet.Internal | |||||
{ | |||||
public class AsyncQueueDequeueResult<TItem> | |||||
{ | |||||
public AsyncQueueDequeueResult(bool isSuccess, TItem item) | |||||
{ | |||||
IsSuccess = isSuccess; | |||||
Item = item; | |||||
} | |||||
public bool IsSuccess { get; } | |||||
public TItem Item { get; } | |||||
} | |||||
} |
@@ -34,8 +34,10 @@ namespace MQTTnet.Server | |||||
private readonly MqttConnectPacket _connectPacket; | private readonly MqttConnectPacket _connectPacket; | ||||
private DateTime _lastPacketReceivedTimestamp; | private DateTime _lastPacketReceivedTimestamp; | ||||
private DateTime _lastNonKeepAlivePacketReceivedTimestamp; | |||||
private long _receivedPacketsCount; | private long _receivedPacketsCount; | ||||
private long _sentPacketsCount; | |||||
private long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere. | |||||
private long _receivedApplicationMessagesCount; | private long _receivedApplicationMessagesCount; | ||||
private long _sentApplicationMessagesCount; | private long _sentApplicationMessagesCount; | ||||
@@ -64,6 +66,7 @@ namespace MQTTnet.Server | |||||
_keepAliveMonitor = new MqttClientKeepAliveMonitor(this, _logger); | _keepAliveMonitor = new MqttClientKeepAliveMonitor(this, _logger); | ||||
_lastPacketReceivedTimestamp = DateTime.UtcNow; | _lastPacketReceivedTimestamp = DateTime.UtcNow; | ||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp; | |||||
} | } | ||||
public string ClientId => _connectPacket.ClientId; | public string ClientId => _connectPacket.ClientId; | ||||
@@ -85,7 +88,7 @@ namespace MQTTnet.Server | |||||
{ | { | ||||
status.ClientId = ClientId; | status.ClientId = ClientId; | ||||
status.Endpoint = _endpoint; | status.Endpoint = _endpoint; | ||||
status.ProtocolVersion = _channelAdapter.PacketFormatterAdapter.ProtocolVersion.Value; | |||||
status.ProtocolVersion = _channelAdapter.PacketFormatterAdapter.ProtocolVersion; | |||||
status.ReceivedApplicationMessagesCount = Interlocked.Read(ref _receivedApplicationMessagesCount); | status.ReceivedApplicationMessagesCount = Interlocked.Read(ref _receivedApplicationMessagesCount); | ||||
status.SentApplicationMessagesCount = Interlocked.Read(ref _sentApplicationMessagesCount); | status.SentApplicationMessagesCount = Interlocked.Read(ref _sentApplicationMessagesCount); | ||||
@@ -94,8 +97,10 @@ namespace MQTTnet.Server | |||||
status.SentPacketsCount = Interlocked.Read(ref _sentPacketsCount); | status.SentPacketsCount = Interlocked.Read(ref _sentPacketsCount); | ||||
status.LastPacketReceivedTimestamp = _lastPacketReceivedTimestamp; | status.LastPacketReceivedTimestamp = _lastPacketReceivedTimestamp; | ||||
status.LastNonKeepAlivePacketReceivedTimestamp = _lastNonKeepAlivePacketReceivedTimestamp; | |||||
//status.LastNonKeepAlivePacketReceived = _keepAliveMonitor.LastNonKeepAlivePacketReceived; | |||||
status.BytesSent = _channelAdapter.BytesSent; | |||||
status.BytesReceived = _channelAdapter.BytesReceived; | |||||
} | } | ||||
//public void ClearPendingApplicationMessages() | //public void ClearPendingApplicationMessages() | ||||
@@ -131,10 +136,9 @@ namespace MQTTnet.Server | |||||
try | try | ||||
{ | { | ||||
_logger.Info("Client '{0}': Session started.", ClientId); | _logger.Info("Client '{0}': Session started.", ClientId); | ||||
//_eventDispatcher.OnClientConnected(ClientId); | |||||
_channelAdapter.ReadingPacketStarted += OnAdapterReadingPacketStarted; | |||||
_channelAdapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted; | |||||
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted; | |||||
_channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted; | |||||
Session.WillMessage = _connectPacket.WillMessage; | Session.WillMessage = _connectPacket.WillMessage; | ||||
@@ -166,7 +170,12 @@ namespace MQTTnet.Server | |||||
Interlocked.Increment(ref _sentPacketsCount); | Interlocked.Increment(ref _sentPacketsCount); | ||||
_lastPacketReceivedTimestamp = DateTime.UtcNow; | _lastPacketReceivedTimestamp = DateTime.UtcNow; | ||||
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket)) | |||||
{ | |||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp; | |||||
} | |||||
_keepAliveMonitor.PacketReceived(); | _keepAliveMonitor.PacketReceived(); | ||||
if (packet is MqttPublishPacket publishPacket) | if (packet is MqttPublishPacket publishPacket) | ||||
@@ -243,12 +252,11 @@ namespace MQTTnet.Server | |||||
_packetDispatcher.Reset(); | _packetDispatcher.Reset(); | ||||
_channelAdapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted; | |||||
_channelAdapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted; | |||||
_channelAdapter.ReadingPacketStartedCallback = null; | |||||
_channelAdapter.ReadingPacketCompletedCallback = null; | |||||
_logger.Info("Client '{0}': Session stopped.", ClientId); | _logger.Info("Client '{0}': Session stopped.", ClientId); | ||||
//_eventDispatcher.OnClientDisconnected(ClientId); | |||||
_packageReceiverTask = null; | _packageReceiverTask = null; | ||||
} | } | ||||
@@ -376,7 +384,7 @@ namespace MQTTnet.Server | |||||
private async Task SendPendingPacketsAsync(CancellationToken cancellationToken) | private async Task SendPendingPacketsAsync(CancellationToken cancellationToken) | ||||
{ | { | ||||
MqttPendingApplicationMessage queuedApplicationMessage = null; | |||||
MqttQueuedApplicationMessage queuedApplicationMessage = null; | |||||
MqttPublishPacket publishPacket = null; | MqttPublishPacket publishPacket = null; | ||||
try | try | ||||
@@ -501,12 +509,12 @@ namespace MQTTnet.Server | |||||
} | } | ||||
} | } | ||||
private void OnAdapterReadingPacketCompleted(object sender, EventArgs e) | |||||
private void OnAdapterReadingPacketCompleted() | |||||
{ | { | ||||
_keepAliveMonitor?.Resume(); | _keepAliveMonitor?.Resume(); | ||||
} | } | ||||
private void OnAdapterReadingPacketStarted(object sender, EventArgs e) | |||||
private void OnAdapterReadingPacketStarted() | |||||
{ | { | ||||
_keepAliveMonitor?.Pause(); | _keepAliveMonitor?.Pause(); | ||||
} | } | ||||
@@ -3,7 +3,6 @@ using System.Diagnostics; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Diagnostics; | using MQTTnet.Diagnostics; | ||||
using MQTTnet.Packets; | |||||
namespace MQTTnet.Server | namespace MQTTnet.Server | ||||
{ | { | ||||
@@ -1,54 +1,29 @@ | |||||
using MQTTnet.Internal; | using MQTTnet.Internal; | ||||
using MQTTnet.Protocol; | using MQTTnet.Protocol; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace MQTTnet.Server | namespace MQTTnet.Server | ||||
{ | { | ||||
public class MqttPendingApplicationMessage | |||||
{ | |||||
public MqttApplicationMessage ApplicationMessage { get; set; } | |||||
public string SenderClientId { get; set; } | |||||
public bool IsRetainedMessage { get; set; } | |||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } | |||||
public bool IsDuplicate { get; set; } | |||||
} | |||||
public class MqttClientSessionApplicationMessagesQueue : IDisposable | public class MqttClientSessionApplicationMessagesQueue : IDisposable | ||||
{ | { | ||||
private readonly Queue<MqttPendingApplicationMessage> _messageQueue = new Queue<MqttPendingApplicationMessage>(); | |||||
private readonly AsyncAutoResetEvent _messageQueueLock = new AsyncAutoResetEvent(); | |||||
private readonly AsyncQueue<MqttQueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttQueuedApplicationMessage>(); | |||||
private readonly IMqttServerOptions _options; | private readonly IMqttServerOptions _options; | ||||
public MqttClientSessionApplicationMessagesQueue(IMqttServerOptions options) | public MqttClientSessionApplicationMessagesQueue(IMqttServerOptions options) | ||||
{ | { | ||||
_options = options ?? throw new ArgumentNullException(nameof(options)); | _options = options ?? throw new ArgumentNullException(nameof(options)); | ||||
} | } | ||||
public int Count | |||||
{ | |||||
get | |||||
{ | |||||
lock (_messageQueue) | |||||
{ | |||||
return _messageQueue.Count; | |||||
} | |||||
} | |||||
} | |||||
public int Count => _messageQueue.Count; | |||||
public void Enqueue(MqttApplicationMessage applicationMessage, string senderClientId, MqttQualityOfServiceLevel qualityOfServiceLevel, bool isRetainedMessage) | public void Enqueue(MqttApplicationMessage applicationMessage, string senderClientId, MqttQualityOfServiceLevel qualityOfServiceLevel, bool isRetainedMessage) | ||||
{ | { | ||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); | if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); | ||||
Enqueue(new MqttPendingApplicationMessage | |||||
Enqueue(new MqttQueuedApplicationMessage | |||||
{ | { | ||||
ApplicationMessage = applicationMessage, | ApplicationMessage = applicationMessage, | ||||
SenderClientId = senderClientId, | SenderClientId = senderClientId, | ||||
@@ -59,39 +34,23 @@ namespace MQTTnet.Server | |||||
public void Clear() | public void Clear() | ||||
{ | { | ||||
lock (_messageQueue) | |||||
{ | |||||
_messageQueue.Clear(); | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_messageQueue.Clear(); | |||||
} | } | ||||
public async Task<MqttPendingApplicationMessage> TakeAsync(CancellationToken cancellationToken) | |||||
public async Task<MqttQueuedApplicationMessage> TakeAsync(CancellationToken cancellationToken) | |||||
{ | { | ||||
// TODO: Create a blocking queue from this. | |||||
while (!cancellationToken.IsCancellationRequested) | |||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false); | |||||
if (!dequeueResult.IsSuccess) | |||||
{ | { | ||||
lock (_messageQueue) | |||||
{ | |||||
if (_messageQueue.Count > 0) | |||||
{ | |||||
return _messageQueue.Dequeue(); | |||||
} | |||||
} | |||||
await _messageQueueLock.WaitOneAsync(cancellationToken).ConfigureAwait(false); | |||||
return null; | |||||
} | } | ||||
return null; | |||||
return dequeueResult.Item; | |||||
} | } | ||||
public void Enqueue(MqttPendingApplicationMessage enqueuedApplicationMessage) | |||||
public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage) | |||||
{ | { | ||||
if (enqueuedApplicationMessage == null) throw new ArgumentNullException(nameof(enqueuedApplicationMessage)); | |||||
if (queuedApplicationMessage == null) throw new ArgumentNullException(nameof(queuedApplicationMessage)); | |||||
lock (_messageQueue) | lock (_messageQueue) | ||||
{ | { | ||||
@@ -104,14 +63,17 @@ namespace MQTTnet.Server | |||||
if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage) | if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage) | ||||
{ | { | ||||
_messageQueue.Dequeue(); | |||||
_messageQueue.TryDequeue(); | |||||
} | } | ||||
} | } | ||||
_messageQueue.Enqueue(enqueuedApplicationMessage); | |||||
_messageQueue.Enqueue(queuedApplicationMessage); | |||||
} | } | ||||
} | |||||
_messageQueueLock.Set(); | |||||
public void Dispose() | |||||
{ | |||||
_messageQueue.Dispose(); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -68,7 +68,7 @@ namespace MQTTnet.Server | |||||
foreach (var connection in _connections.Values) | foreach (var connection in _connections.Values) | ||||
{ | { | ||||
var clientStatus = new MqttClientStatus(connection, this); | |||||
var clientStatus = new MqttClientStatus(connection); | |||||
connection.FillStatus(clientStatus); | connection.FillStatus(clientStatus); | ||||
var sessionStatus = new MqttSessionStatus(connection.Session, this); | var sessionStatus = new MqttSessionStatus(connection.Session, this); | ||||
@@ -175,7 +175,8 @@ namespace MQTTnet.Server | |||||
return; | return; | ||||
} | } | ||||
var queuedApplicationMessage = await _messageQueue.DequeueAsync(cancellationToken).ConfigureAwait(false); | |||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false); | |||||
var queuedApplicationMessage = dequeueResult.Item; | |||||
var sender = queuedApplicationMessage.Sender; | var sender = queuedApplicationMessage.Sender; | ||||
var applicationMessage = queuedApplicationMessage.ApplicationMessage; | var applicationMessage = queuedApplicationMessage.ApplicationMessage; | ||||
@@ -0,0 +1,17 @@ | |||||
using MQTTnet.Protocol; | |||||
namespace MQTTnet.Server | |||||
{ | |||||
public class MqttQueuedApplicationMessage | |||||
{ | |||||
public MqttApplicationMessage ApplicationMessage { get; set; } | |||||
public string SenderClientId { get; set; } | |||||
public bool IsRetainedMessage { get; set; } | |||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } | |||||
public bool IsDuplicate { get; set; } | |||||
} | |||||
} |
@@ -26,6 +26,10 @@ namespace MQTTnet.Server.Status | |||||
IMqttSessionStatus Session { get; } | IMqttSessionStatus Session { get; } | ||||
long BytesSent { get; } | |||||
long BytesReceived { get; } | |||||
Task DisconnectAsync(); | Task DisconnectAsync(); | ||||
} | } | ||||
} | } |
@@ -6,13 +6,11 @@ namespace MQTTnet.Server.Status | |||||
{ | { | ||||
public class MqttClientStatus : IMqttClientStatus | public class MqttClientStatus : IMqttClientStatus | ||||
{ | { | ||||
private readonly MqttClientSessionsManager _sessionsManager; | |||||
private readonly MqttClientConnection _connection; | private readonly MqttClientConnection _connection; | ||||
public MqttClientStatus(MqttClientConnection connection, MqttClientSessionsManager sessionsManager) | |||||
public MqttClientStatus(MqttClientConnection connection) | |||||
{ | { | ||||
_connection = connection; | _connection = connection; | ||||
_sessionsManager = sessionsManager; | |||||
} | } | ||||
public string ClientId { get; set; } | public string ClientId { get; set; } | ||||
@@ -35,6 +33,10 @@ namespace MQTTnet.Server.Status | |||||
public IMqttSessionStatus Session { get; set; } | public IMqttSessionStatus Session { get; set; } | ||||
public long BytesSent { get; set; } | |||||
public long BytesReceived { get; set; } | |||||
public Task DisconnectAsync() | public Task DisconnectAsync() | ||||
{ | { | ||||
return _connection.StopAsync(); | return _connection.StopAsync(); | ||||