mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-19 02:53:06 +08:00
Fixed WebSocket client stream
This commit is contained in:
@@ -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,33 +38,9 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public override Task FlushAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var currentOffset = offset;
|
return Task.FromResult(0);
|
||||||
var targetOffset = offset + count;
|
|
||||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_webSocket.State == WebSocketState.Closed)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException( "connection closed" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentOffset - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 int Read(byte[] buffer, int offset, int count)
|
||||||
@@ -68,11 +48,58 @@ namespace MQTTnet.Implementations
|
|||||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var bytesRead = 0;
|
||||||
|
|
||||||
|
// Use existing date from buffer.
|
||||||
|
while (count > 0 && _buffer.Any())
|
||||||
|
{
|
||||||
|
buffer[offset++] = _buffer.Dequeue();
|
||||||
|
count--;
|
||||||
|
bytesRead++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_webSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
await FetchChunkAsync(cancellationToken);
|
||||||
|
|
||||||
|
while (count > 0 && _buffer.Any())
|
||||||
|
{
|
||||||
|
buffer[offset++] = _buffer.Dequeue();
|
||||||
|
count--;
|
||||||
|
bytesRead++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_webSocket.State == WebSocketState.Closed)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException("WebSocket connection closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 long Seek(long offset, SeekOrigin origin)
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
@@ -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]
|
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||||
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false);
|
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
{
|
|
||||||
foreach (var publishPacket in qosPackets)
|
|
||||||
{
|
{
|
||||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
|
foreach (var publishPacket in qosPackets)
|
||||||
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
|
{
|
||||||
}
|
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();
|
foreach (var publishPacket in qosPackets)
|
||||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
|
{
|
||||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false);
|
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
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartSendKeepAliveMessages(CancellationToken cancellationToken)
|
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 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)
|
Task.Run(
|
||||||
.ConfigureAwait(false);
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user