@@ -10,9 +10,11 @@ | |||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> | |||
<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> | |||
<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] 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 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] Published messages from the server are now retained (if set) (Thanks to @ChristianRiedl). BREAKING CHANGE! | |||
</releaseNotes> | |||
@@ -1,12 +1,8 @@ | |||
using System; | |||
using System.IO; | |||
using System.Net.WebSockets; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Adapter; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Server; | |||
using MQTTnet.Implementations; | |||
namespace MQTTnet.AspNetCore | |||
{ | |||
@@ -47,47 +43,5 @@ namespace MQTTnet.AspNetCore | |||
{ | |||
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 | |||
{ | |||
// 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 ClientWebSocket _webSocket; | |||
@@ -80,7 +84,7 @@ namespace MQTTnet.Implementations | |||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false); | |||
} | |||
_webSocket = null; | |||
Dispose(); | |||
} | |||
public void Dispose() | |||
@@ -1,5 +1,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net.WebSockets; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -10,7 +12,9 @@ namespace MQTTnet.Implementations | |||
public class WebSocketStream : Stream | |||
{ | |||
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) | |||
{ | |||
_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) | |||
{ | |||
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) | |||
{ | |||
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) | |||
@@ -82,5 +109,21 @@ namespace MQTTnet.Implementations | |||
{ | |||
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) | |||
{ | |||
_serviceProvider = serviceProvider; | |||
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); | |||
} | |||
public ILoggerFactory GetLoggerFactory() | |||
@@ -60,14 +60,14 @@ namespace MQTTnet.Core.Client | |||
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); | |||
_logger.LogTrace("Connection with server established."); | |||
await SetupIncomingPacketProcessingAsync().ConfigureAwait(false); | |||
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false); | |||
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false); | |||
_logger.LogTrace("MQTT connection with server established."); | |||
if (_options.KeepAlivePeriod != TimeSpan.Zero) | |||
{ | |||
StartSendKeepAliveMessages(_cancellationTokenSource.Token); | |||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token); | |||
} | |||
IsConnected = true; | |||
@@ -96,7 +96,6 @@ namespace MQTTnet.Core.Client | |||
finally | |||
{ | |||
await DisconnectInternalAsync().ConfigureAwait(false); | |||
_scopeHandle.Dispose(); | |||
} | |||
} | |||
@@ -149,36 +148,36 @@ namespace MQTTnet.Core.Client | |||
switch (qosGroup.Key) | |||
{ | |||
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: | |||
{ | |||
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: | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
} | |||
} | |||
} | |||
@@ -191,7 +190,7 @@ namespace MQTTnet.Core.Client | |||
Username = _options.Credentials?.Username, | |||
Password = _options.Credentials?.Password, | |||
CleanSession = _options.CleanSession, | |||
KeepAlivePeriod = (ushort) _options.KeepAlivePeriod.TotalSeconds, | |||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds, | |||
WillMessage = willApplicationMessage | |||
}; | |||
@@ -204,24 +203,6 @@ namespace MQTTnet.Core.Client | |||
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() | |||
{ | |||
if (!IsConnected) throw new MqttCommunicationException("The client is not connected."); | |||
@@ -234,6 +215,8 @@ namespace MQTTnet.Core.Client | |||
private async Task DisconnectInternalAsync() | |||
{ | |||
_scopeHandle?.Dispose(); | |||
var clientWasConnected = IsConnected; | |||
IsConnected = false; | |||
@@ -258,6 +241,7 @@ namespace MQTTnet.Core.Client | |||
} | |||
finally | |||
{ | |||
_logger.LogInformation("Disconnected."); | |||
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected)); | |||
} | |||
} | |||
@@ -324,7 +308,7 @@ namespace MQTTnet.Core.Client | |||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) | |||
{ | |||
FireApplicationMessageReceivedEvent(publishPacket); | |||
await SendAsync(new MqttPubAckPacket {PacketIdentifier = publishPacket.PacketIdentifier}); | |||
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }); | |||
return; | |||
} | |||
@@ -337,7 +321,7 @@ namespace MQTTnet.Core.Client | |||
} | |||
FireApplicationMessageReceivedEvent(publishPacket); | |||
await SendAsync(new MqttPubRecPacket {PacketIdentifier = publishPacket.PacketIdentifier}); | |||
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }); | |||
return; | |||
} | |||
@@ -363,12 +347,12 @@ namespace MQTTnet.Core.Client | |||
{ | |||
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.CommunicationTimeout); | |||
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() | |||
{ | |||
return (ushort) Interlocked.Increment(ref _latestPacketIdentifier); | |||
return (ushort)Interlocked.Increment(ref _latestPacketIdentifier); | |||
} | |||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) | |||
@@ -390,6 +374,12 @@ namespace MQTTnet.Core.Client | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
if (cancellationToken.IsCancellationRequested) | |||
{ | |||
return; | |||
} | |||
await DisconnectInternalAsync().ConfigureAwait(false); | |||
} | |||
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."); | |||
@@ -423,6 +413,7 @@ namespace MQTTnet.Core.Client | |||
_isReceivingPackets = true; | |||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); | |||
if (cancellationToken.IsCancellationRequested) | |||
{ | |||
return; | |||
@@ -433,6 +424,12 @@ namespace MQTTnet.Core.Client | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
if (cancellationToken.IsCancellationRequested) | |||
{ | |||
return; | |||
} | |||
await DisconnectInternalAsync().ConfigureAwait(false); | |||
} | |||
catch (MqttCommunicationException exception) | |||
{ | |||
@@ -458,15 +455,34 @@ namespace MQTTnet.Core.Client | |||
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 | |||
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 | |||
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 | |||
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 | |||
} | |||
} |
@@ -1,5 +1,4 @@ | |||
using System; | |||
using MQTTnet.Core.Protocol; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
@@ -19,7 +18,7 @@ namespace MQTTnet.Core.Packets | |||
{ | |||
return nameof(MqttPublishPacket) + | |||
": [Topic=" + Topic + "]" + | |||
" [Payload=" + Convert.ToBase64String(Payload) + "]" + | |||
" [PayloadLength=" + Payload?.Length + "]" + | |||
" [QoSLevel=" + QualityOfServiceLevel + "]" + | |||
" [Dup=" + Dup + "]" + | |||
" [Retain=" + Retain + "]" + | |||
@@ -91,7 +91,7 @@ namespace MQTTnet.Core.Serializer | |||
if (buffer == -1) | |||
{ | |||
break; | |||
throw new MqttCommunicationException("Connection closed while reading remaining length data."); | |||
} | |||
encodedByte = (byte)buffer; | |||
@@ -78,6 +78,7 @@ | |||
</StackPanel> | |||
<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.ItemTemplate> | |||
<DataTemplate> | |||
@@ -30,14 +30,14 @@ namespace MQTTnet.TestApp.UniversalWindows | |||
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; | |||
}); | |||
} | |||
@@ -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}"; | |||
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) | |||