mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 17:23:04 +08:00
Compare commits
18
Commits
v4.2.0.706
...
v4.2.1.781
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ad86928f1 | ||
|
|
d8e5936d45 | ||
|
|
caa717544b | ||
|
|
66b77f8d6d | ||
|
|
ab5264c8ca | ||
|
|
db12309bae | ||
|
|
2953e0dcc5 | ||
|
|
1b18ac9fe3 | ||
|
|
2cdad6622a | ||
|
|
7601d11a7a | ||
|
|
0bb4c6ef09 | ||
|
|
449624fd72 | ||
|
|
99f4f46062 | ||
|
|
b08d11c069 | ||
|
|
ac41e838ec | ||
|
|
836ddf3bbc | ||
|
|
0e815c626c | ||
|
|
4b7d035e9e |
@@ -1,20 +1,13 @@
|
||||
* [Core] Added net7.0 builds (#1635, thanks to @YAJeff).
|
||||
* [Core] Added support for payload segments (#1585, thanks to @xljiulang)
|
||||
* [Client] Added support for passing MQTT v5 options (User properties etc.) for disconnects.
|
||||
* [Client] An internal exception is no longer caught silently when calling _DisconnectAsync_ to indicate that the disconnect is not clean (BREAKING CHANGE).
|
||||
* [Client] MQTTv5 features are now checked and an exception is thrown if they are used when using protocol version 3.1.1 and lower. These checks can be disabled in client options. (BREAKING CHANGE!).
|
||||
* [Client] Added support for disabling packet fragmentation (required for i.e. AWS, #1690, thanks to @logicaloud).
|
||||
* [Client] Added new overloads for options builder.
|
||||
* [Client] Renamed _InspectPackage_ to _InspectPacketAsync_ (BREAKING CHANGE).
|
||||
* [ManagedClient] Exposed added and removed topics in _ManagedProcessFailedEventArgs_ (#1678, thanks to @scottbrogden-iheartmedia).
|
||||
* [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event.
|
||||
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
||||
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
||||
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
||||
* [Server] Improved performance for retained message handling when subscribing using "DoNotSendOnSubscribe" or "SendAtSubscribeIfNewSubscriptionOnly" (#1661, thanks to @Int32Overflow).
|
||||
* [Server] Added support for changing the used TLS certificate while the server is running (#1652, thanks to @YAJeff). The certificate provider will now be invoked for every new connection!
|
||||
* [Server] Added a new API method which allows reading a single retained message without the need to processing the entire set of retained messages (#1659).
|
||||
* [Server] Added a new event (InterceptingClientEnqueueAsync) which allows intercepting when an application message is enqueued for a client (#1648).
|
||||
* [Server] Fixed race condition when handling connections which leads to stopped message transfers (#1677, thanks to @RazvanEmilR).
|
||||
* [Server] Fixed not delivered packets to other sessions if the _InterceptingClientEnqueueEvent_ is used and accepting the packet is denied (#1685, thanks to @RossHNPC).
|
||||
* [Server] Added missing user properties to subscribe and unsubscribe interceptor (#1702).
|
||||
* [Core] Add validation of maximum string lengths (#1718).
|
||||
* [Core] Added .NET 4.8 builds (#1729).
|
||||
* [Core] Exposed more details of DISCONNECT packet in log (#1729).
|
||||
* [Core] Fixed _ArgumentNullException_ in _ConvertPayloadToString_ (#1745, thanks to @ramonsmits).
|
||||
* [Client] Added overloads for setting packet payload and will payload (#1720).
|
||||
* [Client] The proper connect result is now exposed in the _Disconnected_ event when authentication fails (#1139).
|
||||
* [Client] Exposed _UseDefaultCredentials_ and more for Web Socket options and proxy options (#1734, thanks to @impworks).
|
||||
* [Client] Exposed more TLS options (#1729).
|
||||
* [Client] Fixed wrong return code conversion (#1729).
|
||||
* [Client] Added an option to avoid throwing an exception when the server returned a proper non success (but valid) response (#1681).
|
||||
* [Server] Improved performance by changing internal locking strategy for subscriptions (#1716, thanks to @zeheng).
|
||||
* [Server] Fixed exceptions when clients are connecting and disconnecting very fast while accessing the client status for connection validation (#1742).
|
||||
* [Server] Exposed more properties in _ClientConnectedEventArgs_ (#1738).
|
||||
|
||||
@@ -3,7 +3,7 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
VERSION: "4.2.0.${{github.run_number}}"
|
||||
VERSION: "4.2.1.${{github.run_number}}"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -30,17 +30,17 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
}
|
||||
@@ -83,11 +83,11 @@ namespace MQTTnet.Benchmarks
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574589);
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
writer.Reset(0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.ManagedClient</AssemblyName>
|
||||
|
||||
@@ -12,22 +12,37 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (mqttClient == null)
|
||||
{
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
}
|
||||
|
||||
|
||||
return new ManagedMqttClient(mqttClient, factory.DefaultLogger);
|
||||
}
|
||||
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||
}
|
||||
|
||||
public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory _)
|
||||
{
|
||||
return new ManagedMqttClientOptionsBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.Rpc</AssemblyName>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.WebSocket4Net</AssemblyName>
|
||||
|
||||
@@ -22,6 +22,38 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
||||
[TestClass]
|
||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Expose_Custom_Connection_Error()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||
|
||||
MqttClientDisconnectedEventArgs disconnectedArgs = null;
|
||||
managedClient.DisconnectedAsync += args =>
|
||||
{
|
||||
disconnectedArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var clientOptions = testEnvironment.Factory.CreateManagedMqttClientOptionsBuilder().WithClientOptions(testEnvironment.CreateDefaultClientOptions()).Build();
|
||||
await managedClient.StartAsync(clientOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(disconnectedArgs);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, disconnectedArgs.ConnectResult.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -10,6 +11,7 @@ using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -127,6 +129,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
||||
}
|
||||
@@ -154,7 +158,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.UserProperties);
|
||||
Assert.AreEqual(1, eventArgs.UserProperties.Count);
|
||||
@@ -162,5 +166,35 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Non_Success()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ResponseUserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Property", "Value")
|
||||
};
|
||||
|
||||
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().WithoutThrowOnNonSuccessfulConnectResponse().Build());
|
||||
|
||||
Assert.IsNotNull(response);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
|
||||
Assert.AreEqual(response.UserProperties[0].Name, "Property");
|
||||
Assert.AreEqual(response.UserProperties[0].Value, "Value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V310()
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
@@ -47,23 +48,32 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.AreEqual(1234U, reader.ReadVariableByteInteger());
|
||||
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Throw_If_String_Too_Long()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
|
||||
writer.WriteString(string.Empty.PadLeft(65536));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Write_And_Read_Multiple_Times()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinaryData(new byte[1234]);
|
||||
writer.WriteBinary(new byte[1234]);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinaryData(new byte[48]);
|
||||
writer.WriteBinary(new byte[48]);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(new byte[3]);
|
||||
writer.WriteBinary(new byte[3]);
|
||||
|
||||
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
||||
{
|
||||
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
|
||||
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort).WithClientId(TestContext.TestName + "_" + Guid.NewGuid());
|
||||
}
|
||||
|
||||
public ILowLevelMqttClient CreateLowLevelClient()
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace MQTTnet.Tests
|
||||
writer.WriteString("1234567890");
|
||||
Assert.AreEqual(10 + 2, writer.Length);
|
||||
|
||||
writer.WriteBinaryData(new byte[300]);
|
||||
writer.WriteBinary(new byte[300]);
|
||||
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
||||
|
||||
writer.WriteBinaryData(new byte[5000]);
|
||||
writer.WriteBinary(new byte[5000]);
|
||||
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Client.Internal
|
||||
{
|
||||
public sealed class MqttClientEvents
|
||||
{
|
||||
public AsyncEvent<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceivedEvent { get; } = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||
public AsyncEvent<MqttClientConnectedEventArgs> ConnectedEvent { get; } = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
public AsyncEvent<MqttClientConnectingEventArgs> ConnectingEvent { get; } = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||
public AsyncEvent<MqttClientDisconnectedEventArgs> DisconnectedEvent { get; } = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
public AsyncEvent<InspectMqttPacketEventArgs> InspectPacketEvent { get; } = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Internal
|
||||
{
|
||||
public static class MqttClientResultFactory
|
||||
{
|
||||
public static readonly MqttClientConnectResultFactory ConnectResult = new MqttClientConnectResultFactory();
|
||||
public static readonly MqttClientPublishResultFactory PublishResult = new MqttClientPublishResultFactory();
|
||||
public static readonly MqttClientSubscribeResultFactory SubscribeResult = new MqttClientSubscribeResultFactory();
|
||||
public static readonly MqttClientUnsubscribeResultFactory UnsubscribeResult = new MqttClientUnsubscribeResultFactory();
|
||||
}
|
||||
}
|
||||
+194
-156
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Internal;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
@@ -20,17 +21,9 @@ namespace MQTTnet.Client
|
||||
public sealed class MqttClient : Disposable, IMqttClient
|
||||
{
|
||||
readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||
|
||||
readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory();
|
||||
readonly MqttClientSubscribeResultFactory _clientSubscribeResultFactory = new MqttClientSubscribeResultFactory();
|
||||
readonly MqttClientUnsubscribeResultFactory _clientUnsubscribeResultFactory = new MqttClientUnsubscribeResultFactory();
|
||||
|
||||
readonly AsyncEvent<MqttClientConnectedEventArgs> _connectedEvent = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
readonly AsyncEvent<MqttClientConnectingEventArgs> _connectingEvent = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||
readonly AsyncEvent<MqttClientDisconnectedEventArgs> _disconnectedEvent = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
readonly object _disconnectLock = new object();
|
||||
readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||
readonly MqttClientEvents _events = new MqttClientEvents();
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||
@@ -65,32 +58,32 @@ namespace MQTTnet.Client
|
||||
|
||||
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
||||
{
|
||||
add => _applicationMessageReceivedEvent.AddHandler(value);
|
||||
remove => _applicationMessageReceivedEvent.RemoveHandler(value);
|
||||
add => _events.ApplicationMessageReceivedEvent.AddHandler(value);
|
||||
remove => _events.ApplicationMessageReceivedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
||||
{
|
||||
add => _connectedEvent.AddHandler(value);
|
||||
remove => _connectedEvent.RemoveHandler(value);
|
||||
add => _events.ConnectedEvent.AddHandler(value);
|
||||
remove => _events.ConnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync
|
||||
{
|
||||
add => _connectingEvent.AddHandler(value);
|
||||
remove => _connectingEvent.RemoveHandler(value);
|
||||
add => _events.ConnectingEvent.AddHandler(value);
|
||||
remove => _events.ConnectingEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
||||
{
|
||||
add => _disconnectedEvent.AddHandler(value);
|
||||
remove => _disconnectedEvent.RemoveHandler(value);
|
||||
add => _events.DisconnectedEvent.AddHandler(value);
|
||||
remove => _events.DisconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync
|
||||
{
|
||||
add => _inspectPacketEvent.AddHandler(value);
|
||||
remove => _inspectPacketEvent.RemoveHandler(value);
|
||||
add => _events.InspectPacketEvent.AddHandler(value);
|
||||
remove => _events.InspectPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
||||
@@ -114,9 +107,9 @@ namespace MQTTnet.Client
|
||||
{
|
||||
Options = options;
|
||||
|
||||
if (_connectingEvent.HasHandlers)
|
||||
if (_events.ConnectingEvent.HasHandlers)
|
||||
{
|
||||
await _connectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||
await _events.ConnectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
@@ -127,12 +120,12 @@ namespace MQTTnet.Client
|
||||
_mqttClientAlive = new CancellationTokenSource();
|
||||
var mqttClientAliveToken = _mqttClientAlive.Token;
|
||||
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_inspectPacketEvent, _rootLogger), _rootLogger);
|
||||
_adapter = adapter;
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_events.InspectPacketEvent, _rootLogger), _rootLogger);
|
||||
_adapter = adapter ?? throw new InvalidOperationException("The adapter factory did not provide an adapter.");
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
connectResult = await ConnectInternal(cancellationToken).ConfigureAwait(false);
|
||||
connectResult = await ConnectInternal(adapter, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,43 +133,50 @@ namespace MQTTnet.Client
|
||||
// CancellationToken.None or similar.
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
connectResult = await ConnectInternal(timeout.Token).ConfigureAwait(false);
|
||||
connectResult = await ConnectInternal(adapter, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
|
||||
{
|
||||
_logger.Warning("Connecting failed: {0}", connectResult.ResultCode);
|
||||
return connectResult;
|
||||
}
|
||||
|
||||
_lastPacketSentTimestamp = DateTime.UtcNow;
|
||||
|
||||
var keepAliveInterval = Options.KeepAlivePeriod;
|
||||
if (connectResult.ServerKeepAlive > 0)
|
||||
{
|
||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server.");
|
||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server");
|
||||
keepAliveInterval = TimeSpan.FromSeconds(connectResult.ServerKeepAlive);
|
||||
}
|
||||
|
||||
if (keepAliveInterval != TimeSpan.Zero)
|
||||
{
|
||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(mqttClientAliveToken), mqttClientAliveToken);
|
||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessages(mqttClientAliveToken), mqttClientAliveToken);
|
||||
}
|
||||
|
||||
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
|
||||
|
||||
_logger.Info("Connected.");
|
||||
_logger.Info("Connected");
|
||||
|
||||
if (_connectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
await _connectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
await OnConnected(connectResult).ConfigureAwait(false);
|
||||
|
||||
return connectResult;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is MqttConnectingFailedException connectingFailedException)
|
||||
{
|
||||
connectResult = connectingFailedException.Result;
|
||||
}
|
||||
|
||||
_disconnectReason = (int)MqttClientDisconnectOptionsReason.UnspecifiedError;
|
||||
|
||||
_logger.Error(exception, "Error while connecting with server.");
|
||||
_logger.Error(exception, "Error while connecting with server");
|
||||
|
||||
await DisconnectInternalAsync(null, exception, connectResult).ConfigureAwait(false);
|
||||
await DisconnectInternal(null, exception, connectResult).ConfigureAwait(false);
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -219,19 +219,19 @@ namespace MQTTnet.Client
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
await SendAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||
await Send(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
|
||||
await DisconnectCore(null, null, null, clientWasConnected).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,13 +239,13 @@ namespace MQTTnet.Client
|
||||
{
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
||||
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
||||
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,11 +274,11 @@ namespace MQTTnet.Client
|
||||
}
|
||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||
{
|
||||
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
|
||||
return PublishAtLeastOnce(publishPacket, cancellationToken);
|
||||
}
|
||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||
{
|
||||
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
|
||||
return PublishExactlyOnce(publishPacket, cancellationToken);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -306,7 +306,7 @@ namespace MQTTnet.Client
|
||||
UserProperties = data.UserProperties
|
||||
};
|
||||
|
||||
return SendAsync(authPacket, cancellationToken);
|
||||
return Send(authPacket, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
@@ -335,17 +335,17 @@ namespace MQTTnet.Client
|
||||
MqttSubAckPacket subAckPacket;
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return _clientSubscribeResultFactory.Create(subscribePacket, subAckPacket);
|
||||
return MqttClientResultFactory.SubscribeResult.Create(subscribePacket, subAckPacket);
|
||||
}
|
||||
|
||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
@@ -374,17 +374,17 @@ namespace MQTTnet.Client
|
||||
MqttUnsubAckPacket unsubAckPacket;
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return _clientUnsubscribeResultFactory.Create(unsubscribePacket, unsubAckPacket);
|
||||
return MqttClientResultFactory.UnsubscribeResult.Create(unsubscribePacket, unsubAckPacket);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -408,7 +408,7 @@ namespace MQTTnet.Client
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubAckPacket = MqttPacketFactories.PubAck.Create(eventArgs);
|
||||
return SendAsync(pubAckPacket, cancellationToken);
|
||||
return Send(pubAckPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
@@ -416,7 +416,7 @@ namespace MQTTnet.Client
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubRecPacket = MqttPacketFactories.PubRec.Create(eventArgs);
|
||||
return SendAsync(pubRecPacket, cancellationToken);
|
||||
return Send(pubRecPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -427,27 +427,43 @@ namespace MQTTnet.Client
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
async Task<MqttClientConnectResult> AuthenticateAsync(MqttClientOptions options, CancellationToken cancellationToken)
|
||||
async Task<MqttClientConnectResult> Authenticate(IMqttChannelAdapter channelAdapter, MqttClientOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
MqttClientConnectResult result;
|
||||
|
||||
try
|
||||
{
|
||||
var connectPacket = MqttPacketFactories.Connect.Create(options);
|
||||
await Send(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
var receivedPacket = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var clientConnectResultFactory = new MqttClientConnectResultFactory();
|
||||
result = clientConnectResultFactory.Create(connAckPacket, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
if (receivedPacket is MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
result = MqttClientResultFactory.ConnectResult.Create(connAckPacket, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Extended authentication handler is not yet supported");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
|
||||
}
|
||||
|
||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||
// This is no feature. It is basically a backward compatibility option and should be removed in the future.
|
||||
// The client should not throw any exception if the transport layer connection was successful and the server
|
||||
// did send a proper ACK packet with a non success response.
|
||||
if (options.ThrowOnNonSuccessfulConnectResponse)
|
||||
{
|
||||
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
||||
_logger.Warning(
|
||||
"Client will now throw an _MqttConnectingFailedException_. This is obsolete and will be removed in the future. Consider setting _ThrowOnNonSuccessfulResponseFromServer=False_ in client options.");
|
||||
|
||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||
{
|
||||
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Verbose("Authenticated MQTT connection with server established.");
|
||||
@@ -482,7 +498,7 @@ namespace MQTTnet.Client
|
||||
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
|
||||
}
|
||||
|
||||
async Task<MqttClientConnectResult> ConnectInternal(CancellationToken cancellationToken)
|
||||
async Task<MqttClientConnectResult> ConnectInternal(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var backgroundCancellationToken = _mqttClientAlive.Token;
|
||||
|
||||
@@ -495,14 +511,18 @@ namespace MQTTnet.Client
|
||||
_publishPacketReceiverQueue?.Dispose();
|
||||
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
|
||||
|
||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
|
||||
var connectResult = await Authenticate(channelAdapter, Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
if (connectResult.ResultCode == MqttClientConnectResultCode.Success)
|
||||
{
|
||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||
_packetReceiverTask = Task.Run(() => ReceivePacketsLoop(backgroundCancellationToken), backgroundCancellationToken);
|
||||
}
|
||||
|
||||
return await AuthenticateAsync(Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
return connectResult;
|
||||
}
|
||||
}
|
||||
|
||||
async Task DisconnectCoreAsync(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
||||
async Task DisconnectCore(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
||||
{
|
||||
TryInitiateDisconnect();
|
||||
|
||||
@@ -557,17 +577,17 @@ namespace MQTTnet.Client
|
||||
|
||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||
// when trying to reconnect in that handler etc.
|
||||
Task.Run(() => _disconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||
Task.Run(() => _events.DisconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||
}
|
||||
}
|
||||
|
||||
Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||
Task DisconnectInternal(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||
{
|
||||
var clientWasConnected = IsConnected;
|
||||
|
||||
if (!DisconnectIsPendingOrFinished())
|
||||
{
|
||||
return DisconnectCoreAsync(sender, exception, connectResult, clientWasConnected);
|
||||
return DisconnectCore(sender, exception, connectResult, clientWasConnected);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
@@ -612,15 +632,26 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
|
||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
||||
await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
await _events.ApplicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
Task OnConnected(MqttClientConnectResult connectResult)
|
||||
{
|
||||
if (_events.ConnectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
return _events.ConnectedEvent.InvokeAsync(eventArgs);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
|
||||
{
|
||||
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
|
||||
@@ -641,7 +672,7 @@ namespace MQTTnet.Client
|
||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||
_packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket));
|
||||
|
||||
return DisconnectInternalAsync(_packetReceiverTask, null, null);
|
||||
return DisconnectInternal(_packetReceiverTask, null, null);
|
||||
}
|
||||
|
||||
async Task ProcessReceivedPublishPackets(CancellationToken cancellationToken)
|
||||
@@ -657,7 +688,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
|
||||
var publishPacket = publishPacketDequeueResult.Item;
|
||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
||||
var eventArgs = await HandleReceivedApplicationMessage(publishPacket).ConfigureAwait(false);
|
||||
|
||||
if (eventArgs.AutoAcknowledge)
|
||||
{
|
||||
@@ -681,7 +712,7 @@ namespace MQTTnet.Client
|
||||
// The packet is unknown. Probably due to a restart of the client.
|
||||
// So wen send this to the server to trigger a full resend of the message.
|
||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
||||
return SendAsync(pubRelPacket, cancellationToken);
|
||||
return Send(pubRelPacket, cancellationToken);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
@@ -690,39 +721,115 @@ namespace MQTTnet.Client
|
||||
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
return SendAsync(pubCompPacket, cancellationToken);
|
||||
return Send(pubCompPacket, cancellationToken);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
async Task<MqttClientPublishResult> PublishAtLeastOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
return _clientPublishResultFactory.Create(pubAckPacket);
|
||||
var pubAckPacket = await Request<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
return MqttClientResultFactory.PublishResult.Create(pubAckPacket);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _clientPublishResultFactory.Create(null);
|
||||
return MqttClientResultFactory.PublishResult.Create(null);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
async Task<MqttClientPublishResult> PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubRecPacket = await Request<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
|
||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubCompPacket = await Request<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _clientPublishResultFactory.Create(pubRecPacket, pubCompPacket);
|
||||
return MqttClientResultFactory.PublishResult.Create(pubRecPacket, pubCompPacket);
|
||||
}
|
||||
|
||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||
async Task<MqttPacket> Receive(CancellationToken cancellationToken)
|
||||
{
|
||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||
|
||||
MqttPacket packet;
|
||||
if (packetTask.IsCompleted)
|
||||
{
|
||||
packet = packetTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = await packetTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
async Task ReceivePacketsLoop(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var packet = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet == null)
|
||||
{
|
||||
await DisconnectInternal(_packetReceiverTask, null, null).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await TryProcessReceivedPacket(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (_cleanDisconnectInitiated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is AggregateException aggregateException)
|
||||
{
|
||||
exception = aggregateException.GetBaseException();
|
||||
}
|
||||
|
||||
if (exception is OperationCanceledException)
|
||||
{
|
||||
}
|
||||
else if (exception is MqttCommunicationException)
|
||||
{
|
||||
_logger.Warning(exception, "Communication error while receiving packets.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error(exception, "Error while receiving packets.");
|
||||
}
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Verbose("Stopped receiving packets.");
|
||||
}
|
||||
}
|
||||
|
||||
async Task<TResponsePacket> Request<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -736,7 +843,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
await SendAsync(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -760,7 +867,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
Task SendAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
Task Send(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -823,7 +930,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryProcessReceivedPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
async Task TryProcessReceivedPacket(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -884,80 +991,11 @@ namespace MQTTnet.Client
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
MqttPacket packet;
|
||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||
|
||||
if (packetTask.IsCompleted)
|
||||
{
|
||||
packet = packetTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = await packetTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet == null)
|
||||
{
|
||||
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await TryProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (_cleanDisconnectInitiated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is AggregateException aggregateException)
|
||||
{
|
||||
exception = aggregateException.GetBaseException();
|
||||
}
|
||||
|
||||
if (exception is OperationCanceledException)
|
||||
{
|
||||
}
|
||||
else if (exception is MqttCommunicationException)
|
||||
{
|
||||
_logger.Warning(exception, "Communication error while receiving packets.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error(exception, "Error while receiving packets.");
|
||||
}
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Verbose("Stopped receiving packets.");
|
||||
}
|
||||
}
|
||||
|
||||
async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
||||
async Task TrySendKeepAliveMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1006,7 +1044,7 @@ namespace MQTTnet.Client
|
||||
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
|
||||
}
|
||||
|
||||
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
||||
await DisconnectInternal(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -12,6 +12,14 @@ namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||
/// be set to _false_.
|
||||
/// </summary>
|
||||
public bool AllowPacketFragmentation { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authentication data.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
@@ -24,14 +32,6 @@ namespace MQTTnet.Client
|
||||
/// </summary>
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||
/// be set to _false_.
|
||||
/// </summary>
|
||||
public bool AllowPacketFragmentation { get; set; } = true;
|
||||
|
||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -100,6 +100,11 @@ namespace MQTTnet.Client
|
||||
/// </summary>
|
||||
public uint SessionExpiryInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an exception should be thrown when the server has sent a non success ACK packet.
|
||||
/// </summary>
|
||||
public bool ThrowOnNonSuccessfulConnectResponse { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timeout which will be applied at socket level and internal operations.
|
||||
/// The default value is the same as for sockets in .NET in general.
|
||||
@@ -222,4 +227,4 @@ namespace MQTTnet.Client
|
||||
/// </summary>
|
||||
public int WriterBufferSizeMax { get; set; } = 65535;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -78,7 +78,17 @@ namespace MQTTnet.Client
|
||||
|
||||
return _options;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The client will not throw an exception when the MQTT server responses with a non success ACK packet.
|
||||
/// This will become the default behavior in future versions of the library.
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithoutThrowOnNonSuccessfulConnectResponse()
|
||||
{
|
||||
_options.ThrowOnNonSuccessfulConnectResponse = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
||||
{
|
||||
_options.AuthenticationMethod = method;
|
||||
@@ -421,6 +431,18 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(ArraySegment<byte> willPayload)
|
||||
{
|
||||
if (willPayload.Count == 0)
|
||||
{
|
||||
_options.WillPayload = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
_options.WillPayload = willPayload.ToArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(string willPayload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(willPayload))
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
@@ -30,11 +31,21 @@ namespace MQTTnet.Client
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
public List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
|
||||
public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
||||
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
|
||||
public CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
||||
|
||||
public EncryptionPolicy EncryptionPolicy { get; set; } = EncryptionPolicy.RequireEncryption;
|
||||
|
||||
public bool AllowRenegotiation { get; set; } = true;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target host.
|
||||
/// If the value is null or empty the same host as the TCP socket host will be used.
|
||||
/// </summary>
|
||||
public string TargetHost { get; set; }
|
||||
|
||||
#if NET48 || NETCOREAPP3_1_OR_GREATER
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
#else
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
||||
{
|
||||
public CookieContainer CookieContainer { get; set; }
|
||||
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
||||
|
||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||
@@ -18,12 +22,34 @@ namespace MQTTnet.Client
|
||||
public ICollection<string> SubProtocols { get; set; } = new List<string> { "mqtt" };
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
|
||||
public string Uri { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Uri;
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
#if !WINDOWS_UWP
|
||||
/// <summary>
|
||||
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAliveInterval { get; set; } = WebSocket.DefaultKeepAliveInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the default (system) credentials should be used when connecting via Web Socket connection.
|
||||
/// This is not related to the credentials which are used for the MQTT protocol.
|
||||
/// </summary>
|
||||
public bool UseDefaultCredentials { get; set; }
|
||||
#else
|
||||
/// <summary>
|
||||
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAliveInterval { get; set; } = TimeSpan.FromSeconds(30);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public bool BypassOnLocal { get; set; }
|
||||
|
||||
public bool UseDefaultCredentials { get; set; }
|
||||
|
||||
public string[] BypassList { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Client
|
||||
/// Gets or sets the reason code.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
/// </summary>
|
||||
public MqttClientPublishReasonCode ReasonCode { get; } = MqttClientPublishReasonCode.Success;
|
||||
public MqttClientPublishReasonCode ReasonCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string.
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Diagnostics
|
||||
return "net461";
|
||||
#elif NET472
|
||||
return "net472";
|
||||
#elif NET48
|
||||
return "net48";
|
||||
#elif NETSTANDARD1_3
|
||||
return "netstandard1.3";
|
||||
#elif NETSTANDARD2_0
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace MQTTnet.Formatter
|
||||
/// </summary>
|
||||
public sealed class MqttBufferWriter
|
||||
{
|
||||
public const uint VariableByteIntegerMaxValue = 268435455;
|
||||
const uint VariableByteIntegerMaxValue = 268435455;
|
||||
const int EncodedStringMaxLength = 65535;
|
||||
|
||||
readonly int _maxBufferSize;
|
||||
|
||||
@@ -112,25 +113,7 @@ namespace MQTTnet.Formatter
|
||||
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void WriteBinaryData(byte[] value)
|
||||
public void WriteBinary(byte[] value)
|
||||
{
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
@@ -155,6 +138,24 @@ namespace MQTTnet.Formatter
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void WriteByte(byte @byte)
|
||||
{
|
||||
EnsureAdditionalCapacity(1);
|
||||
@@ -186,6 +187,13 @@ namespace MQTTnet.Formatter
|
||||
|
||||
var writtenBytes = Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _position + 2);
|
||||
|
||||
// From RFC: 1.5.4 UTF-8 Encoded String
|
||||
// Unless stated otherwise all UTF-8 encoded strings can have any length in the range 0 to 65,535 bytes.
|
||||
if (writtenBytes > EncodedStringMaxLength)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"The maximum string length is 65535. The current string has a length of {writtenBytes}.");
|
||||
}
|
||||
|
||||
_buffer[_position] = (byte)(writtenBytes >> 8);
|
||||
_buffer[_position + 1] = (byte)writtenBytes;
|
||||
|
||||
@@ -273,8 +281,7 @@ namespace MQTTnet.Formatter
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
// Array.Resize will create a new array and copy the existing
|
||||
// data to the new one.
|
||||
// Array.Resize will create a new array and copy the existing data to the new one.
|
||||
Array.Resize(ref _buffer, newBufferLength);
|
||||
}
|
||||
|
||||
@@ -285,6 +292,8 @@ namespace MQTTnet.Formatter
|
||||
|
||||
if (_position > Length)
|
||||
{
|
||||
// Also extend the position because we reached the end of the
|
||||
// pre allocated buffer.
|
||||
Length = _position;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,54 +2,12 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public static class MqttConnectReasonCodeConverter
|
||||
{
|
||||
public static MqttConnectReasonCode ToConnectReasonCode(MqttConnectReturnCode returnCode)
|
||||
{
|
||||
switch (returnCode)
|
||||
{
|
||||
case MqttConnectReturnCode.ConnectionAccepted:
|
||||
{
|
||||
return MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
||||
{
|
||||
return MqttConnectReasonCode.UnsupportedProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
||||
{
|
||||
return MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
||||
{
|
||||
return MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
||||
{
|
||||
return MqttConnectReasonCode.ServerUnavailable;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
||||
{
|
||||
return MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
||||
{
|
||||
switch (reasonCode)
|
||||
@@ -59,11 +17,13 @@ namespace MQTTnet.Formatter
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.Banned:
|
||||
case MqttConnectReasonCode.NotAuthorized:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.BadAuthenticationMethod:
|
||||
case MqttConnectReasonCode.BadUserNameOrPassword:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||
@@ -79,6 +39,7 @@ namespace MQTTnet.Formatter
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.UseAnotherServer:
|
||||
case MqttConnectReasonCode.ServerUnavailable:
|
||||
case MqttConnectReasonCode.ServerBusy:
|
||||
case MqttConnectReasonCode.ServerMoved:
|
||||
@@ -87,9 +48,7 @@ namespace MQTTnet.Formatter
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,14 @@ namespace MQTTnet.Formatter
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_bufferWriter.Cleanup();
|
||||
}
|
||||
|
||||
public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Decode(receivedMqttPacket);
|
||||
}
|
||||
|
||||
@@ -50,11 +54,6 @@ namespace MQTTnet.Formatter
|
||||
return _formatter.Encode(packet);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_bufferWriter.Cleanup();
|
||||
}
|
||||
|
||||
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, MqttBufferWriter bufferWriter)
|
||||
{
|
||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||
@@ -98,7 +97,7 @@ namespace MQTTnet.Formatter
|
||||
// Remove the mosquitto try_private flag (MQTT 3.1.1 Bridge).
|
||||
// This flag is accepted but not yet used.
|
||||
protocolLevel &= 0x7F;
|
||||
|
||||
|
||||
if (protocolName == "MQTT")
|
||||
{
|
||||
if (protocolLevel == 5)
|
||||
|
||||
@@ -459,7 +459,7 @@ namespace MQTTnet.Formatter.V3
|
||||
if (packet.WillFlag)
|
||||
{
|
||||
bufferWriter.WriteString(packet.WillTopic);
|
||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -469,7 +469,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
bufferWriter.WriteBinaryData(packet.Password);
|
||||
bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
@@ -521,7 +521,7 @@ namespace MQTTnet.Formatter.V3
|
||||
if (packet.WillFlag)
|
||||
{
|
||||
bufferWriter.WriteString(packet.WillTopic);
|
||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -531,7 +531,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
bufferWriter.WriteBinaryData(packet.Password);
|
||||
bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
|
||||
@@ -130,6 +130,9 @@ namespace MQTTnet.Formatter.V5
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce
|
||||
};
|
||||
|
||||
// Also set the return code of MQTT 3.1.1 for backward compatibility and debugging purposes.
|
||||
packet.ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(packet.ReasonCode);
|
||||
|
||||
var propertiesReader = new MqttV5PropertiesReader(_bufferReader);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace MQTTnet.Formatter.V5
|
||||
_propertiesWriter.Reset();
|
||||
|
||||
_bufferWriter.WriteString(packet.WillTopic);
|
||||
_bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
_bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -196,7 +196,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
_bufferWriter.WriteBinaryData(packet.Password);
|
||||
_bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
|
||||
_bufferWriter.WriteByte((byte)id);
|
||||
_bufferWriter.WriteBinaryData(value);
|
||||
_bufferWriter.WriteBinary(value);
|
||||
}
|
||||
|
||||
void WriteAsFourByteInteger(MqttPropertyId id, uint value)
|
||||
|
||||
@@ -100,6 +100,12 @@ namespace MQTTnet.Implementations
|
||||
|
||||
if (_tcpOptions.TlsOptions?.UseTls == true)
|
||||
{
|
||||
var targetHost = _tcpOptions.TlsOptions.TargetHost;
|
||||
if (string.IsNullOrEmpty(targetHost))
|
||||
{
|
||||
targetHost = _tcpOptions.Server;
|
||||
}
|
||||
|
||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||
try
|
||||
{
|
||||
@@ -111,14 +117,16 @@ namespace MQTTnet.Implementations
|
||||
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
||||
CertificateRevocationCheckMode =
|
||||
_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||
TargetHost = _tcpOptions.Server,
|
||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy
|
||||
TargetHost = targetHost,
|
||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
|
||||
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
|
||||
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
|
||||
};
|
||||
|
||||
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await sslStream.AuthenticateAsClientAsync(
|
||||
_tcpOptions.Server,
|
||||
targetHost,
|
||||
LoadCertificates(),
|
||||
_tcpOptions.TlsOptions.SslProtocol,
|
||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||
@@ -290,12 +298,12 @@ namespace MQTTnet.Implementations
|
||||
|
||||
X509CertificateCollection LoadCertificates()
|
||||
{
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (_tcpOptions.TlsOptions.Certificates == null)
|
||||
{
|
||||
return certificates;
|
||||
return null;
|
||||
}
|
||||
|
||||
var certificates = new X509CertificateCollection();
|
||||
foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
|
||||
{
|
||||
certificates.Add(certificate);
|
||||
|
||||
@@ -155,14 +155,26 @@ namespace MQTTnet.Implementations
|
||||
throw new NotSupportedException("Proxies are not supported when using 'netstandard 1.3'.");
|
||||
#else
|
||||
var proxyUri = new Uri(_options.ProxyOptions.Address);
|
||||
|
||||
WebProxy webProxy;
|
||||
|
||||
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
||||
{
|
||||
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||
webProxy = new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||
}
|
||||
else
|
||||
{
|
||||
webProxy = new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||
}
|
||||
|
||||
if (_options.ProxyOptions.UseDefaultCredentials)
|
||||
{
|
||||
// Only update the property if required because setting it to false will alter
|
||||
// the used credentials internally!
|
||||
webProxy.UseDefaultCredentials = true;
|
||||
}
|
||||
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||
return webProxy;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -207,6 +219,14 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
#if !WINDOWS_UWP
|
||||
clientWebSocket.Options.UseDefaultCredentials = _options.UseDefaultCredentials;
|
||||
#endif
|
||||
clientWebSocket.Options.KeepAliveInterval = _options.KeepAliveInterval;
|
||||
#endif
|
||||
clientWebSocket.Options.Credentials = _options.Credentials;
|
||||
|
||||
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
|
||||
if (certificateValidationHandler != null)
|
||||
{
|
||||
@@ -220,6 +240,8 @@ namespace MQTTnet.Implementations
|
||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
|
||||
#elif NET461
|
||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
|
||||
#elif NET48
|
||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net48'.");
|
||||
#else
|
||||
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<AssemblyName>MQTTnet</AssemblyName>
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
@@ -20,9 +20,9 @@ namespace MQTTnet
|
||||
string _contentType;
|
||||
byte[] _correlationData;
|
||||
uint _messageExpiryInterval;
|
||||
ArraySegment<byte> _payloadSegment;
|
||||
|
||||
MqttPayloadFormatIndicator _payloadFormatIndicator;
|
||||
ArraySegment<byte> _payloadSegment;
|
||||
MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||
string _responseTopic;
|
||||
bool _retain;
|
||||
@@ -89,13 +89,11 @@ namespace MQTTnet
|
||||
|
||||
public MqttApplicationMessageBuilder WithPayload(byte[] payload)
|
||||
{
|
||||
_payloadSegment = payload == null || payload.Length == 0
|
||||
? EmptyBuffer.ArraySegment
|
||||
: new ArraySegment<byte>(payload);
|
||||
_payloadSegment = payload == null || payload.Length == 0 ? EmptyBuffer.ArraySegment : new ArraySegment<byte>(payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttApplicationMessageBuilder WithPayloadSegment(ArraySegment<byte> payloadSegment)
|
||||
public MqttApplicationMessageBuilder WithPayload(ArraySegment<byte> payloadSegment)
|
||||
{
|
||||
_payloadSegment = payloadSegment;
|
||||
return this;
|
||||
@@ -123,9 +121,7 @@ namespace MQTTnet
|
||||
|
||||
public MqttApplicationMessageBuilder WithPayload(Stream payload)
|
||||
{
|
||||
return payload == null
|
||||
? WithPayload(default(byte[]))
|
||||
: WithPayload(payload, payload.Length - payload.Position);
|
||||
return payload == null ? WithPayload(default(byte[])) : WithPayload(payload, payload.Length - payload.Position);
|
||||
}
|
||||
|
||||
public MqttApplicationMessageBuilder WithPayload(Stream payload, long length)
|
||||
@@ -162,16 +158,6 @@ namespace MQTTnet
|
||||
return WithPayload(payloadBuffer);
|
||||
}
|
||||
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1
|
||||
public MqttApplicationMessageBuilder WithPayloadSegment(ReadOnlyMemory<byte> payloadSegment)
|
||||
{
|
||||
return MemoryMarshal.TryGetArray(payloadSegment, out var segment)
|
||||
? WithPayloadSegment(segment)
|
||||
: WithPayload(payloadSegment.ToArray());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Adds the payload format indicator to the message.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
@@ -182,6 +168,20 @@ namespace MQTTnet
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttApplicationMessageBuilder WithPayloadSegment(ArraySegment<byte> payloadSegment)
|
||||
{
|
||||
_payloadSegment = payloadSegment;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1
|
||||
public MqttApplicationMessageBuilder WithPayloadSegment(ReadOnlyMemory<byte> payloadSegment)
|
||||
{
|
||||
return MemoryMarshal.TryGetArray(payloadSegment, out var segment) ? WithPayloadSegment(segment) : WithPayload(payloadSegment.ToArray());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The quality of service level.
|
||||
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
@@ -16,6 +17,16 @@ namespace MQTTnet
|
||||
throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
if(applicationMessage.PayloadSegment == EmptyBuffer.ArraySegment)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (applicationMessage.PayloadSegment.Array == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var payloadSegment = applicationMessage.PayloadSegment;
|
||||
return Encoding.UTF8.GetString(payloadSegment.Array, payloadSegment.Offset, payloadSegment.Count);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Packets
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Disconnect: [ReasonCode={ReasonCode}]";
|
||||
return $"Disconnect: [ReasonCode={ReasonCode}] [ReasonString={ReasonString}] [ServerReference={ServerReference}] [SessionExpiryInterval={SessionExpiryInterval}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,26 +4,33 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class ClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public ClientConnectedEventArgs(string clientId, string userName, MqttProtocolVersion protocolVersion, string endpoint, IDictionary sessionItems)
|
||||
readonly MqttConnectPacket _connectPacket;
|
||||
|
||||
public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, string endpoint, IDictionary sessionItems)
|
||||
{
|
||||
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
||||
UserName = userName;
|
||||
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||
ProtocolVersion = protocolVersion;
|
||||
Endpoint = endpoint;
|
||||
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
|
||||
}
|
||||
|
||||
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
|
||||
|
||||
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client identifier of the connected client.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
public string ClientId { get; }
|
||||
public string ClientId => _connectPacket.ClientId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoint of the connected client.
|
||||
@@ -43,6 +50,12 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets the user name of the connected client.
|
||||
/// </summary>
|
||||
public string UserName { get; }
|
||||
public string UserName => _connectPacket.Username;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user properties sent by the client.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> UserProperties => _connectPacket?.UserProperties;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Server
|
||||
// See the MqttSubscription object for a detailed explanation.
|
||||
readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
|
||||
|
||||
readonly object _sessionsManagementLock = new object();
|
||||
readonly ReaderWriterLockSlim _sessionsManagementLock = new ReaderWriterLockSlim();
|
||||
readonly HashSet<MqttSession> _subscriberSessions = new HashSet<MqttSession>();
|
||||
|
||||
public MqttClientSessionsManager(
|
||||
@@ -58,7 +58,7 @@ namespace MQTTnet.Server
|
||||
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
||||
}
|
||||
|
||||
public async Task CloseAllConnectionsAsync()
|
||||
public async Task CloseAllConnections()
|
||||
{
|
||||
List<MqttClient> connections;
|
||||
lock (_clients)
|
||||
@@ -86,7 +86,8 @@ namespace MQTTnet.Server
|
||||
|
||||
MqttSession session;
|
||||
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_sessions.TryGetValue(clientId, out session);
|
||||
_sessions.Remove(clientId);
|
||||
@@ -96,6 +97,10 @@ namespace MQTTnet.Server
|
||||
_subscriberSessions.Remove(session);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -173,10 +178,15 @@ namespace MQTTnet.Server
|
||||
}
|
||||
|
||||
List<MqttSession> subscriberSessions;
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
subscriberSessions = _subscriberSessions.ToList();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitReadLock();
|
||||
}
|
||||
|
||||
// Calculate application message topic hash once for subscription checks
|
||||
MqttTopicHash.Calculate(applicationMessage.Topic, out var topicHash, out _, out _);
|
||||
@@ -256,13 +266,23 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_createConnectionSyncRoot.Dispose();
|
||||
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
foreach (var sessionItem in _sessions)
|
||||
{
|
||||
sessionItem.Value.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
if (_sessionsManagementLock != null)
|
||||
{
|
||||
_sessionsManagementLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public MqttClient GetClient(string id)
|
||||
@@ -286,17 +306,17 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IList<MqttClientStatus>> GetClientStatusesAsync()
|
||||
public Task<IList<MqttClientStatus>> GetClientsStatus()
|
||||
{
|
||||
var result = new List<MqttClientStatus>();
|
||||
|
||||
lock (_clients)
|
||||
{
|
||||
foreach (var connection in _clients.Values)
|
||||
foreach (var client in _clients.Values)
|
||||
{
|
||||
var clientStatus = new MqttClientStatus(connection)
|
||||
var clientStatus = new MqttClientStatus(client)
|
||||
{
|
||||
Session = new MqttSessionStatus(connection.Session)
|
||||
Session = new MqttSessionStatus(client.Session)
|
||||
};
|
||||
|
||||
result.Add(clientStatus);
|
||||
@@ -306,11 +326,12 @@ namespace MQTTnet.Server
|
||||
return Task.FromResult((IList<MqttClientStatus>)result);
|
||||
}
|
||||
|
||||
public Task<IList<MqttSessionStatus>> GetSessionStatusAsync()
|
||||
public Task<IList<MqttSessionStatus>> GetSessionsStatus()
|
||||
{
|
||||
var result = new List<MqttSessionStatus>();
|
||||
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
foreach (var sessionItem in _sessions)
|
||||
{
|
||||
@@ -318,6 +339,10 @@ namespace MQTTnet.Server
|
||||
result.Add(sessionStatus);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitReadLock();
|
||||
}
|
||||
|
||||
return Task.FromResult((IList<MqttSessionStatus>)result);
|
||||
}
|
||||
@@ -352,9 +377,7 @@ namespace MQTTnet.Server
|
||||
|
||||
if (_eventContainer.ClientConnectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new ClientConnectedEventArgs(
|
||||
connectPacket.ClientId,
|
||||
connectPacket.Username,
|
||||
var eventArgs = new ClientConnectedEventArgs(connectPacket,
|
||||
channelAdapter.PacketFormatterAdapter.ProtocolVersion,
|
||||
channelAdapter.Endpoint,
|
||||
client.Session.Items);
|
||||
@@ -415,7 +438,8 @@ namespace MQTTnet.Server
|
||||
|
||||
public void OnSubscriptionsAdded(MqttSession clientSession, List<string> topics)
|
||||
{
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (!clientSession.HasSubscribedTopics)
|
||||
{
|
||||
@@ -428,11 +452,16 @@ namespace MQTTnet.Server
|
||||
clientSession.AddSubscribedTopic(topic);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSubscriptionsRemoved(MqttSession clientSession, List<string> subscriptionTopics)
|
||||
{
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
foreach (var subscriptionTopic in subscriptionTopics)
|
||||
{
|
||||
@@ -445,6 +474,10 @@ namespace MQTTnet.Server
|
||||
_subscriberSessions.Remove(clientSession);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -545,7 +578,8 @@ namespace MQTTnet.Server
|
||||
MqttSession oldSession;
|
||||
MqttClient oldClient;
|
||||
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
MqttSession session;
|
||||
|
||||
@@ -578,17 +612,23 @@ namespace MQTTnet.Server
|
||||
_sessions[connectPacket.ClientId] = session;
|
||||
|
||||
// Create a new client (always required).
|
||||
_clients.TryGetValue(connectPacket.ClientId, out oldClient);
|
||||
if (oldClient != null)
|
||||
lock (_clients)
|
||||
{
|
||||
// This will stop the current client from sending and receiving but remains the connection active
|
||||
// for a later DISCONNECT packet.
|
||||
oldClient.IsTakenOver = true;
|
||||
_clients.TryGetValue(connectPacket.ClientId, out oldClient);
|
||||
if (oldClient != null)
|
||||
{
|
||||
// This will stop the current client from sending and receiving but remains the connection active
|
||||
// for a later DISCONNECT packet.
|
||||
oldClient.IsTakenOver = true;
|
||||
}
|
||||
|
||||
client = CreateClient(connectPacket, channelAdapter, session);
|
||||
_clients[connectPacket.ClientId] = client;
|
||||
}
|
||||
|
||||
client = CreateClient(connectPacket, channelAdapter, session);
|
||||
|
||||
_clients[connectPacket.ClientId] = client;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
if (!connAckPacket.IsSessionPresent)
|
||||
@@ -636,7 +676,8 @@ namespace MQTTnet.Server
|
||||
|
||||
MqttSession GetClientSession(string clientId)
|
||||
{
|
||||
lock (_sessionsManagementLock)
|
||||
_sessionsManagementLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (!_sessions.TryGetValue(clientId, out var session))
|
||||
{
|
||||
@@ -645,6 +686,10 @@ namespace MQTTnet.Server
|
||||
|
||||
return session;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessionsManagementLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttConnectPacket> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
@@ -695,4 +740,4 @@ namespace MQTTnet.Server
|
||||
return eventArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Server
|
||||
readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>();
|
||||
|
||||
// Use subscription lock to maintain consistency across subscriptions and topic hash dictionaries
|
||||
readonly AsyncLock _subscriptionsLock = new AsyncLock();
|
||||
readonly ReaderWriterLockSlim _subscriptionsLock = new ReaderWriterLockSlim();
|
||||
readonly Dictionary<ulong, TopicHashMaskSubscriptions> _wildcardSubscriptionsByTopicHash = new Dictionary<ulong, TopicHashMaskSubscriptions>();
|
||||
|
||||
public MqttClientSubscriptionsManager(
|
||||
@@ -51,7 +51,8 @@ namespace MQTTnet.Server
|
||||
var possibleSubscriptions = new List<MqttSubscription>();
|
||||
|
||||
// Check for possible subscriptions. They might have collisions but this is fine.
|
||||
using (_subscriptionsLock.EnterAsync(CancellationToken.None).GetAwaiter().GetResult())
|
||||
_subscriptionsLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var noWildcardSubscriptions))
|
||||
{
|
||||
@@ -73,6 +74,10 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitReadLock();
|
||||
}
|
||||
|
||||
// The pre check has evaluated that nothing is subscribed.
|
||||
// If there were some possible candidates they get checked below
|
||||
@@ -157,7 +162,10 @@ namespace MQTTnet.Server
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_subscriptionsLock.Dispose();
|
||||
if (_subscriptionsLock != null)
|
||||
{
|
||||
_subscriptionsLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
|
||||
@@ -232,7 +240,8 @@ namespace MQTTnet.Server
|
||||
|
||||
var removedSubscriptions = new List<string>();
|
||||
|
||||
using (await _subscriptionsLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||
_subscriptionsLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||
{
|
||||
@@ -293,8 +302,11 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_subscriptionChangedNotification?.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitWriteLock();
|
||||
_subscriptionChangedNotification?.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
||||
}
|
||||
|
||||
if (_eventContainer.ClientUnsubscribedTopicEvent.HasHandlers)
|
||||
{
|
||||
@@ -341,7 +353,8 @@ namespace MQTTnet.Server
|
||||
|
||||
// Add to subscriptions and maintain topic hash dictionaries
|
||||
|
||||
using (_subscriptionsLock.EnterAsync(CancellationToken.None).GetAwaiter().GetResult())
|
||||
_subscriptionsLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
MqttTopicHash.Calculate(topicFilter.Topic, out var topicHash, out _, out var hasWildcard);
|
||||
|
||||
@@ -391,6 +404,10 @@ namespace MQTTnet.Server
|
||||
subscriptions.Add(subscription);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
return new CreateSubscriptionResult
|
||||
{
|
||||
@@ -511,4 +528,4 @@ namespace MQTTnet.Server
|
||||
public MqttSubscription Subscription { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.GetClientStatusesAsync();
|
||||
return _clientSessionsManager.GetClientsStatus();
|
||||
}
|
||||
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedMessagesAsync()
|
||||
@@ -226,7 +226,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.GetSessionStatusAsync();
|
||||
return _clientSessionsManager.GetSessionsStatus();
|
||||
}
|
||||
|
||||
public Task InjectApplicationMessage(InjectedMqttApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default)
|
||||
@@ -292,7 +292,7 @@ namespace MQTTnet.Server
|
||||
|
||||
_cancellationTokenSource.Cancel(false);
|
||||
|
||||
await _clientSessionsManager.CloseAllConnectionsAsync().ConfigureAwait(false);
|
||||
await _clientSessionsManager.CloseAllConnections().ConfigureAwait(false);
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user