@@ -2,7 +2,7 @@ | |||||
<package > | <package > | ||||
<metadata> | <metadata> | ||||
<id>MQTTnet</id> | <id>MQTTnet</id> | ||||
<version>2.7.1</version> | |||||
<version>2.7.2</version> | |||||
<authors>Christian Kratky</authors> | <authors>Christian Kratky</authors> | ||||
<owners>Christian Kratky</owners> | <owners>Christian Kratky</owners> | ||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl> | ||||
@@ -10,15 +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 wrong parsing of ConnAck packet for protocol version 3.1.0. | |||||
* [Core] Log messages are now overriding ToString() and providing a ready to use text representation. | |||||
* [Client] Optimized package dispatcher and added several new exceptions. | |||||
* [Client] The _ManagedClient_ now has an event which is fired after a queued application message was processed (including exception). | |||||
* [Client] The _ManagedClient_ now supports unsubscribing (thanks to @lerppana) | |||||
* [Server] Fixed some minor async issues. | |||||
* [Server] Fixed wrong comparison of the topic and QoS for retained messages. | |||||
* [Server] Added a property which provides access to the used options (read only). | |||||
* [Server] Fixed a null ref expection when using an interceptor and publishing via the server directly. | |||||
<releaseNotes>* [Client] Added the subprotocol "mqtt" as default for web socket based connections. | |||||
* [Client] Added a new client setting called "KeepAliveSendInterval". It allows configuring the effective interval for sending ping requests. | |||||
* [Client] The client will no longer send ping requests if other packets are sent within the configured interval. | |||||
* [Server] The server now generates a valid packet identifier when disaptching publish packets to clients. | |||||
* [Core] Add several new extension methods. | |||||
</releaseNotes> | </releaseNotes> | ||||
<copyright>Copyright Christian Kratky 2016-2018</copyright> | <copyright>Copyright Christian Kratky 2016-2018</copyright> | ||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags> | <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags> | ||||
@@ -138,7 +138,7 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
var body = header.BodyLength <= ReadBufferSize ? new MemoryStream(header.BodyLength) : new MemoryStream(); | var body = header.BodyLength <= ReadBufferSize ? new MemoryStream(header.BodyLength) : new MemoryStream(); | ||||
var buffer = new byte[ReadBufferSize]; | var buffer = new byte[ReadBufferSize]; | ||||
while (body.Length < header.BodyLength) | while (body.Length < header.BodyLength) | ||||
{ | { | ||||
@@ -149,7 +149,7 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
var readBytesCount = await stream.ReadAsync(buffer, 0, bytesLeft, cancellationToken).ConfigureAwait(false); | var readBytesCount = await stream.ReadAsync(buffer, 0, bytesLeft, cancellationToken).ConfigureAwait(false); | ||||
// Check if the client closed the connection before sending the full body. | // Check if the client closed the connection before sending the full body. | ||||
if (readBytesCount == 0) | if (readBytesCount == 0) | ||||
{ | { | ||||
@@ -162,7 +162,7 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
body.Seek(0L, SeekOrigin.Begin); | body.Seek(0L, SeekOrigin.Begin); | ||||
return new ReceivedMqttPacket(header, body); | return new ReceivedMqttPacket(header, body); | ||||
} | } | ||||
@@ -190,7 +190,7 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
catch (COMException comException) | catch (COMException comException) | ||||
{ | { | ||||
if ((uint) comException.HResult == ErrorOperationAborted) | |||||
if ((uint)comException.HResult == ErrorOperationAborted) | |||||
{ | { | ||||
throw new OperationCanceledException(); | throw new OperationCanceledException(); | ||||
} | } | ||||
@@ -1,17 +0,0 @@ | |||||
using System; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using MQTTnet.Packets; | |||||
namespace MQTTnet.Adapter | |||||
{ | |||||
public static class MqttChannelAdapterExtensions | |||||
{ | |||||
public static Task SendPacketsAsync(this IMqttChannelAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets) | |||||
{ | |||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter)); | |||||
return adapter.SendPacketsAsync(timeout, cancellationToken, packets); | |||||
} | |||||
} | |||||
} |
@@ -1,16 +1,41 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Protocol; | |||||
namespace MQTTnet | namespace MQTTnet | ||||
{ | { | ||||
public static class ApplicationMessagePublisherExtensions | public static class ApplicationMessagePublisherExtensions | ||||
{ | { | ||||
public static Task PublishAsync(this IApplicationMessagePublisher client, params MqttApplicationMessage[] applicationMessages) | |||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages) | |||||
{ | { | ||||
if (client == null) throw new ArgumentNullException(nameof(client)); | |||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher)); | |||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages)); | if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages)); | ||||
return client.PublishAsync(applicationMessages); | |||||
return publisher.PublishAsync(applicationMessages); | |||||
} | |||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic) | |||||
{ | |||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher)); | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).Build()); | |||||
} | |||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload) | |||||
{ | |||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher)); | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).Build()); | |||||
} | |||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel) | |||||
{ | |||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher)); | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -13,6 +13,8 @@ namespace MQTTnet.Client | |||||
TimeSpan CommunicationTimeout { get; } | TimeSpan CommunicationTimeout { get; } | ||||
TimeSpan KeepAlivePeriod { get; } | TimeSpan KeepAlivePeriod { get; } | ||||
TimeSpan? KeepAliveSendInterval { get; set; } | |||||
MqttProtocolVersion ProtocolVersion { get; } | MqttProtocolVersion ProtocolVersion { get; } | ||||
IMqttClientChannelOptions ChannelOptions { get; } | IMqttClientChannelOptions ChannelOptions { get; } | ||||
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -15,6 +16,7 @@ namespace MQTTnet.Client | |||||
public class MqttClient : IMqttClient | public class MqttClient : IMqttClient | ||||
{ | { | ||||
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider(); | private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider(); | ||||
private readonly Stopwatch _sendTracker = new Stopwatch(); | |||||
private readonly IMqttClientAdapterFactory _adapterFactory; | private readonly IMqttClientAdapterFactory _adapterFactory; | ||||
private readonly MqttPacketDispatcher _packetDispatcher; | private readonly MqttPacketDispatcher _packetDispatcher; | ||||
private readonly IMqttNetLogger _logger; | private readonly IMqttNetLogger _logger; | ||||
@@ -59,10 +61,12 @@ namespace MQTTnet.Client | |||||
_logger.Trace<MqttClient>("Connection with server established."); | _logger.Trace<MqttClient>("Connection with server established."); | ||||
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).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.Trace<MqttClient>("MQTT connection with server established."); | _logger.Trace<MqttClient>("MQTT connection with server established."); | ||||
_sendTracker.Restart(); | |||||
if (_options.KeepAlivePeriod != TimeSpan.Zero) | if (_options.KeepAlivePeriod != TimeSpan.Zero) | ||||
{ | { | ||||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token); | StartSendingKeepAliveMessages(_cancellationTokenSource.Token); | ||||
@@ -149,7 +153,7 @@ namespace MQTTnet.Client | |||||
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, qosGroup).ConfigureAwait(false); | |||||
await SendAsync(qosGroup.ToArray()).ConfigureAwait(false); | |||||
break; | break; | ||||
} | } | ||||
case MqttQualityOfServiceLevel.AtLeastOnce: | case MqttQualityOfServiceLevel.AtLeastOnce: | ||||
@@ -167,8 +171,14 @@ namespace MQTTnet.Client | |||||
foreach (var publishPacket in qosGroup) | foreach (var publishPacket in qosGroup) | ||||
{ | { | ||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(); | publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(); | ||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false); | var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false); | ||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false); | |||||
var pubRelPacket = new MqttPubRelPacket | |||||
{ | |||||
PacketIdentifier = pubRecPacket.PacketIdentifier | |||||
}; | |||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket).ConfigureAwait(false); | |||||
} | } | ||||
break; | break; | ||||
@@ -325,24 +335,31 @@ namespace MQTTnet.Client | |||||
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket) | private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket) | ||||
{ | { | ||||
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>()); | |||||
var response = new MqttPubCompPacket | |||||
{ | |||||
PacketIdentifier = pubRelPacket.PacketIdentifier | |||||
}; | |||||
return SendAsync(response); | |||||
} | } | ||||
private Task SendAsync(MqttBasePacket packet) | |||||
private Task SendAsync(params MqttBasePacket[] packets) | |||||
{ | { | ||||
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packet); | |||||
_sendTracker.Restart(); | |||||
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packets); | |||||
} | } | ||||
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket | private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket | ||||
{ | { | ||||
ushort identifier = 0; | |||||
ushort? identifier = null; | |||||
if (requestPacket is IMqttPacketWithIdentifier requestPacketWithIdentifier) | if (requestPacket is IMqttPacketWithIdentifier requestPacketWithIdentifier) | ||||
{ | { | ||||
identifier = requestPacketWithIdentifier.PacketIdentifier; | identifier = requestPacketWithIdentifier.PacketIdentifier; | ||||
} | } | ||||
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(typeof(TResponsePacket), identifier, _options.CommunicationTimeout); | var packetAwaiter = _packetDispatcher.WaitForPacketAsync(typeof(TResponsePacket), identifier, _options.CommunicationTimeout); | ||||
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false); | |||||
await SendAsync(requestPacket).ConfigureAwait(false); | |||||
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false); | return (TResponsePacket)await packetAwaiter.ConfigureAwait(false); | ||||
} | } | ||||
@@ -354,8 +371,22 @@ namespace MQTTnet.Client | |||||
{ | { | ||||
while (!cancellationToken.IsCancellationRequested) | while (!cancellationToken.IsCancellationRequested) | ||||
{ | { | ||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false); | |||||
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false); | |||||
TimeSpan keepAliveSendInterval; | |||||
if (_options.KeepAliveSendInterval.HasValue) | |||||
{ | |||||
keepAliveSendInterval = _options.KeepAliveSendInterval.Value; | |||||
} | |||||
else | |||||
{ | |||||
keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75); | |||||
} | |||||
if (_sendTracker.Elapsed > keepAliveSendInterval) | |||||
{ | |||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false); | |||||
} | |||||
await Task.Delay(keepAliveSendInterval, cancellationToken).ConfigureAwait(false); | |||||
} | } | ||||
} | } | ||||
catch (OperationCanceledException) | catch (OperationCanceledException) | ||||
@@ -24,6 +24,14 @@ namespace MQTTnet.Client | |||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()); | return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()); | ||||
} | } | ||||
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic) | |||||
{ | |||||
if (client == null) throw new ArgumentNullException(nameof(client)); | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build()); | |||||
} | |||||
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters) | public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters) | ||||
{ | { | ||||
if (client == null) throw new ArgumentNullException(nameof(client)); | if (client == null) throw new ArgumentNullException(nameof(client)); | ||||
@@ -13,7 +13,9 @@ namespace MQTTnet.Client | |||||
public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials(); | public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials(); | ||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5); | |||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15); | |||||
public TimeSpan? KeepAliveSendInterval { get; set; } | |||||
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); | public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); | ||||
@@ -9,7 +9,7 @@ namespace MQTTnet.Client | |||||
public IDictionary<string, string> RequestHeaders { get; set; } | public IDictionary<string, string> RequestHeaders { get; set; } | ||||
public ICollection<string> SubProtocols { get; set; } | |||||
public ICollection<string> SubProtocols { get; set; } = new List<string> { "mqtt" }; | |||||
public CookieContainer CookieContainer { get; set; } | public CookieContainer CookieContainer { get; set; } | ||||
@@ -18,7 +18,7 @@ namespace MQTTnet.Client | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
} | } | ||||
public async Task<MqttBasePacket> WaitForPacketAsync(Type responseType, ushort identifier, TimeSpan timeout) | |||||
public async Task<MqttBasePacket> WaitForPacketAsync(Type responseType, ushort? identifier, TimeSpan timeout) | |||||
{ | { | ||||
var packetAwaiter = AddPacketAwaiter(responseType, identifier); | var packetAwaiter = AddPacketAwaiter(responseType, identifier); | ||||
try | try | ||||
@@ -44,13 +44,13 @@ namespace MQTTnet.Client | |||||
if (_awaiters.TryGetValue(type, out var byId)) | if (_awaiters.TryGetValue(type, out var byId)) | ||||
{ | { | ||||
ushort identifier = 0; | |||||
ushort? identifier = 0; | |||||
if (packet is IMqttPacketWithIdentifier packetWithIdentifier) | if (packet is IMqttPacketWithIdentifier packetWithIdentifier) | ||||
{ | { | ||||
identifier = packetWithIdentifier.PacketIdentifier; | identifier = packetWithIdentifier.PacketIdentifier; | ||||
} | } | ||||
if (byId.TryRemove(identifier, out var tcs)) | |||||
if (byId.TryRemove(identifier.Value, out var tcs)) | |||||
{ | { | ||||
tcs.TrySetResult(packet); | tcs.TrySetResult(packet); | ||||
return; | return; | ||||
@@ -65,12 +65,17 @@ namespace MQTTnet.Client | |||||
_awaiters.Clear(); | _awaiters.Clear(); | ||||
} | } | ||||
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(Type responseType, ushort identifier) | |||||
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(Type responseType, ushort? identifier) | |||||
{ | { | ||||
var tcs = new TaskCompletionSource<MqttBasePacket>(); | var tcs = new TaskCompletionSource<MqttBasePacket>(); | ||||
if (!identifier.HasValue) | |||||
{ | |||||
identifier = 0; | |||||
} | |||||
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>()); | var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>()); | ||||
if (!byId.TryAdd(identifier, tcs)) | |||||
if (!byId.TryAdd(identifier.Value, tcs)) | |||||
{ | { | ||||
throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{responseType}' with identifier {identifier}."); | throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{responseType}' with identifier {identifier}."); | ||||
} | } | ||||
@@ -78,10 +83,15 @@ namespace MQTTnet.Client | |||||
return tcs; | return tcs; | ||||
} | } | ||||
private void RemovePacketAwaiter(Type responseType, ushort identifier) | |||||
private void RemovePacketAwaiter(Type responseType, ushort? identifier) | |||||
{ | { | ||||
if (!identifier.HasValue) | |||||
{ | |||||
identifier = 0; | |||||
} | |||||
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>()); | var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>()); | ||||
byId.TryRemove(identifier, out var _); | |||||
byId.TryRemove(identifier.Value, out var _); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -69,12 +69,6 @@ namespace MQTTnet.Implementations | |||||
public void Dispose() | public void Dispose() | ||||
{ | { | ||||
SendStream?.Dispose(); | |||||
SendStream = null; | |||||
ReceiveStream?.Dispose(); | |||||
ReceiveStream = null; | |||||
_socket?.Dispose(); | _socket?.Dispose(); | ||||
_socket = null; | _socket = null; | ||||
} | } | ||||
@@ -11,9 +11,9 @@ namespace MQTTnet.Implementations | |||||
{ | { | ||||
public class WebSocketStream : Stream | public class WebSocketStream : Stream | ||||
{ | { | ||||
private readonly WebSocket _webSocket; | |||||
private readonly byte[] _chunkBuffer = new byte[MqttWebSocketChannel.BufferSize]; | |||||
private readonly byte[] _chunckBuffer = new byte[MqttWebSocketChannel.BufferSize]; | |||||
private readonly Queue<byte> _buffer = new Queue<byte>(MqttWebSocketChannel.BufferSize); | private readonly Queue<byte> _buffer = new Queue<byte>(MqttWebSocketChannel.BufferSize); | ||||
private readonly WebSocket _webSocket; | |||||
public WebSocketStream(WebSocket webSocket) | public WebSocketStream(WebSocket webSocket) | ||||
{ | { | ||||
@@ -55,25 +55,28 @@ namespace MQTTnet.Implementations | |||||
// Use existing date from buffer. | // Use existing date from buffer. | ||||
while (count > 0 && _buffer.Any()) | while (count > 0 && _buffer.Any()) | ||||
{ | { | ||||
buffer[offset++] = _buffer.Dequeue(); | |||||
buffer[offset] = _buffer.Dequeue(); | |||||
count--; | count--; | ||||
bytesRead++; | bytesRead++; | ||||
offset++; | |||||
} | } | ||||
if (count == 0) | if (count == 0) | ||||
{ | { | ||||
return bytesRead; | return bytesRead; | ||||
} | } | ||||
// Fetch new data if the buffer is not full. | |||||
while (_webSocket.State == WebSocketState.Open) | while (_webSocket.State == WebSocketState.Open) | ||||
{ | { | ||||
await FetchChunkAsync(cancellationToken); | |||||
await FetchChunkAsync(cancellationToken).ConfigureAwait(false); | |||||
while (count > 0 && _buffer.Any()) | while (count > 0 && _buffer.Any()) | ||||
{ | { | ||||
buffer[offset++] = _buffer.Dequeue(); | |||||
buffer[offset] = _buffer.Dequeue(); | |||||
count--; | count--; | ||||
bytesRead++; | bytesRead++; | ||||
offset++; | |||||
} | } | ||||
if (count == 0) | if (count == 0) | ||||
@@ -111,19 +114,24 @@ namespace MQTTnet.Implementations | |||||
} | } | ||||
private async Task FetchChunkAsync(CancellationToken cancellationToken) | 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); | |||||
} | |||||
{ | |||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(_chunckBuffer, 0, _chunckBuffer.Length), cancellationToken).ConfigureAwait(false); | |||||
if (response.MessageType == WebSocketMessageType.Close) | if (response.MessageType == WebSocketMessageType.Close) | ||||
{ | { | ||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false); | await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false); | ||||
} | } | ||||
else if (response.MessageType == WebSocketMessageType.Binary) | |||||
{ | |||||
for (var i = 0; i < response.Count; i++) | |||||
{ | |||||
_buffer.Enqueue(_chunckBuffer[i]); | |||||
} | |||||
} | |||||
else if (response.MessageType == WebSocketMessageType.Text) | |||||
{ | |||||
throw new MqttProtocolViolationException("WebSocket channel received TEXT message."); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -6,6 +6,7 @@ namespace MQTTnet.ManagedClient | |||||
public class ManagedMqttClientOptionsBuilder | public class ManagedMqttClientOptionsBuilder | ||||
{ | { | ||||
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions(); | private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions(); | ||||
private MqttClientOptionsBuilder _clientOptionsBuilder; | |||||
public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value) | public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value) | ||||
{ | { | ||||
@@ -21,7 +22,24 @@ namespace MQTTnet.ManagedClient | |||||
public ManagedMqttClientOptionsBuilder WithClientOptions(IMqttClientOptions value) | public ManagedMqttClientOptionsBuilder WithClientOptions(IMqttClientOptions value) | ||||
{ | { | ||||
if (_clientOptionsBuilder != null) | |||||
{ | |||||
throw new InvalidOperationException("Cannot use client options builder and client options at the same time."); | |||||
} | |||||
_options.ClientOptions = value ?? throw new ArgumentNullException(nameof(value)); | _options.ClientOptions = value ?? throw new ArgumentNullException(nameof(value)); | ||||
return this; | |||||
} | |||||
public ManagedMqttClientOptionsBuilder WithClientOptions(MqttClientOptionsBuilder builder) | |||||
{ | |||||
if (_options.ClientOptions != null) | |||||
{ | |||||
throw new InvalidOperationException("Cannot use client options builder and client options at the same time."); | |||||
} | |||||
_clientOptionsBuilder = builder; | |||||
return this; | return this; | ||||
} | } | ||||
@@ -29,15 +47,22 @@ namespace MQTTnet.ManagedClient | |||||
{ | { | ||||
if (options == null) throw new ArgumentNullException(nameof(options)); | if (options == null) throw new ArgumentNullException(nameof(options)); | ||||
var builder = new MqttClientOptionsBuilder(); | |||||
options(builder); | |||||
_options.ClientOptions = builder.Build(); | |||||
if (_clientOptionsBuilder != null) | |||||
{ | |||||
_clientOptionsBuilder = new MqttClientOptionsBuilder(); | |||||
} | |||||
options(_clientOptionsBuilder); | |||||
return this; | return this; | ||||
} | } | ||||
public ManagedMqttClientOptions Build() | public ManagedMqttClientOptions Build() | ||||
{ | { | ||||
if (_clientOptionsBuilder != null) | |||||
{ | |||||
_options.ClientOptions = _clientOptionsBuilder.Build(); | |||||
} | |||||
if (_options.ClientOptions == null) | if (_options.ClientOptions == null) | ||||
{ | { | ||||
throw new InvalidOperationException("The ClientOptions cannot be null."); | throw new InvalidOperationException("The ClientOptions cannot be null."); | ||||
@@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Text; | |||||
namespace MQTTnet | |||||
{ | |||||
public static class MqttApplicationMessageExtensions | |||||
{ | |||||
public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage) | |||||
{ | |||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); | |||||
if (applicationMessage.Payload == null) | |||||
{ | |||||
return null; | |||||
} | |||||
if (applicationMessage.Payload.Length == 0) | |||||
{ | |||||
return string.Empty; | |||||
} | |||||
return Encoding.UTF8.GetString(applicationMessage.Payload, 0, applicationMessage.Payload.Length); | |||||
} | |||||
} | |||||
} |
@@ -2,6 +2,6 @@ | |||||
{ | { | ||||
public interface IMqttPacketWithIdentifier | public interface IMqttPacketWithIdentifier | ||||
{ | { | ||||
ushort PacketIdentifier { get; set; } | |||||
ushort? PacketIdentifier { get; set; } | |||||
} | } | ||||
} | } |
@@ -2,6 +2,6 @@ | |||||
{ | { | ||||
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier | public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier | ||||
{ | { | ||||
public ushort PacketIdentifier { get; set; } | |||||
public ushort? PacketIdentifier { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,26 +0,0 @@ | |||||
using System; | |||||
namespace MQTTnet.Packets | |||||
{ | |||||
public static class MqttPacketExtensions | |||||
{ | |||||
public static TResponsePacket CreateResponse<TResponsePacket>(this MqttBasePacket packet) | |||||
{ | |||||
if (packet == null) throw new ArgumentNullException(nameof(packet)); | |||||
var responsePacket = Activator.CreateInstance<TResponsePacket>(); | |||||
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier) | |||||
{ | |||||
if (!(packet is IMqttPacketWithIdentifier requestPacketWithIdentifier)) | |||||
{ | |||||
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not."); | |||||
} | |||||
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier; | |||||
} | |||||
return responsePacket; | |||||
} | |||||
} | |||||
} |
@@ -6,7 +6,7 @@ namespace MQTTnet.Packets | |||||
{ | { | ||||
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier | public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier | ||||
{ | { | ||||
public ushort PacketIdentifier { get; set; } | |||||
public ushort? PacketIdentifier { get; set; } | |||||
public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; } = new List<MqttSubscribeReturnCode>(); | public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; } = new List<MqttSubscribeReturnCode>(); | ||||
@@ -5,7 +5,7 @@ namespace MQTTnet.Packets | |||||
{ | { | ||||
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier | public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier | ||||
{ | { | ||||
public ushort PacketIdentifier { get; set; } | |||||
public ushort? PacketIdentifier { get; set; } | |||||
public IList<TopicFilter> TopicFilters { get; set; } = new List<TopicFilter>(); | public IList<TopicFilter> TopicFilters { get; set; } = new List<TopicFilter>(); | ||||
@@ -2,7 +2,7 @@ | |||||
{ | { | ||||
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier | public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier | ||||
{ | { | ||||
public ushort PacketIdentifier { get; set; } | |||||
public ushort? PacketIdentifier { get; set; } | |||||
public override string ToString() | public override string ToString() | ||||
{ | { | ||||
@@ -4,7 +4,7 @@ namespace MQTTnet.Packets | |||||
{ | { | ||||
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier | public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier | ||||
{ | { | ||||
public ushort PacketIdentifier { get; set; } | |||||
public ushort? PacketIdentifier { get; set; } | |||||
public IList<string> TopicFilters { get; set; } = new List<string>(); | public IList<string> TopicFilters { get; set; } = new List<string>(); | ||||
@@ -397,7 +397,12 @@ namespace MQTTnet.Serializer | |||||
private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter writer) | private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter writer) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("PubRel packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); | ||||
} | } | ||||
@@ -410,7 +415,12 @@ namespace MQTTnet.Serializer | |||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) | if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("Publish packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -444,21 +454,36 @@ namespace MQTTnet.Serializer | |||||
private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter writer) | private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter writer) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("PubAck packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); | ||||
} | } | ||||
private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter writer) | private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter writer) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("PubRec packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); | ||||
} | } | ||||
private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter writer) | private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter writer) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("PubComp packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); | ||||
} | } | ||||
@@ -467,7 +492,12 @@ namespace MQTTnet.Serializer | |||||
{ | { | ||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); | if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("Subscribe packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
if (packet.TopicFilters?.Count > 0) | if (packet.TopicFilters?.Count > 0) | ||||
{ | { | ||||
@@ -483,7 +513,12 @@ namespace MQTTnet.Serializer | |||||
private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter writer) | private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter writer) | ||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("SubAck packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
if (packet.SubscribeReturnCodes?.Any() == true) | if (packet.SubscribeReturnCodes?.Any() == true) | ||||
{ | { | ||||
@@ -500,7 +535,12 @@ namespace MQTTnet.Serializer | |||||
{ | { | ||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2]."); | if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2]."); | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
if (packet.TopicFilters?.Any() == true) | if (packet.TopicFilters?.Any() == true) | ||||
{ | { | ||||
@@ -513,9 +553,14 @@ namespace MQTTnet.Serializer | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); | ||||
} | } | ||||
private static byte Serialize(IMqttPacketWithIdentifier packet, BinaryWriter writer) | |||||
private static byte Serialize(MqttUnsubAckPacket packet, BinaryWriter writer) | |||||
{ | { | ||||
writer.Write(packet.PacketIdentifier); | |||||
if (!packet.PacketIdentifier.HasValue) | |||||
{ | |||||
throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier."); | |||||
} | |||||
writer.Write(packet.PacketIdentifier.Value); | |||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); | return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); | ||||
} | } | ||||
@@ -78,7 +78,7 @@ namespace MQTTnet.Server | |||||
throw new InvalidOperationException(); // should not happen | throw new InvalidOperationException(); // should not happen | ||||
} | } | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packet).ConfigureAwait(false); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false); | |||||
_logger.Trace<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId); | _logger.Trace<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId); | ||||
} | } | ||||
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Adapter; | using MQTTnet.Adapter; | ||||
using MQTTnet.Client; | |||||
using MQTTnet.Diagnostics; | using MQTTnet.Diagnostics; | ||||
using MQTTnet.Exceptions; | using MQTTnet.Exceptions; | ||||
using MQTTnet.Internal; | using MQTTnet.Internal; | ||||
@@ -14,6 +15,7 @@ namespace MQTTnet.Server | |||||
{ | { | ||||
public sealed class MqttClientSession : IDisposable | public sealed class MqttClientSession : IDisposable | ||||
{ | { | ||||
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider(); | |||||
private readonly IMqttServerOptions _options; | private readonly IMqttServerOptions _options; | ||||
private readonly IMqttNetLogger _logger; | private readonly IMqttNetLogger _logger; | ||||
private readonly MqttRetainedMessagesManager _retainedMessagesManager; | private readonly MqttRetainedMessagesManager _retainedMessagesManager; | ||||
@@ -129,6 +131,11 @@ namespace MQTTnet.Server | |||||
var publishPacket = applicationMessage.ToPublishPacket(); | var publishPacket = applicationMessage.ToPublishPacket(); | ||||
publishPacket.QualityOfServiceLevel = result.QualityOfServiceLevel; | publishPacket.QualityOfServiceLevel = result.QualityOfServiceLevel; | ||||
if (publishPacket.QualityOfServiceLevel > 0) | |||||
{ | |||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(); | |||||
} | |||||
PendingMessagesQueue.Enqueue(publishPacket); | PendingMessagesQueue.Enqueue(publishPacket); | ||||
} | } | ||||
@@ -205,7 +212,7 @@ namespace MQTTnet.Server | |||||
if (packet is MqttPingReqPacket) | if (packet is MqttPingReqPacket) | ||||
{ | { | ||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPingRespPacket()); | |||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttPingRespPacket() }); | |||||
} | } | ||||
if (packet is MqttPubRelPacket pubRelPacket) | if (packet is MqttPubRelPacket pubRelPacket) | ||||
@@ -215,7 +222,12 @@ namespace MQTTnet.Server | |||||
if (packet is MqttPubRecPacket pubRecPacket) | if (packet is MqttPubRecPacket pubRecPacket) | ||||
{ | { | ||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, pubRecPacket.CreateResponse<MqttPubRelPacket>()); | |||||
var responsePacket = new MqttPubRelPacket | |||||
{ | |||||
PacketIdentifier = pubRecPacket.PacketIdentifier | |||||
}; | |||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { responsePacket }); | |||||
} | } | ||||
if (packet is MqttPubAckPacket || packet is MqttPubCompPacket) | if (packet is MqttPubAckPacket || packet is MqttPubCompPacket) | ||||
@@ -246,11 +258,11 @@ namespace MQTTnet.Server | |||||
private async Task HandleIncomingSubscribePacketAsync(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken) | private async Task HandleIncomingSubscribePacketAsync(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken) | ||||
{ | { | ||||
var subscribeResult = await SubscriptionsManager.SubscribeAsync(subscribePacket).ConfigureAwait(false); | var subscribeResult = await SubscriptionsManager.SubscribeAsync(subscribePacket).ConfigureAwait(false); | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, subscribeResult.ResponsePacket).ConfigureAwait(false); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { subscribeResult.ResponsePacket }).ConfigureAwait(false); | |||||
if (subscribeResult.CloseConnection) | if (subscribeResult.CloseConnection) | ||||
{ | { | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttDisconnectPacket() }).ConfigureAwait(false); | |||||
await StopAsync().ConfigureAwait(false); | await StopAsync().ConfigureAwait(false); | ||||
} | } | ||||
@@ -260,7 +272,7 @@ namespace MQTTnet.Server | |||||
private async Task HandleIncomingUnsubscribePacketAsync(IMqttChannelAdapter adapter, MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken) | private async Task HandleIncomingUnsubscribePacketAsync(IMqttChannelAdapter adapter, MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken) | ||||
{ | { | ||||
var unsubscribeResult = await SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false); | var unsubscribeResult = await SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false); | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, unsubscribeResult); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { unsubscribeResult }); | |||||
} | } | ||||
private async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<TopicFilter> topicFilters) | private async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<TopicFilter> topicFilters) | ||||
@@ -302,7 +314,7 @@ namespace MQTTnet.Server | |||||
await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false); | await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false); | ||||
var response = new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }; | var response = new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }; | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { response }).ConfigureAwait(false); | |||||
} | } | ||||
private async Task HandleIncomingPublishPacketWithQoS2(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken) | private async Task HandleIncomingPublishPacketWithQoS2(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken) | ||||
@@ -311,13 +323,13 @@ namespace MQTTnet.Server | |||||
await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false); | await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false); | ||||
var response = new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }; | var response = new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }; | ||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false); | |||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { response }).ConfigureAwait(false); | |||||
} | } | ||||
private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) | private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) | ||||
{ | { | ||||
var response = new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }; | var response = new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }; | ||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response); | |||||
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { response }); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -53,10 +53,10 @@ namespace MQTTnet.Server | |||||
var connectReturnCode = ValidateConnection(connectPacket); | var connectReturnCode = ValidateConnection(connectPacket); | ||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted) | if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted) | ||||
{ | { | ||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket | |||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket | |||||
{ | { | ||||
ConnectReturnCode = connectReturnCode | ConnectReturnCode = connectReturnCode | ||||
}).ConfigureAwait(false); | |||||
}}).ConfigureAwait(false); | |||||
return; | return; | ||||
} | } | ||||
@@ -64,11 +64,11 @@ namespace MQTTnet.Server | |||||
var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false); | var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false); | ||||
clientSession = result.Session; | clientSession = result.Session; | ||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket | |||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket | |||||
{ | { | ||||
ConnectReturnCode = connectReturnCode, | ConnectReturnCode = connectReturnCode, | ||||
IsSessionPresent = result.IsExistingSession | IsSessionPresent = result.IsExistingSession | ||||
}).ConfigureAwait(false); | |||||
}}).ConfigureAwait(false); | |||||
ClientConnectedCallback?.Invoke(new ConnectedMqttClient | ClientConnectedCallback?.Invoke(new ConnectedMqttClient | ||||
{ | { | ||||
@@ -14,7 +14,7 @@ namespace MQTTnet.Server | |||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>(); | private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>(); | ||||
private readonly IMqttServerOptions _options; | private readonly IMqttServerOptions _options; | ||||
private readonly string _clientId; | private readonly string _clientId; | ||||
public MqttClientSubscriptionsManager(IMqttServerOptions options, string clientId) | public MqttClientSubscriptionsManager(IMqttServerOptions options, string clientId) | ||||
{ | { | ||||
_options = options ?? throw new ArgumentNullException(nameof(options)); | _options = options ?? throw new ArgumentNullException(nameof(options)); | ||||
@@ -30,7 +30,11 @@ namespace MQTTnet.Server | |||||
var result = new MqttClientSubscribeResult | var result = new MqttClientSubscribeResult | ||||
{ | { | ||||
ResponsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>(), | |||||
ResponsePacket = new MqttSubAckPacket | |||||
{ | |||||
PacketIdentifier = subscribePacket.PacketIdentifier | |||||
}, | |||||
CloseConnection = false | CloseConnection = false | ||||
}; | }; | ||||
@@ -87,7 +91,10 @@ namespace MQTTnet.Server | |||||
_semaphore.Release(); | _semaphore.Release(); | ||||
} | } | ||||
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>(); | |||||
return new MqttUnsubAckPacket | |||||
{ | |||||
PacketIdentifier = unsubscribePacket.PacketIdentifier | |||||
}; | |||||
} | } | ||||
public async Task<CheckSubscriptionsResult> CheckSubscriptionsAsync(MqttApplicationMessage applicationMessage) | public async Task<CheckSubscriptionsResult> CheckSubscriptionsAsync(MqttApplicationMessage applicationMessage) | ||||
@@ -12,7 +12,7 @@ | |||||
<DefaultLanguage>en-US</DefaultLanguage> | <DefaultLanguage>en-US</DefaultLanguage> | ||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> | <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> | ||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.16299.0</TargetPlatformVersion> | <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.16299.0</TargetPlatformVersion> | ||||
<TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion> | |||||
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion> | |||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> | <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> | ||||
<FileAlignment>512</FileAlignment> | <FileAlignment>512</FileAlignment> | ||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | ||||
@@ -148,7 +148,7 @@ | |||||
</PivotItem> | </PivotItem> | ||||
<PivotItem Header="Log"> | <PivotItem Header="Log"> | ||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> | <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> | ||||
<Button Click="Clear" Width="120">Clear</Button> | |||||
<Button Click="ClearLog" Width="120">Clear</Button> | |||||
</StackPanel> | </StackPanel> | ||||
</PivotItem> | </PivotItem> | ||||
</Pivot> | </Pivot> | ||||
@@ -42,7 +42,8 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
foreach (var traceMessage in _traceMessages) | foreach (var traceMessage in _traceMessages) | ||||
{ | { | ||||
logText.AppendFormat( | logText.AppendFormat( | ||||
"[{0:yyyy-MM-dd HH:mm:ss.fff}] [{1}] [{2}] [{3}] [{4}]{5}", traceMessage.Timestamp, | |||||
"[{0:yyyy-MM-dd HH:mm:ss.fff}] [{1}] [{2}] [{3}] [{4}]{5}", | |||||
traceMessage.Timestamp, | |||||
traceMessage.Level, | traceMessage.Level, | ||||
traceMessage.Source, | traceMessage.Source, | ||||
traceMessage.ThreadId, | traceMessage.ThreadId, | ||||
@@ -197,8 +198,13 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
} | } | ||||
} | } | ||||
private void Clear(object sender, RoutedEventArgs e) | |||||
private void ClearLog(object sender, RoutedEventArgs e) | |||||
{ | { | ||||
while (_traceMessages.Count > 0) | |||||
{ | |||||
_traceMessages.TryDequeue(out _); | |||||
} | |||||
Trace.Text = string.Empty; | Trace.Text = string.Empty; | ||||
} | } | ||||