@@ -10,9 +10,11 @@ | |||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> | <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> | ||||
<requireLicenseAcceptance>false</requireLicenseAcceptance> | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description> | <description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description> | ||||
<releaseNotes>* [Core] Fixed library reference issues for .NET 4.6 (Thanks to @JanEggers). | |||||
<releaseNotes>* [Core] Fixed library reference issues for .NET 4.6 and netstandard 2.0 (Thanks to @JanEggers). | |||||
* [Core] Several COM exceptions are now wrapped properly resulting in less warnings in the trace. | * [Core] Several COM exceptions are now wrapped properly resulting in less warnings in the trace. | ||||
* [Core] Removed application message payload from trace to reduce trace size and increase performance. | |||||
* [Client] Fixed WebSocket sub protocol negotiation for ASP.NET Core 2 servers (Thanks to @JanEggers). | * [Client] Fixed WebSocket sub protocol negotiation for ASP.NET Core 2 servers (Thanks to @JanEggers). | ||||
* [Client] Fixed broken connection after 30 seconds then using WebSocket protocol (Thanks to @ChristianRiedl). | |||||
* [Server] Client connections are now closed when the server is stopped (Thanks to @zhudanfei). | * [Server] Client connections are now closed when the server is stopped (Thanks to @zhudanfei). | ||||
* [Server] Published messages from the server are now retained (if set) (Thanks to @ChristianRiedl). BREAKING CHANGE! | * [Server] Published messages from the server are now retained (if set) (Thanks to @ChristianRiedl). BREAKING CHANGE! | ||||
</releaseNotes> | </releaseNotes> | ||||
@@ -1,12 +1,8 @@ | |||||
using System; | using System; | ||||
using System.IO; | |||||
using System.Net.WebSockets; | using System.Net.WebSockets; | ||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Core.Adapter; | using MQTTnet.Core.Adapter; | ||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Server; | using MQTTnet.Core.Server; | ||||
using MQTTnet.Implementations; | |||||
namespace MQTTnet.AspNetCore | namespace MQTTnet.AspNetCore | ||||
{ | { | ||||
@@ -47,47 +43,5 @@ namespace MQTTnet.AspNetCore | |||||
{ | { | ||||
StopAsync(); | StopAsync(); | ||||
} | } | ||||
private class MqttWebSocketServerChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | |||||
private readonly WebSocket _webSocket; | |||||
public MqttWebSocketServerChannel(WebSocket webSocket) | |||||
{ | |||||
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket)); | |||||
RawReceiveStream = new WebSocketStream(_webSocket); | |||||
} | |||||
public Stream SendStream => RawReceiveStream; | |||||
public Stream ReceiveStream => RawReceiveStream; | |||||
public Stream RawReceiveStream { get; } | |||||
public Task ConnectAsync() | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
public Task DisconnectAsync() | |||||
{ | |||||
RawReceiveStream?.Dispose(); | |||||
if (_webSocket == null) | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
return _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
RawReceiveStream?.Dispose(); | |||||
SendStream?.Dispose(); | |||||
ReceiveStream?.Dispose(); | |||||
_webSocket?.Dispose(); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,60 @@ | |||||
using System; | |||||
using System.IO; | |||||
using System.Net.WebSockets; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Implementations; | |||||
namespace MQTTnet.AspNetCore | |||||
{ | |||||
public class MqttWebSocketServerChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | |||||
private WebSocket _webSocket; | |||||
public MqttWebSocketServerChannel(WebSocket webSocket) | |||||
{ | |||||
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket)); | |||||
SendStream = new WebSocketStream(_webSocket); | |||||
ReceiveStream = SendStream; | |||||
} | |||||
public Stream SendStream { get; private set; } | |||||
public Stream ReceiveStream { get; private set; } | |||||
public Task ConnectAsync() | |||||
{ | |||||
return Task.CompletedTask; | |||||
} | |||||
public async Task DisconnectAsync() | |||||
{ | |||||
if (_webSocket == null) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
finally | |||||
{ | |||||
Dispose(); | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
SendStream?.Dispose(); | |||||
ReceiveStream?.Dispose(); | |||||
_webSocket?.Dispose(); | |||||
SendStream = null; | |||||
ReceiveStream = null; | |||||
_webSocket = null; | |||||
} | |||||
} | |||||
} |
@@ -11,6 +11,10 @@ namespace MQTTnet.Implementations | |||||
{ | { | ||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable | public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable | ||||
{ | { | ||||
// ReSharper disable once MemberCanBePrivate.Global | |||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global | |||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user. | |||||
private readonly MqttClientWebSocketOptions _options; | private readonly MqttClientWebSocketOptions _options; | ||||
private ClientWebSocket _webSocket; | private ClientWebSocket _webSocket; | ||||
@@ -80,7 +84,7 @@ namespace MQTTnet.Implementations | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false); | await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false); | ||||
} | } | ||||
_webSocket = null; | |||||
Dispose(); | |||||
} | } | ||||
public void Dispose() | public void Dispose() | ||||
@@ -1,5 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | |||||
using System.Net.WebSockets; | using System.Net.WebSockets; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -10,7 +12,9 @@ namespace MQTTnet.Implementations | |||||
public class WebSocketStream : Stream | public class WebSocketStream : Stream | ||||
{ | { | ||||
private readonly WebSocket _webSocket; | private readonly WebSocket _webSocket; | ||||
private readonly byte[] _chunkBuffer = new byte[MqttWebSocketChannel.BufferSize]; | |||||
private readonly Queue<byte> _buffer = new Queue<byte>(MqttWebSocketChannel.BufferSize); | |||||
public WebSocketStream(WebSocket webSocket) | public WebSocketStream(WebSocket webSocket) | ||||
{ | { | ||||
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket)); | _webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket)); | ||||
@@ -34,43 +38,66 @@ namespace MQTTnet.Implementations | |||||
{ | { | ||||
} | } | ||||
public override Task FlushAsync(CancellationToken cancellationToken) | |||||
{ | |||||
return Task.FromResult(0); | |||||
} | |||||
public override int Read(byte[] buffer, int offset, int count) | |||||
{ | |||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); | |||||
} | |||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | ||||
{ | { | ||||
var currentOffset = offset; | |||||
var targetOffset = offset + count; | |||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset) | |||||
var bytesRead = 0; | |||||
// Use existing date from buffer. | |||||
while (count > 0 && _buffer.Any()) | |||||
{ | { | ||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false); | |||||
currentOffset += response.Count; | |||||
count -= response.Count; | |||||
if (response.MessageType == WebSocketMessageType.Close) | |||||
buffer[offset++] = _buffer.Dequeue(); | |||||
count--; | |||||
bytesRead++; | |||||
} | |||||
if (count == 0) | |||||
{ | |||||
return bytesRead; | |||||
} | |||||
while (_webSocket.State == WebSocketState.Open) | |||||
{ | |||||
await FetchChunkAsync(cancellationToken); | |||||
while (count > 0 && _buffer.Any()) | |||||
{ | { | ||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false); | |||||
buffer[offset++] = _buffer.Dequeue(); | |||||
count--; | |||||
bytesRead++; | |||||
} | |||||
if (count == 0) | |||||
{ | |||||
return bytesRead; | |||||
} | } | ||||
} | } | ||||
if (_webSocket.State == WebSocketState.Closed) | if (_webSocket.State == WebSocketState.Closed) | ||||
{ | { | ||||
throw new MqttCommunicationException( "connection closed" ); | |||||
throw new MqttCommunicationException("WebSocket connection closed."); | |||||
} | } | ||||
return currentOffset - offset; | |||||
return bytesRead; | |||||
} | } | ||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | |||||
{ | |||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken); | |||||
} | |||||
public override int Read(byte[] buffer, int offset, int count) | |||||
public override void Write(byte[] buffer, int offset, int count) | |||||
{ | { | ||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); | |||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult(); | |||||
} | } | ||||
public override void Write(byte[] buffer, int offset, int count) | |||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | |||||
{ | { | ||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult(); | |||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken); | |||||
} | } | ||||
public override long Seek(long offset, SeekOrigin origin) | public override long Seek(long offset, SeekOrigin origin) | ||||
@@ -82,5 +109,21 @@ namespace MQTTnet.Implementations | |||||
{ | { | ||||
throw new NotSupportedException(); | throw new NotSupportedException(); | ||||
} | } | ||||
private async Task FetchChunkAsync(CancellationToken cancellationToken) | |||||
{ | |||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(_chunkBuffer, 0, _chunkBuffer.Length), cancellationToken).ConfigureAwait(false); | |||||
for (var i = 0; i < response.Count; i++) | |||||
{ | |||||
var @byte = _chunkBuffer[i]; | |||||
_buffer.Enqueue(@byte); | |||||
} | |||||
if (response.MessageType == WebSocketMessageType.Close) | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -23,7 +23,7 @@ namespace MQTTnet | |||||
public MqttFactory(IServiceProvider serviceProvider) | public MqttFactory(IServiceProvider serviceProvider) | ||||
{ | { | ||||
_serviceProvider = serviceProvider; | |||||
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); | |||||
} | } | ||||
public ILoggerFactory GetLoggerFactory() | public ILoggerFactory GetLoggerFactory() | ||||
@@ -60,14 +60,14 @@ namespace MQTTnet.Core.Client | |||||
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); | await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); | ||||
_logger.LogTrace("Connection with server established."); | _logger.LogTrace("Connection with server established."); | ||||
await SetupIncomingPacketProcessingAsync().ConfigureAwait(false); | |||||
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false); | |||||
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false); | var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false); | ||||
_logger.LogTrace("MQTT connection with server established."); | _logger.LogTrace("MQTT connection with server established."); | ||||
if (_options.KeepAlivePeriod != TimeSpan.Zero) | if (_options.KeepAlivePeriod != TimeSpan.Zero) | ||||
{ | { | ||||
StartSendKeepAliveMessages(_cancellationTokenSource.Token); | |||||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token); | |||||
} | } | ||||
IsConnected = true; | IsConnected = true; | ||||
@@ -96,7 +96,6 @@ namespace MQTTnet.Core.Client | |||||
finally | finally | ||||
{ | { | ||||
await DisconnectInternalAsync().ConfigureAwait(false); | await DisconnectInternalAsync().ConfigureAwait(false); | ||||
_scopeHandle.Dispose(); | |||||
} | } | ||||
} | } | ||||
@@ -149,36 +148,36 @@ namespace MQTTnet.Core.Client | |||||
switch (qosGroup.Key) | switch (qosGroup.Key) | ||||
{ | { | ||||
case MqttQualityOfServiceLevel.AtMostOnce: | case MqttQualityOfServiceLevel.AtMostOnce: | ||||
{ | |||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] | |||||
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false); | |||||
break; | |||||
} | |||||
case MqttQualityOfServiceLevel.AtLeastOnce: | |||||
{ | |||||
foreach (var publishPacket in qosPackets) | |||||
{ | { | ||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); | |||||
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false); | |||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] | |||||
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false); | |||||
break; | |||||
} | } | ||||
case MqttQualityOfServiceLevel.AtLeastOnce: | |||||
{ | |||||
foreach (var publishPacket in qosPackets) | |||||
{ | |||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); | |||||
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false); | |||||
} | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
case MqttQualityOfServiceLevel.ExactlyOnce: | case MqttQualityOfServiceLevel.ExactlyOnce: | ||||
{ | |||||
foreach (var publishPacket in qosPackets) | |||||
{ | { | ||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); | |||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false); | |||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false); | |||||
foreach (var publishPacket in qosPackets) | |||||
{ | |||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); | |||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false); | |||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false); | |||||
} | |||||
break; | |||||
} | } | ||||
break; | |||||
} | |||||
default: | default: | ||||
{ | |||||
throw new InvalidOperationException(); | |||||
} | |||||
{ | |||||
throw new InvalidOperationException(); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -191,7 +190,7 @@ namespace MQTTnet.Core.Client | |||||
Username = _options.Credentials?.Username, | Username = _options.Credentials?.Username, | ||||
Password = _options.Credentials?.Password, | Password = _options.Credentials?.Password, | ||||
CleanSession = _options.CleanSession, | CleanSession = _options.CleanSession, | ||||
KeepAlivePeriod = (ushort) _options.KeepAlivePeriod.TotalSeconds, | |||||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds, | |||||
WillMessage = willApplicationMessage | WillMessage = willApplicationMessage | ||||
}; | }; | ||||
@@ -204,24 +203,6 @@ namespace MQTTnet.Core.Client | |||||
return response; | return response; | ||||
} | } | ||||
private async Task SetupIncomingPacketProcessingAsync() | |||||
{ | |||||
_isReceivingPackets = false; | |||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | |||||
Task.Factory.StartNew( | |||||
() => ReceivePackets(_cancellationTokenSource.Token), | |||||
_cancellationTokenSource.Token, | |||||
TaskCreationOptions.LongRunning, | |||||
TaskScheduler.Default).ConfigureAwait(false); | |||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | |||||
while (!_isReceivingPackets && _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested) | |||||
{ | |||||
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); | |||||
} | |||||
} | |||||
private void ThrowIfNotConnected() | private void ThrowIfNotConnected() | ||||
{ | { | ||||
if (!IsConnected) throw new MqttCommunicationException("The client is not connected."); | if (!IsConnected) throw new MqttCommunicationException("The client is not connected."); | ||||
@@ -234,6 +215,8 @@ namespace MQTTnet.Core.Client | |||||
private async Task DisconnectInternalAsync() | private async Task DisconnectInternalAsync() | ||||
{ | { | ||||
_scopeHandle?.Dispose(); | |||||
var clientWasConnected = IsConnected; | var clientWasConnected = IsConnected; | ||||
IsConnected = false; | IsConnected = false; | ||||
@@ -258,6 +241,7 @@ namespace MQTTnet.Core.Client | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
_logger.LogInformation("Disconnected."); | |||||
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected)); | Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected)); | ||||
} | } | ||||
} | } | ||||
@@ -324,7 +308,7 @@ namespace MQTTnet.Core.Client | |||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) | if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) | ||||
{ | { | ||||
FireApplicationMessageReceivedEvent(publishPacket); | FireApplicationMessageReceivedEvent(publishPacket); | ||||
await SendAsync(new MqttPubAckPacket {PacketIdentifier = publishPacket.PacketIdentifier}); | |||||
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }); | |||||
return; | return; | ||||
} | } | ||||
@@ -337,7 +321,7 @@ namespace MQTTnet.Core.Client | |||||
} | } | ||||
FireApplicationMessageReceivedEvent(publishPacket); | FireApplicationMessageReceivedEvent(publishPacket); | ||||
await SendAsync(new MqttPubRecPacket {PacketIdentifier = publishPacket.PacketIdentifier}); | |||||
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }); | |||||
return; | return; | ||||
} | } | ||||
@@ -363,12 +347,12 @@ namespace MQTTnet.Core.Client | |||||
{ | { | ||||
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.CommunicationTimeout); | var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.CommunicationTimeout); | ||||
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false); | await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false); | ||||
return (TResponsePacket) await packetAwaiter.ConfigureAwait(false); | |||||
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false); | |||||
} | } | ||||
private ushort GetNewPacketIdentifier() | private ushort GetNewPacketIdentifier() | ||||
{ | { | ||||
return (ushort) Interlocked.Increment(ref _latestPacketIdentifier); | |||||
return (ushort)Interlocked.Increment(ref _latestPacketIdentifier); | |||||
} | } | ||||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) | private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) | ||||
@@ -390,6 +374,12 @@ namespace MQTTnet.Core.Client | |||||
} | } | ||||
catch (OperationCanceledException) | catch (OperationCanceledException) | ||||
{ | { | ||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return; | |||||
} | |||||
await DisconnectInternalAsync().ConfigureAwait(false); | |||||
} | } | ||||
catch (MqttCommunicationException exception) | catch (MqttCommunicationException exception) | ||||
{ | { | ||||
@@ -412,7 +402,7 @@ namespace MQTTnet.Core.Client | |||||
} | } | ||||
} | } | ||||
private async Task ReceivePackets(CancellationToken cancellationToken) | |||||
private async Task ReceivePacketsAsync(CancellationToken cancellationToken) | |||||
{ | { | ||||
_logger.LogInformation("Start receiving packets."); | _logger.LogInformation("Start receiving packets."); | ||||
@@ -423,6 +413,7 @@ namespace MQTTnet.Core.Client | |||||
_isReceivingPackets = true; | _isReceivingPackets = true; | ||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); | var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); | ||||
if (cancellationToken.IsCancellationRequested) | if (cancellationToken.IsCancellationRequested) | ||||
{ | { | ||||
return; | return; | ||||
@@ -433,6 +424,12 @@ namespace MQTTnet.Core.Client | |||||
} | } | ||||
catch (OperationCanceledException) | catch (OperationCanceledException) | ||||
{ | { | ||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return; | |||||
} | |||||
await DisconnectInternalAsync().ConfigureAwait(false); | |||||
} | } | ||||
catch (MqttCommunicationException exception) | catch (MqttCommunicationException exception) | ||||
{ | { | ||||
@@ -458,15 +455,34 @@ namespace MQTTnet.Core.Client | |||||
private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken) | private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken) | ||||
{ | { | ||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
Task.Run(() => ProcessReceivedPacketAsync(packet), cancellationToken).ConfigureAwait(false); | |||||
Task.Run( | |||||
() => ProcessReceivedPacketAsync(packet), | |||||
cancellationToken).ConfigureAwait(false); | |||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | |||||
} | |||||
private async Task StartReceivingPacketsAsync(CancellationToken cancellationToken) | |||||
{ | |||||
_isReceivingPackets = false; | |||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | |||||
Task.Run( | |||||
async () => await ReceivePacketsAsync(cancellationToken), | |||||
cancellationToken).ConfigureAwait(false); | |||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
while (!_isReceivingPackets && !cancellationToken.IsCancellationRequested) | |||||
{ | |||||
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false); | |||||
} | |||||
} | } | ||||
private void StartSendKeepAliveMessages(CancellationToken cancellationToken) | |||||
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken) | |||||
{ | { | ||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
Task.Factory.StartNew(() => SendKeepAliveMessagesAsync(cancellationToken), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default) | |||||
.ConfigureAwait(false); | |||||
Task.Run( | |||||
async () => await SendKeepAliveMessagesAsync(cancellationToken), | |||||
cancellationToken).ConfigureAwait(false); | |||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
} | } | ||||
} | } |
@@ -1,5 +1,4 @@ | |||||
using System; | |||||
using MQTTnet.Core.Protocol; | |||||
using MQTTnet.Core.Protocol; | |||||
namespace MQTTnet.Core.Packets | namespace MQTTnet.Core.Packets | ||||
{ | { | ||||
@@ -19,7 +18,7 @@ namespace MQTTnet.Core.Packets | |||||
{ | { | ||||
return nameof(MqttPublishPacket) + | return nameof(MqttPublishPacket) + | ||||
": [Topic=" + Topic + "]" + | ": [Topic=" + Topic + "]" + | ||||
" [Payload=" + Convert.ToBase64String(Payload) + "]" + | |||||
" [PayloadLength=" + Payload?.Length + "]" + | |||||
" [QoSLevel=" + QualityOfServiceLevel + "]" + | " [QoSLevel=" + QualityOfServiceLevel + "]" + | ||||
" [Dup=" + Dup + "]" + | " [Dup=" + Dup + "]" + | ||||
" [Retain=" + Retain + "]" + | " [Retain=" + Retain + "]" + | ||||
@@ -91,7 +91,7 @@ namespace MQTTnet.Core.Serializer | |||||
if (buffer == -1) | if (buffer == -1) | ||||
{ | { | ||||
break; | |||||
throw new MqttCommunicationException("Connection closed while reading remaining length data."); | |||||
} | } | ||||
encodedByte = (byte)buffer; | encodedByte = (byte)buffer; | ||||
@@ -78,6 +78,7 @@ | |||||
</StackPanel> | </StackPanel> | ||||
<TextBlock>Received messages:</TextBlock> | <TextBlock>Received messages:</TextBlock> | ||||
<CheckBox x:Name="AddReceivedMessagesToList" IsChecked="True">Add received messages to list</CheckBox> | |||||
<ListBox MinHeight="50" MaxHeight="250" x:Name="ReceivedMessages" Margin="0,0,0,10"> | <ListBox MinHeight="50" MaxHeight="250" x:Name="ReceivedMessages" Margin="0,0,0,10"> | ||||
<ListBox.ItemTemplate> | <ListBox.ItemTemplate> | ||||
<DataTemplate> | <DataTemplate> | ||||
@@ -30,14 +30,14 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
private async void OnTraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e) | private async void OnTraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e) | ||||
{ | { | ||||
await Trace.Dispatcher.RunAsync(CoreDispatcherPriority.High, () => | |||||
var text = $"[{e.TraceMessage.Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{e.TraceMessage.Level}] [{e.TraceMessage.Source}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Message}]{Environment.NewLine}"; | |||||
if (e.TraceMessage.Exception != null) | |||||
{ | |||||
text += $"{e.TraceMessage.Exception}{Environment.NewLine}"; | |||||
} | |||||
await Trace.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => | |||||
{ | { | ||||
var text = $"[{e.TraceMessage.Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{e.TraceMessage.Level}] [{e.TraceMessage.Source}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Message}]{Environment.NewLine}"; | |||||
if (e.TraceMessage.Exception != null) | |||||
{ | |||||
text += $"{e.TraceMessage.Exception}{Environment.NewLine}"; | |||||
} | |||||
Trace.Text += text; | Trace.Text += text; | ||||
}); | }); | ||||
} | } | ||||
@@ -109,11 +109,13 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
{ | { | ||||
var item = $"Timestamp: {DateTime.Now:O} | Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload)} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel}"; | var item = $"Timestamp: {DateTime.Now:O} | Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload)} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel}"; | ||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => | |||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => | |||||
{ | { | ||||
ReceivedMessages.Items.Add(item); | |||||
if (AddReceivedMessagesToList.IsChecked == true) | |||||
{ | |||||
ReceivedMessages.Items.Add(item); | |||||
} | |||||
}); | }); | ||||
} | } | ||||
private async void Publish(object sender, RoutedEventArgs e) | private async void Publish(object sender, RoutedEventArgs e) | ||||