mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-29 01:32:37 +08:00
@@ -289,3 +289,5 @@ __pycache__/
|
|||||||
|
|
||||||
|
|
||||||
Build/nuget.exe
|
Build/nuget.exe
|
||||||
|
*.js
|
||||||
|
*.map
|
||||||
|
|||||||
+14
-16
@@ -2,28 +2,26 @@
|
|||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MQTTnet</id>
|
<id>MQTTnet</id>
|
||||||
<version>2.4.0</version>
|
<version>2.5.0</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>
|
||||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||||
<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 .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>* [Server] Added an event which is fired when a client has disconnected
|
<releaseNotes>* [Core] Merged the .NET Framwork and netstandard projects (Thanks to @JanEggers)
|
||||||
* [Server] Added support for retained application messages
|
* [Core] Migrated the trace to a non-static approach (Breaking Change!)
|
||||||
* [Server] Added support for saving and loading retained messages
|
* [Core] Added a builder for application messages using a fluent API
|
||||||
* [Server] The client connection is now closed if sending of one pending application message has failed
|
* [Client] Added a first version of a managed client which will manage the connection, subscription etc. automatically (Thanks to @JTrotta)
|
||||||
* [Server] Fixed handling of _Dup_ flag (Thanks to haeberle)
|
* [Client] The session state response from the server is now returned in the _ConnectAsync_ method and also part of the _Connected_ event args
|
||||||
* [Core] Optimized exception handling
|
* [Client] Added a _TopicFilterBuilder_ using a fluent API (Namespace Changes!)
|
||||||
* [Core] Mono is now also supported (Thanks to JTrotta)
|
* [Client] Added several new options for the WebSocket channel (Thanks to @ChristianRiedl)
|
||||||
* [Client] The options are now passed in _ConnectAsync_ (Breaking change! Read Wiki for examples)
|
* [Server] Added support for WebSockets via ASP.NET Core 2.0 (Thanks to @ChristianRiedl)
|
||||||
* [Core] Trace class renamed to _MqttNetTrace_ (Breaking change!)
|
* [Server] Added support for a custom application message interceptor
|
||||||
* [Client] Extended certificate validation options (Breaking change!)
|
|
||||||
* [Client] Added static certificate validation callback (NetFramework, NetStandard) / ignorable certificate errors (UniversalWindows) to _MqttTcpChannel_
|
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M</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</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<group targetFramework="netstandard1.3">
|
<group targetFramework="netstandard1.3">
|
||||||
@@ -48,8 +46,8 @@
|
|||||||
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.*" target="lib\uap10.0\"/>
|
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.*" target="lib\uap10.0\"/>
|
||||||
|
|
||||||
<!-- .NET Framework -->
|
<!-- .NET Framework -->
|
||||||
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.Core.*" target="lib\net45\"/>
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net45\MQTTnet.Core.*" target="lib\net45\"/>
|
||||||
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.*" target="lib\net45\"/>
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net45\MQTTnet.*" target="lib\net45\"/>
|
||||||
|
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
+1
-2
@@ -3,8 +3,7 @@ param([string]$version)
|
|||||||
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
|
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
|
||||||
|
|
||||||
$msbuild = "MSBuild.exe"
|
$msbuild = "MSBuild.exe"
|
||||||
&$msbuild ..\Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj /t:Build /p:Configuration="Release"
|
&dotnet build ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj -c="Release"
|
||||||
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release"
|
|
||||||
&$msbuild ..\Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj /t:Build /p:Configuration="Release"
|
&$msbuild ..\Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj /t:Build /p:Configuration="Release"
|
||||||
|
|
||||||
Remove-Item .\NuGet -Force -Recurse
|
Remove-Item .\NuGet -Force -Recurse
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspnetCore
|
||||||
|
{
|
||||||
|
public static class ApplicationBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
|
||||||
|
{
|
||||||
|
app.UseWebSockets();
|
||||||
|
app.Use(async (context, next) =>
|
||||||
|
{
|
||||||
|
if (context.Request.Path == path && context.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||||
|
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync("mqtt"))
|
||||||
|
{
|
||||||
|
await adapter.AcceptWebSocketAsync(webSocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||||
|
{
|
||||||
|
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||||
|
|
||||||
|
configure(server);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspnetCore
|
||||||
|
{
|
||||||
|
public class MqttHostedServer : MqttServer, IHostedService
|
||||||
|
{
|
||||||
|
public MqttHostedServer(IOptions<MqttServerOptions> options, IEnumerable<IMqttServerAdapter> adapters, ILogger<MqttServer> logger, MqttClientSessionsManager clientSessionsManager)
|
||||||
|
: base(options, adapters, logger, clientSessionsManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return StartAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return StopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspNetCore
|
||||||
|
{
|
||||||
|
public class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
|
||||||
|
{
|
||||||
|
private readonly IMqttCommunicationAdapterFactory _mqttCommunicationAdapterFactory;
|
||||||
|
|
||||||
|
public MqttWebSocketServerAdapter(IMqttCommunicationAdapterFactory mqttCommunicationAdapterFactory)
|
||||||
|
{
|
||||||
|
_mqttCommunicationAdapterFactory = mqttCommunicationAdapterFactory ?? throw new ArgumentNullException(nameof(mqttCommunicationAdapterFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||||
|
|
||||||
|
public Task StartAsync(MqttServerOptions options)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AcceptWebSocketAsync(WebSocket webSocket)
|
||||||
|
{
|
||||||
|
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||||
|
|
||||||
|
var channel = new MqttWebSocketServerChannel(webSocket);
|
||||||
|
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerMqttCommunicationAdapter(channel);
|
||||||
|
|
||||||
|
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
||||||
|
ClientAccepted?.Invoke(this, eventArgs);
|
||||||
|
return eventArgs.SessionTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MqttWebSocketServerChannel : IMqttCommunicationChannel, IDisposable
|
||||||
|
{
|
||||||
|
private readonly WebSocket _webSocket;
|
||||||
|
|
||||||
|
public MqttWebSocketServerChannel(WebSocket webSocket)
|
||||||
|
{
|
||||||
|
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
|
||||||
|
|
||||||
|
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream SendStream => RawReceiveStream;
|
||||||
|
public Stream ReceiveStream => RawReceiveStream;
|
||||||
|
public Stream RawReceiveStream { get; }
|
||||||
|
|
||||||
|
public Task ConnectAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DisconnectAsync()
|
||||||
|
{
|
||||||
|
RawReceiveStream?.Dispose();
|
||||||
|
|
||||||
|
if (_webSocket == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
RawReceiveStream?.Dispose();
|
||||||
|
SendStream?.Dispose();
|
||||||
|
ReceiveStream?.Dispose();
|
||||||
|
|
||||||
|
_webSocket?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspnetCore
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMqttServerServices();
|
||||||
|
|
||||||
|
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
|
||||||
|
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
|
||||||
|
services.AddSingleton<MqttHostedServer>();
|
||||||
|
|
||||||
|
services.AddSingleton<MqttWebSocketServerAdapter>();
|
||||||
|
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttCommunicationAdapterFactory : IMqttCommunicationAdapterFactory
|
|
||||||
{
|
|
||||||
public IMqttCommunicationAdapter CreateMqttCommunicationAdapter(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
if (options is MqttClientTcpOptions tcpOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttTcpChannel(tcpOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options is MqttClientWebSocketOptions webSocketOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private Socket _defaultEndpointSocket;
|
|
||||||
private Socket _tlsEndpointSocket;
|
|
||||||
private X509Certificate2 _tlsCertificate;
|
|
||||||
|
|
||||||
private bool _isRunning;
|
|
||||||
|
|
||||||
public event Action<IMqttCommunicationAdapter> ClientAccepted;
|
|
||||||
|
|
||||||
public Task StartAsync(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
|
||||||
_isRunning = true;
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
if (options.DefaultEndpointOptions.IsEnabled)
|
|
||||||
{
|
|
||||||
_defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
_defaultEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetDefaultEndpointPort()));
|
|
||||||
_defaultEndpointSocket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.TlsEndpointOptions.IsEnabled)
|
|
||||||
{
|
|
||||||
if (options.TlsEndpointOptions.Certificate == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("TLS certificate is not set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
|
||||||
|
|
||||||
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort()));
|
|
||||||
_tlsEndpointSocket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync()
|
|
||||||
{
|
|
||||||
_isRunning = false;
|
|
||||||
|
|
||||||
_cancellationTokenSource?.Cancel(false);
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
_defaultEndpointSocket?.Dispose();
|
|
||||||
_defaultEndpointSocket = null;
|
|
||||||
|
|
||||||
_tlsEndpointSocket?.Dispose();
|
|
||||||
_tlsEndpointSocket = null;
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
StopAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var tcpChannel = new MqttTcpChannel(clientSocket, null);
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(tcpChannel, new MqttPacketSerializer());
|
|
||||||
ClientAccepted?.Invoke(clientAdapter);
|
|
||||||
}
|
|
||||||
catch (Exception exception) when (!(exception is ObjectDisposedException))
|
|
||||||
{
|
|
||||||
MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
|
||||||
|
|
||||||
//excessive CPU consumed if in endless loop of socket errors
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var clientSocket = await Task.Factory.FromAsync(_tlsEndpointSocket.BeginAccept, _tlsEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
|
||||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var tcpChannel = new MqttTcpChannel(clientSocket, sslStream);
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(tcpChannel, new MqttPacketSerializer());
|
|
||||||
ClientAccepted?.Invoke(clientAdapter);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at TLS endpoint.");
|
|
||||||
|
|
||||||
//excessive CPU consumed if in endless loop of socket errors
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
|
||||||
{
|
|
||||||
private readonly MqttClientTcpOptions _options;
|
|
||||||
|
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
|
||||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
|
||||||
|
|
||||||
private Socket _socket;
|
|
||||||
private SslStream _sslStream;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// called on client sockets are created in connect
|
|
||||||
/// </summary>
|
|
||||||
public MqttTcpChannel(MqttClientTcpOptions options)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// called on server, sockets are passed in
|
|
||||||
/// connect will not be called
|
|
||||||
/// </summary>
|
|
||||||
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
_sslStream = sslStream;
|
|
||||||
|
|
||||||
CreateStreams(socket, sslStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
|
||||||
public Stream ReceiveStream { get; private set; }
|
|
||||||
public Stream RawReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
|
||||||
{
|
|
||||||
if (_socket == null)
|
|
||||||
{
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (_options.TlsOptions.UseTls)
|
|
||||||
{
|
|
||||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
|
||||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateStreams(_socket, _sslStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
RawReceiveStream?.Dispose();
|
|
||||||
RawReceiveStream = null;
|
|
||||||
|
|
||||||
ReceiveStream?.Dispose();
|
|
||||||
ReceiveStream = null;
|
|
||||||
|
|
||||||
SendStream?.Dispose();
|
|
||||||
SendStream = null;
|
|
||||||
|
|
||||||
_socket?.Dispose();
|
|
||||||
_socket = null;
|
|
||||||
|
|
||||||
_sslStream?.Dispose();
|
|
||||||
_sslStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateStreams(Socket socket, Stream sslStream)
|
|
||||||
{
|
|
||||||
RawReceiveStream = sslStream ?? new NetworkStream(socket);
|
|
||||||
|
|
||||||
//cannot use this as default buffering prevents from receiving the first connect message
|
|
||||||
//need two streams otherwise read and write have to be synchronized
|
|
||||||
SendStream = new BufferedStream(RawReceiveStream, BufferSize);
|
|
||||||
ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
|
||||||
{
|
|
||||||
if (CustomCertificateValidationCallback != null)
|
|
||||||
{
|
|
||||||
return CustomCertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown))
|
|
||||||
{
|
|
||||||
if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
|
||||||
{
|
|
||||||
if (!_options.TlsOptions.IgnoreCertificateChainErrors)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _options.TlsOptions.AllowUntrustedCertificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static X509CertificateCollection LoadCertificates(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
var certificates = new X509CertificateCollection();
|
|
||||||
if (options.TlsOptions.Certificates == null)
|
|
||||||
{
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var certificate in options.TlsOptions.Certificates)
|
|
||||||
{
|
|
||||||
certificates.Add(new X509Certificate(certificate));
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable
|
|
||||||
{
|
|
||||||
private readonly MqttClientWebSocketOptions _options;
|
|
||||||
|
|
||||||
private ClientWebSocket _webSocket;
|
|
||||||
|
|
||||||
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream RawReceiveStream { get; private set; }
|
|
||||||
public Stream SendStream => RawReceiveStream;
|
|
||||||
public Stream ReceiveStream => RawReceiveStream;
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
|
||||||
{
|
|
||||||
var uri = _options.Uri;
|
|
||||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
uri = "ws://" + uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
_webSocket = new ClientWebSocket();
|
|
||||||
_webSocket.Options.KeepAliveInterval = _options.KeepAlivePeriod;
|
|
||||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
|
||||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
RawReceiveStream = null;
|
|
||||||
|
|
||||||
if (_webSocket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_webSocket?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class WebSocketStream : Stream
|
|
||||||
{
|
|
||||||
private readonly ClientWebSocket _webSocket;
|
|
||||||
|
|
||||||
public WebSocketStream(ClientWebSocket webSocket)
|
|
||||||
{
|
|
||||||
_webSocket = webSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var currentOffset = offset;
|
|
||||||
var targetOffset = offset + count;
|
|
||||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset)
|
|
||||||
{
|
|
||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
|
||||||
currentOffset += response.Count;
|
|
||||||
|
|
||||||
if (response.MessageType == WebSocketMessageType.Close)
|
|
||||||
{
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentOffset - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead => true;
|
|
||||||
public override bool CanSeek => false;
|
|
||||||
public override bool CanWrite => true;
|
|
||||||
|
|
||||||
public override long Length => throw new NotSupportedException();
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => throw new NotSupportedException();
|
|
||||||
set => throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>MQTTnet</RootNamespace>
|
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<TargetFrameworkProfile />
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|AnyCPU'">
|
|
||||||
<OutputPath>bin\Any CPU\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x86'">
|
|
||||||
<OutputPath>bin\x86\Any CPU\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
|
||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x64'">
|
|
||||||
<OutputPath>bin\x64\Any CPU\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Implementations\MqttCommunicationAdapterFactory.cs" />
|
|
||||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
|
|
||||||
<Compile Include="Implementations\WebSocketStream.cs" />
|
|
||||||
<Compile Include="MqttClientFactory.cs" />
|
|
||||||
<Compile Include="MqttServerFactory.cs" />
|
|
||||||
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
|
|
||||||
<Project>{2ecb99e4-72d0-4c23-99ba-93d511d3967d}</Project>
|
|
||||||
<Name>MQTTnet.Core</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttClientFactory : IMqttClientFactory
|
|
||||||
{
|
|
||||||
public IMqttClient CreateMqttClient()
|
|
||||||
{
|
|
||||||
return new MqttClient(new MqttCommunicationAdapterFactory());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttServerFactory : IMqttServerFactory
|
|
||||||
{
|
|
||||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
[assembly: AssemblyTitle("MQTTnet")]
|
|
||||||
[assembly: AssemblyDescription("MQTTnet for .NET Framework")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("Christian Kratky")]
|
|
||||||
[assembly: AssemblyProduct("MQTTnet")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
[assembly: Guid("a480ef90-0eaa-4d9a-b271-47a9c47f6f7d")]
|
|
||||||
[assembly: AssemblyVersion("2.4.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("2.4.0.0")]
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttCommunicationAdapterFactory : IMqttCommunicationAdapterFactory
|
|
||||||
{
|
|
||||||
public IMqttCommunicationAdapter CreateMqttCommunicationAdapter(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
if (options is MqttClientTcpOptions tcpOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttTcpChannel(tcpOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options is MqttClientWebSocketOptions webSocketOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,28 +7,33 @@ using System.Security.Cryptography.X509Certificates;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<MqttServerAdapter> _logger;
|
||||||
|
private readonly IMqttCommunicationAdapterFactory _mqttCommunicationAdapterFactory;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private Socket _defaultEndpointSocket;
|
private Socket _defaultEndpointSocket;
|
||||||
private Socket _tlsEndpointSocket;
|
private Socket _tlsEndpointSocket;
|
||||||
private X509Certificate2 _tlsCertificate;
|
private X509Certificate2 _tlsCertificate;
|
||||||
|
|
||||||
private bool _isRunning;
|
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory mqttCommunicationAdapterFactory)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_mqttCommunicationAdapterFactory = mqttCommunicationAdapterFactory ?? throw new ArgumentNullException(nameof(mqttCommunicationAdapterFactory));
|
||||||
|
}
|
||||||
|
|
||||||
public event Action<IMqttCommunicationAdapter> ClientAccepted;
|
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||||
|
|
||||||
public Task StartAsync(MqttServerOptions options)
|
public Task StartAsync(MqttServerOptions options)
|
||||||
{
|
{
|
||||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
|
||||||
|
|
||||||
_isRunning = true;
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
@@ -49,6 +54,10 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
||||||
|
if (!_tlsCertificate.HasPrivateKey)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
||||||
|
}
|
||||||
|
|
||||||
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort()));
|
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort()));
|
||||||
@@ -62,8 +71,6 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
_isRunning = false;
|
|
||||||
|
|
||||||
_cancellationTokenSource?.Cancel(false);
|
_cancellationTokenSource?.Cancel(false);
|
||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
@@ -71,6 +78,8 @@ namespace MQTTnet.Implementations
|
|||||||
_defaultEndpointSocket?.Dispose();
|
_defaultEndpointSocket?.Dispose();
|
||||||
_defaultEndpointSocket = null;
|
_defaultEndpointSocket = null;
|
||||||
|
|
||||||
|
_tlsCertificate = null;
|
||||||
|
|
||||||
_tlsEndpointSocket?.Dispose();
|
_tlsEndpointSocket?.Dispose();
|
||||||
_tlsEndpointSocket = null;
|
_tlsEndpointSocket = null;
|
||||||
|
|
||||||
@@ -88,13 +97,18 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
//todo: else branch can be used with min dependency NET46
|
||||||
|
#if NET451
|
||||||
|
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer());
|
#endif
|
||||||
ClientAccepted?.Invoke(clientAdapter);
|
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerMqttCommunicationAdapter(new MqttTcpChannel(clientSocket, null));
|
||||||
|
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
_logger.LogError(new EventId(), exception, "Error while accepting connection at default endpoint.");
|
||||||
|
|
||||||
//excessive CPU consumed if in endless loop of socket errors
|
//excessive CPU consumed if in endless loop of socket errors
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||||
@@ -108,17 +122,21 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#if NET451
|
||||||
|
var clientSocket = await Task.Factory.FromAsync(_tlsEndpointSocket.BeginAccept, _tlsEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
var clientSocket = await _tlsEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
var clientSocket = await _tlsEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
||||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
||||||
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer());
|
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerMqttCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream));
|
||||||
ClientAccepted?.Invoke(clientAdapter);
|
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at TLS endpoint.");
|
_logger.LogError(new EventId(), exception, "Error while accepting connection at TLS endpoint.");
|
||||||
|
|
||||||
//excessive CPU consumed if in endless loop of socket errors
|
//excessive CPU consumed if in endless loop of socket errors
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
private readonly MqttClientTcpOptions _options;
|
private readonly MqttClientTcpOptions _options;
|
||||||
|
|
||||||
|
//todo: this can be used with min dependency NetStandard1.6
|
||||||
|
#if NET45
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||||
|
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
||||||
|
#endif
|
||||||
|
|
||||||
private Socket _socket;
|
private Socket _socket;
|
||||||
private SslStream _sslStream;
|
private SslStream _sslStream;
|
||||||
|
|
||||||
@@ -34,12 +41,13 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
_sslStream = sslStream;
|
_sslStream = sslStream;
|
||||||
ReceiveStream = (Stream)sslStream ?? new NetworkStream(socket);
|
|
||||||
|
CreateStreams(socket, sslStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream SendStream => ReceiveStream;
|
public Stream SendStream { get; private set; }
|
||||||
public Stream ReceiveStream { get; private set; }
|
public Stream ReceiveStream { get; private set; }
|
||||||
public Stream RawReceiveStream => ReceiveStream;
|
public Stream RawReceiveStream { get; private set; }
|
||||||
|
|
||||||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; }
|
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; }
|
||||||
|
|
||||||
@@ -50,18 +58,20 @@ namespace MQTTnet.Implementations
|
|||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//todo: else brach can be used with min dependency NET46
|
||||||
|
#if NET451
|
||||||
|
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
await _socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
|
await _socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_options.TlsOptions.UseTls)
|
if (_options.TlsOptions.UseTls)
|
||||||
{
|
{
|
||||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
||||||
ReceiveStream = _sslStream;
|
|
||||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
CreateStreams(_socket, _sslStream);
|
||||||
ReceiveStream = new NetworkStream(_socket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
public Task DisconnectAsync()
|
||||||
@@ -110,7 +120,7 @@ namespace MQTTnet.Implementations
|
|||||||
return _options.TlsOptions.AllowUntrustedCertificates;
|
return _options.TlsOptions.AllowUntrustedCertificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static X509CertificateCollection LoadCertificates(MqttClientOptions options)
|
private static X509CertificateCollection LoadCertificates(IMqttClientOptions options)
|
||||||
{
|
{
|
||||||
var certificates = new X509CertificateCollection();
|
var certificates = new X509CertificateCollection();
|
||||||
if (options.TlsOptions.Certificates == null)
|
if (options.TlsOptions.Certificates == null)
|
||||||
@@ -125,5 +135,24 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
return certificates;
|
return certificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateStreams(Socket socket, Stream sslStream)
|
||||||
|
{
|
||||||
|
RawReceiveStream = sslStream ?? new NetworkStream(socket);
|
||||||
|
|
||||||
|
|
||||||
|
//cannot use this as default buffering prevents from receiving the first connect message
|
||||||
|
//need two streams otherwise read and write have to be synchronized
|
||||||
|
|
||||||
|
//todo: if branch can be used with min dependency NetStandard1.6
|
||||||
|
#if NET45
|
||||||
|
SendStream = new BufferedStream(RawReceiveStream, BufferSize);
|
||||||
|
ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize);
|
||||||
|
#else
|
||||||
|
SendStream = RawReceiveStream;
|
||||||
|
ReceiveStream = RawReceiveStream;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using MQTTnet.Core.Client;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -32,6 +33,37 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
_webSocket = new ClientWebSocket();
|
_webSocket = new ClientWebSocket();
|
||||||
_webSocket.Options.KeepAliveInterval = _options.KeepAlivePeriod;
|
_webSocket.Options.KeepAliveInterval = _options.KeepAlivePeriod;
|
||||||
|
|
||||||
|
if (_options.RequestHeaders != null)
|
||||||
|
{
|
||||||
|
foreach (var requestHeader in _options.RequestHeaders)
|
||||||
|
{
|
||||||
|
_webSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.SubProtocols != null)
|
||||||
|
{
|
||||||
|
foreach (var subProtocol in _options.SubProtocols)
|
||||||
|
{
|
||||||
|
_webSocket.Options.AddSubProtocol(subProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.CookieContainer != null)
|
||||||
|
{
|
||||||
|
_webSocket.Options.Cookies = _options.CookieContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
|
||||||
|
{
|
||||||
|
_webSocket.Options.ClientCertificates = new X509CertificateCollection();
|
||||||
|
foreach (var certificate in _options.TlsOptions.Certificates)
|
||||||
|
{
|
||||||
|
_webSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
|
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
|
||||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ using System.IO;
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public class WebSocketStream : Stream
|
public class WebSocketStream : Stream
|
||||||
{
|
{
|
||||||
private readonly ClientWebSocket _webSocket;
|
private readonly WebSocket _webSocket;
|
||||||
|
|
||||||
public WebSocketStream(ClientWebSocket webSocket)
|
public WebSocketStream(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
_webSocket = webSocket;
|
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanRead => true;
|
public override bool CanRead => true;
|
||||||
@@ -41,6 +42,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||||
currentOffset += response.Count;
|
currentOffset += response.Count;
|
||||||
|
count -= response.Count;
|
||||||
|
|
||||||
if (response.MessageType == WebSocketMessageType.Close)
|
if (response.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
@@ -48,6 +50,11 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_webSocket.State == WebSocketState.Closed)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException( "connection closed" );
|
||||||
|
}
|
||||||
|
|
||||||
return currentOffset - offset;
|
return currentOffset - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard1.3</TargetFramework>
|
<TargetFrameworks>netstandard1.3;net451</TargetFrameworks>
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
<RootNamespace>MQTTnet</RootNamespace>
|
<RootNamespace>MQTTnet</RootNamespace>
|
||||||
<AssemblyVersion>2.4.0.0</AssemblyVersion>
|
<AssemblyVersion>2.5.0.0</AssemblyVersion>
|
||||||
<FileVersion>2.4.0.0</FileVersion>
|
<FileVersion>2.5.0.0</FileVersion>
|
||||||
<Version>0.0.0.0</Version>
|
<Version>0.0.0.0</Version>
|
||||||
<Company />
|
<Company />
|
||||||
<Product />
|
<Product />
|
||||||
@@ -19,10 +19,15 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
||||||
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
||||||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
|
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttClientFactory : IMqttClientFactory
|
|
||||||
{
|
|
||||||
public IMqttClient CreateMqttClient()
|
|
||||||
{
|
|
||||||
return new MqttClient(new MqttCommunicationAdapterFactory());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Core.ManagedClient;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using System.Linq;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
|
|
||||||
|
namespace MQTTnet
|
||||||
|
{
|
||||||
|
public class MqttFactory : IMqttCommunicationAdapterFactory, IMqttClientSesssionFactory, IMqttClientFactory, IMqttServerFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
private static IServiceProvider BuildServiceProvider()
|
||||||
|
{
|
||||||
|
var serviceProvider = new ServiceCollection()
|
||||||
|
.AddMqttClient()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddLogging()
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
serviceProvider.GetRequiredService<ILoggerFactory>()
|
||||||
|
.AddMqttTrace();
|
||||||
|
|
||||||
|
return serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttFactory()
|
||||||
|
: this(BuildServiceProvider())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttFactory(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter CreateClientMqttCommunicationAdapter(IMqttClientOptions options)
|
||||||
|
{
|
||||||
|
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
|
||||||
|
return new MqttChannelCommunicationAdapter(CreateMqttCommunicationChannel(options), CreateSerializer(options.ProtocolVersion), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter CreateServerMqttCommunicationAdapter(IMqttCommunicationChannel channel)
|
||||||
|
{
|
||||||
|
var serializer = _serviceProvider.GetRequiredService<IMqttPacketSerializer>();
|
||||||
|
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
|
||||||
|
return new MqttChannelCommunicationAdapter(channel, serializer, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationChannel CreateMqttCommunicationChannel(IMqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
switch (options)
|
||||||
|
{
|
||||||
|
case MqttClientTcpOptions tcpOptions:
|
||||||
|
return CreateTcpChannel(tcpOptions);
|
||||||
|
case MqttClientWebSocketOptions webSocketOptions:
|
||||||
|
return CreateWebSocketChannel(webSocketOptions);
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTcpChannel CreateTcpChannel(MqttClientTcpOptions tcpOptions)
|
||||||
|
{
|
||||||
|
return new MqttTcpChannel(tcpOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttWebSocketChannel CreateWebSocketChannel(MqttClientWebSocketOptions webSocketOptions)
|
||||||
|
{
|
||||||
|
return new MqttWebSocketChannel(webSocketOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttPacketSerializer CreateSerializer(MqttProtocolVersion protocolVersion)
|
||||||
|
{
|
||||||
|
return new MqttPacketSerializer()
|
||||||
|
{
|
||||||
|
ProtocolVersion = protocolVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttClientSession CreateClientSession(string sessionId, MqttClientSessionsManager mqttClientSessionsManager)
|
||||||
|
{
|
||||||
|
return new MqttClientSession(sessionId, mqttClientSessionsManager, _serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(), _serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttClient CreateMqttClient()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<IMqttClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedMqttClient CreateManagedMqttClient()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<ManagedMqttClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttServer CreateMqttServer()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<IMqttServer>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttServerFactory : IMqttServerFactory
|
|
||||||
{
|
|
||||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.ManagedClient;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Core.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddMqttServer(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMqttServerServices();
|
||||||
|
|
||||||
|
services.AddSingleton<IMqttServer>(s => s.GetService<MqttServer>());
|
||||||
|
services.AddSingleton<MqttServer>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMqttServerServices(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddOptions();
|
||||||
|
services.AddSingleton<MqttFactory>();
|
||||||
|
services.AddSingleton<IMqttCommunicationAdapterFactory>(s => s.GetService<MqttFactory>());
|
||||||
|
services.AddSingleton<IMqttClientSesssionFactory>(s => s.GetService<MqttFactory>());
|
||||||
|
|
||||||
|
services.AddTransient<IMqttServerAdapter, MqttServerAdapter>();
|
||||||
|
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
|
||||||
|
|
||||||
|
services.AddTransient<MqttClientSessionsManager>();
|
||||||
|
services.AddTransient<MqttClientRetainedMessagesManager>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMqttServer(this IServiceCollection services, Action<MqttServerOptions> configureOptions)
|
||||||
|
{
|
||||||
|
return services
|
||||||
|
.AddMqttServer()
|
||||||
|
.Configure(configureOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMqttClient(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<MqttFactory>();
|
||||||
|
services.AddSingleton<IMqttCommunicationAdapterFactory>(s => s.GetService<MqttFactory>());
|
||||||
|
|
||||||
|
services.AddTransient<IMqttClient, MqttClient>();
|
||||||
|
services.AddTransient<MqttClient>();
|
||||||
|
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
|
||||||
|
services.AddTransient<ManagedMqttClient>();
|
||||||
|
services.AddTransient<MqttPacketDispatcher>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILoggerFactory AddMqttTrace(this ILoggerFactory factory)
|
||||||
|
{
|
||||||
|
factory.AddProvider(new MqttNetTrace());
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttCommunicationAdapterFactory : IMqttCommunicationAdapterFactory
|
|
||||||
{
|
|
||||||
public IMqttCommunicationAdapter CreateMqttCommunicationAdapter(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
if (options is MqttClientTcpOptions tcpOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttTcpChannel(tcpOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options is MqttClientWebSocketOptions webSocketOptions)
|
|
||||||
{
|
|
||||||
return new MqttChannelCommunicationAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
using Windows.Networking.Sockets;
|
using Windows.Networking.Sockets;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<MqttServerAdapter> _logger;
|
||||||
|
private readonly IMqttCommunicationAdapterFactory _mqttCommunicationAdapterFactory;
|
||||||
private StreamSocketListener _defaultEndpointSocket;
|
private StreamSocketListener _defaultEndpointSocket;
|
||||||
|
|
||||||
private bool _isRunning;
|
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory mqttCommunicationAdapterFactory)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_mqttCommunicationAdapterFactory = mqttCommunicationAdapterFactory ?? throw new ArgumentNullException(nameof(mqttCommunicationAdapterFactory));
|
||||||
|
}
|
||||||
|
|
||||||
public event Action<IMqttCommunicationAdapter> ClientAccepted;
|
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||||
|
|
||||||
public async Task StartAsync(MqttServerOptions options)
|
public async Task StartAsync(MqttServerOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
if (_defaultEndpointSocket != null) throw new InvalidOperationException("Server is already started.");
|
||||||
|
|
||||||
_isRunning = true;
|
|
||||||
|
|
||||||
if (options.DefaultEndpointOptions.IsEnabled)
|
if (options.DefaultEndpointOptions.IsEnabled)
|
||||||
{
|
{
|
||||||
@@ -39,8 +42,6 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
_isRunning = false;
|
|
||||||
|
|
||||||
_defaultEndpointSocket?.Dispose();
|
_defaultEndpointSocket?.Dispose();
|
||||||
_defaultEndpointSocket = null;
|
_defaultEndpointSocket = null;
|
||||||
|
|
||||||
@@ -56,12 +57,12 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer());
|
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerMqttCommunicationAdapter(new MqttTcpChannel(args.Socket));
|
||||||
ClientAccepted?.Invoke(clientAdapter);
|
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
_logger.LogError(new EventId(), exception, "Error while accepting connection at default endpoint.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace MQTTnet.Implementations
|
|||||||
RawReceiveStream = ReceiveStream;
|
RawReceiveStream = ReceiveStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Certificate LoadCertificate(MqttClientOptions options)
|
private static Certificate LoadCertificate(BaseMqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MQTTnet.Core.Client;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -32,6 +33,37 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
_webSocket = new ClientWebSocket();
|
_webSocket = new ClientWebSocket();
|
||||||
_webSocket.Options.KeepAliveInterval = _options.KeepAlivePeriod;
|
_webSocket.Options.KeepAliveInterval = _options.KeepAlivePeriod;
|
||||||
|
|
||||||
|
if (_options.RequestHeaders != null)
|
||||||
|
{
|
||||||
|
foreach (var requestHeader in _options.RequestHeaders)
|
||||||
|
{
|
||||||
|
_webSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.SubProtocols != null)
|
||||||
|
{
|
||||||
|
foreach (var subProtocol in _options.SubProtocols)
|
||||||
|
{
|
||||||
|
_webSocket.Options.AddSubProtocol(subProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.CookieContainer != null)
|
||||||
|
{
|
||||||
|
_webSocket.Options.Cookies = _options.CookieContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
|
||||||
|
{
|
||||||
|
_webSocket.Options.ClientCertificates = new X509CertificateCollection();
|
||||||
|
foreach (var certificate in _options.TlsOptions.Certificates)
|
||||||
|
{
|
||||||
|
_webSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ using System.IO;
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public class WebSocketStream : Stream
|
public class WebSocketStream : Stream
|
||||||
{
|
{
|
||||||
private readonly ClientWebSocket _webSocket;
|
private readonly WebSocket _webSocket;
|
||||||
|
|
||||||
public WebSocketStream(ClientWebSocket webSocket)
|
public WebSocketStream(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
_webSocket = webSocket;
|
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Flush()
|
public override void Flush()
|
||||||
@@ -27,6 +28,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||||
currentOffset += response.Count;
|
currentOffset += response.Count;
|
||||||
|
count -= response.Count;
|
||||||
|
|
||||||
if (response.MessageType == WebSocketMessageType.Close)
|
if (response.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
@@ -34,6 +36,11 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_webSocket.State == WebSocketState.Closed)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException("connection closed");
|
||||||
|
}
|
||||||
|
|
||||||
return currentOffset - offset;
|
return currentOffset - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,14 +110,13 @@
|
|||||||
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Implementations\MqttCommunicationAdapterFactory.cs" />
|
|
||||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
|
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
|
||||||
<Compile Include="MqttClientFactory.cs" />
|
<Compile Include="MqttFactory.cs" />
|
||||||
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
||||||
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
||||||
<Compile Include="MqttServerFactory.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Implementations\WebSocketStream.cs" />
|
<Compile Include="Implementations\WebSocketStream.cs" />
|
||||||
|
<Compile Include="ServiceCollectionExtensions.cs" />
|
||||||
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
|
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -127,6 +126,12 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
|
||||||
|
<Version>1.1.1</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging">
|
||||||
|
<Version>1.1.2</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>5.4.0</Version>
|
<Version>5.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttClientFactory : IMqttClientFactory
|
|
||||||
{
|
|
||||||
public IMqttClient CreateMqttClient()
|
|
||||||
{
|
|
||||||
return new MqttClient(new MqttCommunicationAdapterFactory());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.ManagedClient;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
|
namespace MQTTnet
|
||||||
|
{
|
||||||
|
public class MqttFactory : IMqttCommunicationAdapterFactory, IMqttClientSesssionFactory, IMqttClientFactory, IMqttServerFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
private static IServiceProvider BuildServiceProvider()
|
||||||
|
{
|
||||||
|
var serviceProvider = new ServiceCollection()
|
||||||
|
.AddMqttClient()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddLogging()
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
serviceProvider.GetRequiredService<ILoggerFactory>()
|
||||||
|
.AddMqttTrace();
|
||||||
|
|
||||||
|
return serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttFactory()
|
||||||
|
: this(BuildServiceProvider())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttFactory(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter CreateClientMqttCommunicationAdapter(IMqttClientOptions options)
|
||||||
|
{
|
||||||
|
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
|
||||||
|
return new MqttChannelCommunicationAdapter(CreateMqttCommunicationChannel(options), CreateSerializer(options.ProtocolVersion), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter CreateServerMqttCommunicationAdapter(IMqttCommunicationChannel channel)
|
||||||
|
{
|
||||||
|
var serializer = _serviceProvider.GetRequiredService<IMqttPacketSerializer>();
|
||||||
|
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
|
||||||
|
return new MqttChannelCommunicationAdapter(channel, serializer, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationChannel CreateMqttCommunicationChannel(IMqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
switch (options)
|
||||||
|
{
|
||||||
|
case MqttClientTcpOptions tcpOptions:
|
||||||
|
return CreateTcpChannel(tcpOptions);
|
||||||
|
case MqttClientWebSocketOptions webSocketOptions:
|
||||||
|
return CreateWebSocketChannel(webSocketOptions);
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTcpChannel CreateTcpChannel(MqttClientTcpOptions tcpOptions)
|
||||||
|
{
|
||||||
|
return new MqttTcpChannel(tcpOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttWebSocketChannel CreateWebSocketChannel(MqttClientWebSocketOptions webSocketOptions)
|
||||||
|
{
|
||||||
|
return new MqttWebSocketChannel(webSocketOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttPacketSerializer CreateSerializer(MqttProtocolVersion protocolVersion)
|
||||||
|
{
|
||||||
|
return new MqttPacketSerializer()
|
||||||
|
{
|
||||||
|
ProtocolVersion = protocolVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttClientSession CreateClientSession(string sessionId, MqttClientSessionsManager mqttClientSessionsManager)
|
||||||
|
{
|
||||||
|
return new MqttClientSession(sessionId, mqttClientSessionsManager, _serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(), _serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttClient CreateMqttClient()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<IMqttClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedMqttClient CreateManagedMqttClient()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<ManagedMqttClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttServer CreateMqttServer()
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<IMqttServer>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttServerFactory : IMqttServerFactory
|
|
||||||
{
|
|
||||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,5 +10,5 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
[assembly: AssemblyVersion("2.4.0.0")]
|
[assembly: AssemblyVersion("2.5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("2.4.0.0")]
|
[assembly: AssemblyFileVersion("2.5.0.0")]
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.ManagedClient;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Core.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddMqttServer(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddOptions();
|
||||||
|
services.AddSingleton<MqttFactory>();
|
||||||
|
services.AddSingleton<IMqttCommunicationAdapterFactory, MqttFactory>();
|
||||||
|
services.AddSingleton<IMqttClientSesssionFactory, MqttFactory>();
|
||||||
|
|
||||||
|
|
||||||
|
services.AddSingleton<IMqttServer,MqttServer>();
|
||||||
|
services.AddSingleton<MqttServer>();
|
||||||
|
|
||||||
|
services.AddTransient<IMqttServerAdapter, MqttServerAdapter>();
|
||||||
|
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
|
||||||
|
|
||||||
|
services.AddTransient<MqttClientSessionsManager>();
|
||||||
|
services.AddTransient<MqttClientRetainedMessagesManager>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMqttServer(this IServiceCollection services, Action<MqttServerOptions> configureOptions)
|
||||||
|
{
|
||||||
|
return services
|
||||||
|
.AddMqttServer()
|
||||||
|
.Configure(configureOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMqttClient(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<MqttFactory>();
|
||||||
|
services.AddSingleton<IMqttCommunicationAdapterFactory, MqttFactory>();
|
||||||
|
|
||||||
|
services.AddTransient<IMqttClient, MqttClient>();
|
||||||
|
services.AddTransient<MqttClient>();
|
||||||
|
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
|
||||||
|
services.AddTransient<ManagedMqttClient>();
|
||||||
|
services.AddTransient<MqttPacketDispatcher>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILoggerFactory AddMqttTrace(this ILoggerFactory factory)
|
||||||
|
{
|
||||||
|
factory.AddProvider(new MqttNetTrace());
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 Christian
|
Copyright (c) 2017 Christian Kratky
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Adapter
|
||||||
|
{
|
||||||
|
public interface IMqttCommunicationAdapterFactory
|
||||||
|
{
|
||||||
|
IMqttCommunicationAdapter CreateClientMqttCommunicationAdapter(IMqttClientOptions options);
|
||||||
|
|
||||||
|
IMqttCommunicationAdapter CreateServerMqttCommunicationAdapter(IMqttCommunicationChannel channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace MQTTnet.Core.Adapter
|
|||||||
{
|
{
|
||||||
public interface IMqttServerAdapter
|
public interface IMqttServerAdapter
|
||||||
{
|
{
|
||||||
event Action<IMqttCommunicationAdapter> ClientAccepted;
|
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||||
|
|
||||||
Task StartAsync(MqttServerOptions options);
|
Task StartAsync(MqttServerOptions options);
|
||||||
Task StopAsync();
|
Task StopAsync();
|
||||||
|
|||||||
@@ -4,21 +4,23 @@ using System.IO;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Channel;
|
using MQTTnet.Core.Channel;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Internal;
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Adapter
|
namespace MQTTnet.Core.Adapter
|
||||||
{
|
{
|
||||||
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<MqttChannelCommunicationAdapter> _logger;
|
||||||
private readonly IMqttCommunicationChannel _channel;
|
private readonly IMqttCommunicationChannel _channel;
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer)
|
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer, ILogger<MqttChannelCommunicationAdapter> logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||||
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
||||||
}
|
}
|
||||||
@@ -94,7 +96,7 @@ namespace MQTTnet.Core.Adapter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttNetTrace.Information(nameof(MqttChannelCommunicationAdapter), "TX >>> {0} [Timeout={1}]", packet, timeout);
|
_logger.LogInformation("TX >>> {0} [Timeout={1}]", packet, timeout);
|
||||||
|
|
||||||
var writeBuffer = PacketSerializer.Serialize(packet);
|
var writeBuffer = PacketSerializer.Serialize(packet);
|
||||||
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
|
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
@@ -160,7 +162,7 @@ namespace MQTTnet.Core.Adapter
|
|||||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttNetTrace.Information(nameof(MqttChannelCommunicationAdapter), "RX <<< {0}", packet);
|
_logger.LogInformation("RX <<< {0}", packet);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Adapter
|
||||||
|
{
|
||||||
|
public class MqttServerAdapterClientAcceptedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MqttServerAdapterClientAcceptedEventArgs(IMqttCommunicationAdapter client)
|
||||||
|
{
|
||||||
|
Client = client ?? throw new ArgumentNullException(nameof(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter Client { get; }
|
||||||
|
|
||||||
|
public Task SessionTask { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -3,7 +3,7 @@ using MQTTnet.Core.Serializer;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public abstract class MqttClientOptions
|
public abstract class BaseMqttClientOptions : IMqttClientOptions
|
||||||
{
|
{
|
||||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ namespace MQTTnet.Core.Client
|
|||||||
|
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
public string ClientId { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty);
|
public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
|
||||||
|
|
||||||
public bool CleanSession { get; set; } = true;
|
public bool CleanSession { get; set; } = true;
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public interface IApplicationMessageReceiver
|
||||||
|
{
|
||||||
|
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,14 @@ using MQTTnet.Core.Packets;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public interface IMqttClient
|
public interface IMqttClient : IApplicationMessageReceiver
|
||||||
{
|
{
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
|
||||||
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||||
event EventHandler Connected;
|
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||||
event EventHandler Disconnected;
|
|
||||||
|
|
||||||
Task ConnectAsync(MqttClientOptions options);
|
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options);
|
||||||
Task DisconnectAsync();
|
Task DisconnectAsync();
|
||||||
|
|
||||||
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
namespace MQTTnet.Core.Client
|
using MQTTnet.Core.ManagedClient;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public interface IMqttClientFactory
|
public interface IMqttClientFactory
|
||||||
{
|
{
|
||||||
IMqttClient CreateMqttClient();
|
IMqttClient CreateMqttClient();
|
||||||
|
|
||||||
|
ManagedMqttClient CreateManagedMqttClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public interface IMqttClientOptions
|
||||||
|
{
|
||||||
|
bool CleanSession { get; }
|
||||||
|
string ClientId { get; }
|
||||||
|
TimeSpan DefaultCommunicationTimeout { get; }
|
||||||
|
TimeSpan KeepAlivePeriod { get; }
|
||||||
|
string Password { get; }
|
||||||
|
MqttProtocolVersion ProtocolVersion { get; }
|
||||||
|
MqttClientTlsOptions TlsOptions { get; }
|
||||||
|
string UserName { get; }
|
||||||
|
MqttApplicationMessage WillMessage { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using MQTTnet.Core.Adapter;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
|
||||||
{
|
|
||||||
public interface IMqttCommunicationAdapterFactory
|
|
||||||
{
|
|
||||||
IMqttCommunicationAdapter CreateMqttCommunicationAdapter(MqttClientOptions options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,38 +4,42 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Internal;
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttClient : IMqttClient
|
public class MqttClient : IMqttClient
|
||||||
{
|
{
|
||||||
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||||
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
|
private readonly MqttPacketDispatcher _packetDispatcher;
|
||||||
private readonly IMqttCommunicationAdapterFactory _communicationChannelFactory;
|
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
|
||||||
|
private readonly ILogger<MqttClient> _logger;
|
||||||
|
|
||||||
private MqttClientOptions _options;
|
private IMqttClientOptions _options;
|
||||||
private bool _isReceivingPackets;
|
private bool _isReceivingPackets;
|
||||||
private int _latestPacketIdentifier;
|
private int _latestPacketIdentifier;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private IMqttCommunicationAdapter _adapter;
|
private IMqttCommunicationAdapter _adapter;
|
||||||
|
private IDisposable _scopeHandle;
|
||||||
|
|
||||||
public MqttClient(IMqttCommunicationAdapterFactory communicationChannelFactory)
|
public MqttClient(IMqttCommunicationAdapterFactory communicationAdapterFactory, ILogger<MqttClient> logger, MqttPacketDispatcher packetDispatcher)
|
||||||
{
|
{
|
||||||
_communicationChannelFactory = communicationChannelFactory ?? throw new ArgumentNullException(nameof(communicationChannelFactory));
|
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_packetDispatcher = packetDispatcher ?? throw new ArgumentNullException(nameof(packetDispatcher));
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler Connected;
|
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||||
public event EventHandler Disconnected;
|
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
public bool IsConnected => _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested;
|
public bool IsConnected { get; private set; }
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options)
|
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
@@ -48,23 +52,26 @@ namespace MQTTnet.Core.Client
|
|||||||
_latestPacketIdentifier = 0;
|
_latestPacketIdentifier = 0;
|
||||||
_packetDispatcher.Reset();
|
_packetDispatcher.Reset();
|
||||||
|
|
||||||
_adapter = _communicationChannelFactory.CreateMqttCommunicationAdapter(options);
|
_adapter = _communicationAdapterFactory.CreateClientMqttCommunicationAdapter(options);
|
||||||
|
|
||||||
MqttNetTrace.Verbose(nameof(MqttClient), "Trying to connect with server.");
|
_scopeHandle = _logger.BeginScope(options.ClientId);
|
||||||
|
_logger.LogTrace("Trying to connect with server.");
|
||||||
await _adapter.ConnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
await _adapter.ConnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||||
MqttNetTrace.Verbose(nameof(MqttClient), "Connection with server established.");
|
_logger.LogTrace("Connection with server established.");
|
||||||
|
|
||||||
await SetupIncomingPacketProcessingAsync();
|
await SetupIncomingPacketProcessingAsync();
|
||||||
await AuthenticateAsync(options.WillMessage);
|
var connectResponse = await AuthenticateAsync(options.WillMessage);
|
||||||
|
|
||||||
MqttNetTrace.Verbose(nameof(MqttClient), "MQTT connection with server established.");
|
_logger.LogTrace("MQTT connection with server established.");
|
||||||
|
|
||||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
StartSendKeepAliveMessages(_cancellationTokenSource.Token);
|
StartSendKeepAliveMessages(_cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connected?.Invoke(this, EventArgs.Empty);
|
IsConnected = true;
|
||||||
|
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
|
||||||
|
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -87,6 +94,7 @@ namespace MQTTnet.Core.Client
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||||
|
_scopeHandle.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +181,7 @@ namespace MQTTnet.Core.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
|
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
|
||||||
{
|
{
|
||||||
var connectPacket = new MqttConnectPacket
|
var connectPacket = new MqttConnectPacket
|
||||||
{
|
{
|
||||||
@@ -190,6 +198,8 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetupIncomingPacketProcessingAsync()
|
private async Task SetupIncomingPacketProcessingAsync()
|
||||||
@@ -222,6 +232,9 @@ namespace MQTTnet.Core.Client
|
|||||||
|
|
||||||
private async Task DisconnectInternalAsync()
|
private async Task DisconnectInternalAsync()
|
||||||
{
|
{
|
||||||
|
var clientWasConnected = IsConnected;
|
||||||
|
IsConnected = false;
|
||||||
|
|
||||||
var cts = _cancellationTokenSource;
|
var cts = _cancellationTokenSource;
|
||||||
if (cts == null || cts.IsCancellationRequested)
|
if (cts == null || cts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -235,15 +248,15 @@ namespace MQTTnet.Core.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Disconnected from adapter.");
|
_logger.LogInformation("Disconnected from adapter.");
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClient), exception, "Error while disconnecting from adapter.");
|
_logger.LogWarning(new EventId(), exception, "Error while disconnecting from adapter.");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Disconnected?.Invoke(this, EventArgs.Empty);
|
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +264,7 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Received <<< {0}", packet);
|
_logger.LogInformation("Received <<< {0}", packet);
|
||||||
|
|
||||||
if (packet is MqttPingReqPacket)
|
if (packet is MqttPingReqPacket)
|
||||||
{
|
{
|
||||||
@@ -281,7 +294,7 @@ namespace MQTTnet.Core.Client
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while processing received packet.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while processing received packet.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +307,7 @@ namespace MQTTnet.Core.Client
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while handling application message.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while handling application message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +371,7 @@ namespace MQTTnet.Core.Client
|
|||||||
|
|
||||||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Start sending keep alive packets.");
|
_logger.LogInformation("Start sending keep alive packets.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -383,23 +396,23 @@ namespace MQTTnet.Core.Client
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttNetTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
||||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClient), exception, "Unhandled exception while sending/receiving keep alive packets.");
|
_logger.LogWarning(new EventId(), exception, "Unhandled exception while sending/receiving keep alive packets.");
|
||||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Stopped sending keep alive packets.");
|
_logger.LogInformation("Stopped sending keep alive packets.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReceivePackets(CancellationToken cancellationToken)
|
private async Task ReceivePackets(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Start receiving packets.");
|
_logger.LogInformation("Start receiving packets.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -426,17 +439,17 @@ namespace MQTTnet.Core.Client
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttNetTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while receiving packets.");
|
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while receiving packets.");
|
||||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while receiving packets.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while receiving packets.");
|
||||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttClient), "Stopped receiving packets.");
|
_logger.LogInformation(nameof(MqttClient), "Stopped receiving packets.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public class MqttClientConnectResult
|
||||||
|
{
|
||||||
|
public MqttClientConnectResult(bool isSessionPresent)
|
||||||
|
{
|
||||||
|
IsSessionPresent = isSessionPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSessionPresent { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public class MqttClientConnectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MqttClientConnectedEventArgs(bool isSessionPresent)
|
||||||
|
{
|
||||||
|
IsSessionPresent = isSessionPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSessionPresent { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public class MqttClientDisconnectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MqttClientDisconnectedEventArgs(bool clientWasConnected)
|
||||||
|
{
|
||||||
|
ClientWasConnected = clientWasConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClientWasConnected { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttClientTcpOptions : MqttClientOptions
|
public class MqttClientTcpOptions : BaseMqttClientOptions
|
||||||
{
|
{
|
||||||
public string Server { get; set; }
|
public string Server { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
namespace MQTTnet.Core.Client
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttClientWebSocketOptions : MqttClientOptions
|
public class MqttClientWebSocketOptions : BaseMqttClientOptions
|
||||||
{
|
{
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
|
|
||||||
|
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||||
|
|
||||||
|
public ICollection<string> SubProtocols { get; set; }
|
||||||
|
|
||||||
|
public CookieContainer CookieContainer { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Internal;
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using System.Collections.Concurrent;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
@@ -12,6 +12,12 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>();
|
private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>();
|
||||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
|
||||||
|
private readonly ILogger<MqttPacketDispatcher> _logger;
|
||||||
|
|
||||||
|
public MqttPacketDispatcher(ILogger<MqttPacketDispatcher> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout)
|
public async Task<MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
@@ -24,7 +30,7 @@ namespace MQTTnet.Core.Client
|
|||||||
}
|
}
|
||||||
catch (MqttCommunicationTimedOutException)
|
catch (MqttCommunicationTimedOutException)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttPacketDispatcher), "Timeout while waiting for packet of type '{0}'.", responseType.Name);
|
_logger.LogWarning("Timeout while waiting for packet of type '{0}'.", responseType.Name);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Diagnostics
|
||||||
|
{
|
||||||
|
public class MqttNetLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly string _categoryName;
|
||||||
|
private readonly MqttNetTrace _mqttNetTrace;
|
||||||
|
|
||||||
|
public MqttNetLogger(string categoryName, MqttNetTrace mqttNetTrace)
|
||||||
|
{
|
||||||
|
_categoryName = categoryName;
|
||||||
|
_mqttNetTrace = mqttNetTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
if (formatter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = formatter(state, exception);
|
||||||
|
|
||||||
|
var traceMessage = new MqttNetTraceMessage(DateTime.Now, Environment.CurrentManagedThreadId, _categoryName, logLevel, message, exception);
|
||||||
|
_mqttNetTrace.Publish(traceMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//not supported: async local requires netstandard1.3
|
||||||
|
//for implementation see https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Console/ConsoleLogScope.cs
|
||||||
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
{
|
||||||
|
return new DisposableScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisposableScope : IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +1,33 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Diagnostics
|
namespace MQTTnet.Core.Diagnostics
|
||||||
{
|
{
|
||||||
public static class MqttNetTrace
|
public class MqttNetTrace : ILoggerProvider
|
||||||
{
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, MqttNetLogger> _loggers = new ConcurrentDictionary<string, MqttNetLogger>();
|
||||||
|
|
||||||
public static event EventHandler<MqttNetTraceMessagePublishedEventArgs> TraceMessagePublished;
|
public static event EventHandler<MqttNetTraceMessagePublishedEventArgs> TraceMessagePublished;
|
||||||
|
|
||||||
public static void Verbose(string source, string message, params object[] parameters)
|
public void Publish(MqttNetTraceMessage msg)
|
||||||
{
|
{
|
||||||
Publish(source, MqttNetTraceLevel.Verbose, null, message, parameters);
|
TraceMessagePublished?.Invoke(this, new MqttNetTraceMessagePublishedEventArgs(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Information(string source, string message, params object[] parameters)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Publish(source, MqttNetTraceLevel.Information, null, message, parameters);
|
TraceMessagePublished = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Warning(string source, string message, params object[] parameters)
|
public ILogger CreateLogger(string categoryName)
|
||||||
{
|
{
|
||||||
Publish(source, MqttNetTraceLevel.Warning, null, message, parameters);
|
return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Warning(string source, Exception exception, string message, params object[] parameters)
|
private MqttNetLogger CreateLoggerImplementation(string categoryName)
|
||||||
{
|
{
|
||||||
Publish(source, MqttNetTraceLevel.Warning, exception, message, parameters);
|
return new MqttNetLogger(categoryName, this);
|
||||||
}
|
|
||||||
|
|
||||||
public static void Error(string source, string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish(source, MqttNetTraceLevel.Error, null, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Error(string source, Exception exception, string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish(source, MqttNetTraceLevel.Error, exception, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Publish(string source, MqttNetTraceLevel traceLevel, Exception exception, string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
var handler = TraceMessagePublished;
|
|
||||||
if (handler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters?.Length > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
message = string.Format(message, parameters);
|
|
||||||
}
|
|
||||||
catch (Exception formatException)
|
|
||||||
{
|
|
||||||
Error(nameof(MqttNetTrace), formatException, "Error while tracing message: " + message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.Invoke(null, new MqttNetTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, traceLevel, message, exception));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Core.Diagnostics
|
|
||||||
{
|
|
||||||
public enum MqttNetTraceLevel
|
|
||||||
{
|
|
||||||
Verbose,
|
|
||||||
Information,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,23 +4,11 @@ namespace MQTTnet.Core.Diagnostics
|
|||||||
{
|
{
|
||||||
public sealed class MqttNetTraceMessagePublishedEventArgs : EventArgs
|
public sealed class MqttNetTraceMessagePublishedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttNetTraceMessagePublishedEventArgs(int threadId, string source, MqttNetTraceLevel level, string message, Exception exception)
|
public MqttNetTraceMessagePublishedEventArgs(MqttNetTraceMessage traceMessage)
|
||||||
{
|
{
|
||||||
ThreadId = threadId;
|
TraceMessage = traceMessage ?? throw new ArgumentNullException(nameof(traceMessage));
|
||||||
Source = source;
|
|
||||||
Level = level;
|
|
||||||
Message = message;
|
|
||||||
Exception = exception;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ThreadId { get; }
|
public MqttNetTraceMessage TraceMessage { get; }
|
||||||
|
|
||||||
public string Source { get; }
|
|
||||||
|
|
||||||
public MqttNetTraceLevel Level { get; }
|
|
||||||
|
|
||||||
public string Message { get; }
|
|
||||||
|
|
||||||
public Exception Exception { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class MqttNetTraceMessage
|
||||||
|
{
|
||||||
|
public MqttNetTraceMessage(DateTime timestamp, int threadId, string source, LogLevel level, string message, Exception exception)
|
||||||
|
{
|
||||||
|
Timestamp = timestamp;
|
||||||
|
ThreadId = threadId;
|
||||||
|
Source = source;
|
||||||
|
Level = level;
|
||||||
|
Message = message;
|
||||||
|
Exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime Timestamp { get; }
|
||||||
|
|
||||||
|
public int ThreadId { get; }
|
||||||
|
|
||||||
|
public string Source { get; }
|
||||||
|
|
||||||
|
public LogLevel Level { get; }
|
||||||
|
|
||||||
|
public string Message { get; }
|
||||||
|
|
||||||
|
public Exception Exception { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,10 +17,5 @@ namespace MQTTnet.Core.Exceptions
|
|||||||
: base(message)
|
: base(message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttCommunicationException(string message, Exception innerException)
|
|
||||||
: base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ namespace MQTTnet.Core.Internal
|
|||||||
{
|
{
|
||||||
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
|
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
return new MqttApplicationMessage(
|
return new MqttApplicationMessage
|
||||||
publishPacket.Topic,
|
{
|
||||||
publishPacket.Payload,
|
Topic = publishPacket.Topic,
|
||||||
publishPacket.QualityOfServiceLevel,
|
Payload = publishPacket.Payload,
|
||||||
publishPacket.Retain
|
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||||
);
|
Retain = publishPacket.Retain
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
||||||
|
|||||||
@@ -17,19 +17,15 @@
|
|||||||
<PackageIconUrl></PackageIconUrl>
|
<PackageIconUrl></PackageIconUrl>
|
||||||
<RepositoryUrl></RepositoryUrl>
|
<RepositoryUrl></RepositoryUrl>
|
||||||
<PackageTags></PackageTags>
|
<PackageTags></PackageTags>
|
||||||
<FileVersion>2.4.0.0</FileVersion>
|
<FileVersion>2.5.0.0</FileVersion>
|
||||||
<AssemblyVersion>2.4.0.0</AssemblyVersion>
|
<AssemblyVersion>2.5.0.0</AssemblyVersion>
|
||||||
<PackageLicenseUrl></PackageLicenseUrl>
|
<PackageLicenseUrl></PackageLicenseUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x64'" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
|
||||||
|
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" />
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x86'" />
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public interface IManagedMqttClientOptions
|
||||||
|
{
|
||||||
|
IMqttClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
|
TimeSpan AutoReconnectDelay { get; }
|
||||||
|
|
||||||
|
IManagedMqttClientStorage Storage { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public interface IManagedMqttClientStorage
|
||||||
|
{
|
||||||
|
Task SaveQueuedMessagesAsync(IList<MqttApplicationMessage> messages);
|
||||||
|
|
||||||
|
Task<IList<MqttApplicationMessage>> LoadQueuedMessagesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
using MQTTnet.Core.Packets;
|
||||||
|
using MQTTnet.Core.Protocol;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public class ManagedMqttClient : IApplicationMessageReceiver
|
||||||
|
{
|
||||||
|
private readonly ManagedMqttClientStorageManager _storageManager = new ManagedMqttClientStorageManager();
|
||||||
|
private readonly BlockingCollection<MqttApplicationMessage> _messageQueue = new BlockingCollection<MqttApplicationMessage>();
|
||||||
|
private readonly HashSet<TopicFilter> _subscriptions = new HashSet<TopicFilter>();
|
||||||
|
|
||||||
|
private readonly IMqttClient _mqttClient;
|
||||||
|
private readonly ILogger<ManagedMqttClient> _logger;
|
||||||
|
|
||||||
|
private CancellationTokenSource _connectionCancellationToken;
|
||||||
|
private CancellationTokenSource _publishingCancellationToken;
|
||||||
|
|
||||||
|
private IManagedMqttClientOptions _options;
|
||||||
|
private bool _subscriptionsNotPushed;
|
||||||
|
|
||||||
|
public ManagedMqttClient(ILogger<ManagedMqttClient> logger, IMqttClient mqttClient)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||||
|
|
||||||
|
_mqttClient.Connected += OnConnected;
|
||||||
|
_mqttClient.Disconnected += OnDisconnected;
|
||||||
|
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsConnected => _mqttClient.IsConnected;
|
||||||
|
|
||||||
|
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||||
|
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||||
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
|
public async Task StartAsync(IManagedMqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
|
||||||
|
|
||||||
|
if (!options.ClientOptions.CleanSession)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("The managed client does not support existing sessions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_connectionCancellationToken != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The managed client is already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_options = options;
|
||||||
|
await _storageManager.SetStorageAsync(_options.Storage).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (_options.Storage != null)
|
||||||
|
{
|
||||||
|
var loadedMessages = await _options.Storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
||||||
|
foreach (var loadedMessage in loadedMessages)
|
||||||
|
{
|
||||||
|
_messageQueue.Add(loadedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionCancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
Task.Factory.StartNew(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
|
||||||
|
_logger.LogInformation("Started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync()
|
||||||
|
{
|
||||||
|
_connectionCancellationToken?.Cancel(false);
|
||||||
|
_connectionCancellationToken = null;
|
||||||
|
|
||||||
|
while (_messageQueue.Any())
|
||||||
|
{
|
||||||
|
_messageQueue.Take();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task EnqueueAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||||
|
{
|
||||||
|
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||||
|
|
||||||
|
foreach (var applicationMessage in applicationMessages)
|
||||||
|
{
|
||||||
|
_messageQueue.Add(applicationMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||||
|
{
|
||||||
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
|
lock (_subscriptions)
|
||||||
|
{
|
||||||
|
foreach (var topicFilter in topicFilters)
|
||||||
|
{
|
||||||
|
if (_subscriptions.Add(topicFilter))
|
||||||
|
{
|
||||||
|
_subscriptionsNotPushed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||||
|
{
|
||||||
|
lock (_subscriptions)
|
||||||
|
{
|
||||||
|
foreach (var topicFilter in topicFilters)
|
||||||
|
{
|
||||||
|
if (_subscriptions.Remove(topicFilter))
|
||||||
|
{
|
||||||
|
_subscriptionsNotPushed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
|
||||||
|
if (connectionState == ReconnectionResult.NotConnected)
|
||||||
|
{
|
||||||
|
_publishingCancellationToken?.Cancel(false);
|
||||||
|
_publishingCancellationToken = null;
|
||||||
|
|
||||||
|
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
|
||||||
|
{
|
||||||
|
await PushSubscriptionsAsync();
|
||||||
|
|
||||||
|
_publishingCancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
Task.Factory.StartNew(() => PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionState == ReconnectionResult.StillConnected)
|
||||||
|
{
|
||||||
|
await Task.Delay(100, _connectionCancellationToken.Token).ConfigureAwait(false); // Consider using the _Disconnected_ event here. (TaskCompletionSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (MqttCommunicationException exception)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(new EventId(), exception, "Communication exception while maintaining connection.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(new EventId(), exception, "Unhandled exception while maintaining connection.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||||
|
_logger.LogInformation("Stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var message = _messageQueue.Take(cancellationToken);
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Stopped publishing messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TryPublishQueuedMessageAsync(MqttApplicationMessage message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _mqttClient.PublishAsync(message).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (MqttCommunicationException exception)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(new EventId(), exception, "Publishing application message failed.");
|
||||||
|
|
||||||
|
if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
|
{
|
||||||
|
_messageQueue.Add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(new EventId(), exception, "Unhandled exception while publishing queued application message.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushSubscriptionsAsync()
|
||||||
|
{
|
||||||
|
_logger.LogInformation(nameof(ManagedMqttClient), "Synchronizing subscriptions");
|
||||||
|
|
||||||
|
List<TopicFilter> subscriptions;
|
||||||
|
lock (_subscriptions)
|
||||||
|
{
|
||||||
|
subscriptions = _subscriptions.ToList();
|
||||||
|
_subscriptionsNotPushed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_subscriptions.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(new EventId(), exception, "Synchronizing subscriptions failed");
|
||||||
|
_subscriptionsNotPushed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ReconnectionResult> ReconnectIfRequiredAsync()
|
||||||
|
{
|
||||||
|
if (_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
return ReconnectionResult.StillConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _mqttClient.ConnectAsync(_options.ClientOptions).ConfigureAwait(false);
|
||||||
|
return ReconnectionResult.Reconnected;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return ReconnectionResult.NotConnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
Disconnected?.Invoke(this, eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
Connected?.Invoke(this, eventArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Packets;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public static class ManagedMqttClientExtensions
|
||||||
|
{
|
||||||
|
public static Task EnqueueAsync(this ManagedMqttClient managedClient, params MqttApplicationMessage[] applicationMessages)
|
||||||
|
{
|
||||||
|
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
|
||||||
|
return managedClient.EnqueueAsync(applicationMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task SubscribeAsync(this ManagedMqttClient managedClient, params TopicFilter[] topicFilters)
|
||||||
|
{
|
||||||
|
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
|
||||||
|
return managedClient.SubscribeAsync(topicFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task UnsubscribeAsync(this ManagedMqttClient managedClient, params TopicFilter[] topicFilters)
|
||||||
|
{
|
||||||
|
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
|
||||||
|
return managedClient.UnsubscribeAsync(topicFilters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public class ManagedMqttClientOptions : IManagedMqttClientOptions
|
||||||
|
{
|
||||||
|
public IMqttClientOptions ClientOptions { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
public IManagedMqttClientStorage Storage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public class ManagedMqttClientStorageManager
|
||||||
|
{
|
||||||
|
private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>();
|
||||||
|
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||||
|
private IManagedMqttClientStorage _storage;
|
||||||
|
|
||||||
|
public async Task SetStorageAsync(IManagedMqttClientStorage storage)
|
||||||
|
{
|
||||||
|
await _semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_storage = storage;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddAsync(MqttApplicationMessage applicationMessage)
|
||||||
|
{
|
||||||
|
await _semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_storage == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_applicationMessages.Add(applicationMessage);
|
||||||
|
await SaveAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveAsync(MqttApplicationMessage applicationMessage)
|
||||||
|
{
|
||||||
|
await _semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_storage == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = _applicationMessages.IndexOf(applicationMessage);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_applicationMessages.RemoveAt(index);
|
||||||
|
await SaveAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task SaveAsync()
|
||||||
|
{
|
||||||
|
return _storage.SaveQueuedMessagesAsync(_applicationMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace MQTTnet.Core.ManagedClient
|
||||||
|
{
|
||||||
|
public enum ReconnectionResult
|
||||||
|
{
|
||||||
|
StillConnected,
|
||||||
|
Reconnected,
|
||||||
|
NotConnected
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,11 @@ namespace MQTTnet.Core
|
|||||||
{
|
{
|
||||||
public sealed class MqttApplicationMessage
|
public sealed class MqttApplicationMessage
|
||||||
{
|
{
|
||||||
|
public MqttApplicationMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use object initializer or _MqttApplicationMessageBuilder_ instead.")]
|
||||||
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||||
{
|
{
|
||||||
Topic = topic ?? throw new ArgumentNullException(nameof(topic));
|
Topic = topic ?? throw new ArgumentNullException(nameof(topic));
|
||||||
@@ -13,12 +18,12 @@ namespace MQTTnet.Core
|
|||||||
Retain = retain;
|
Retain = retain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Topic { get; }
|
public string Topic { get; set; }
|
||||||
|
|
||||||
public byte[] Payload { get; }
|
public byte[] Payload { get; set; }
|
||||||
|
|
||||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
|
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||||
|
|
||||||
public bool Retain { get; }
|
public bool Retain { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
using MQTTnet.Core.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core
|
||||||
|
{
|
||||||
|
public class MqttApplicationMessageBuilder
|
||||||
|
{
|
||||||
|
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||||
|
private string _topic;
|
||||||
|
private byte[] _payload;
|
||||||
|
private bool _retain;
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithTopic(string topic)
|
||||||
|
{
|
||||||
|
_topic = topic;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithPayload(IEnumerable<byte> payload)
|
||||||
|
{
|
||||||
|
if (payload == null)
|
||||||
|
{
|
||||||
|
_payload = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_payload = payload.ToArray();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithPayload(MemoryStream payload)
|
||||||
|
{
|
||||||
|
if (payload == null)
|
||||||
|
{
|
||||||
|
_payload = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.Length == 0)
|
||||||
|
{
|
||||||
|
_payload = new byte[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_payload = new byte[payload.Length - payload.Position];
|
||||||
|
payload.Read(_payload, 0, _payload.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithPayload(string payload)
|
||||||
|
{
|
||||||
|
if (payload == null)
|
||||||
|
{
|
||||||
|
_payload = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_payload = string.IsNullOrEmpty(payload) ? new byte[0] : Encoding.UTF8.GetBytes(payload);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = qualityOfServiceLevel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithRetainFlag(bool value = true)
|
||||||
|
{
|
||||||
|
_retain = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithAtLeastOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithAtMostOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithExactlyOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessage Build()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_topic))
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException("Topic is not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = _topic,
|
||||||
|
Payload = _payload ?? new byte[0],
|
||||||
|
QualityOfServiceLevel = _qualityOfServiceLevel,
|
||||||
|
Retain = _retain
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using MQTTnet.Core.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core
|
|
||||||
{
|
|
||||||
public class MqttApplicationMessageFactory
|
|
||||||
{
|
|
||||||
public MqttApplicationMessage CreateApplicationMessage(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
|
||||||
{
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return CreateApplicationMessage(topic, new byte[0], qualityOfServiceLevel, retain);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessage CreateApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
|
||||||
{
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
if (payload == null)
|
|
||||||
{
|
|
||||||
payload = new byte[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MqttApplicationMessage(topic, payload, qualityOfServiceLevel, retain);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessage CreateApplicationMessage<TPayload>(string topic, TPayload payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
|
||||||
{
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
var payloadString = Convert.ToString(payload, CultureInfo.InvariantCulture);
|
|
||||||
var payloadBuffer = string.IsNullOrEmpty(payloadString) ? new byte[0] : Encoding.UTF8.GetBytes(payloadString);
|
|
||||||
|
|
||||||
return CreateApplicationMessage(topic, payloadBuffer, qualityOfServiceLevel, retain);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessage CreateApplicationMessage(string topic, Stream payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
|
||||||
{
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
byte[] payloadBuffer;
|
|
||||||
if (payload == null || payload.Length == 0)
|
|
||||||
{
|
|
||||||
payloadBuffer = new byte[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
payloadBuffer = new byte[payload.Length - payload.Position];
|
|
||||||
payload.Read(payloadBuffer, 0, payloadBuffer.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateApplicationMessage(topic, payloadBuffer, qualityOfServiceLevel, retain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,8 +12,7 @@ namespace MQTTnet.Core.Packets
|
|||||||
|
|
||||||
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
|
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
|
||||||
{
|
{
|
||||||
var requestPacketWithIdentifier = packet as IMqttPacketWithIdentifier;
|
if (!(packet is IMqttPacketWithIdentifier requestPacketWithIdentifier))
|
||||||
if (requestPacketWithIdentifier == null)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
|
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace MQTTnet.Core.Packets
|
|||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; set; } = new List<MqttSubscribeReturnCode>();
|
public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; } = new List<MqttSubscribeReturnCode>();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,81 +36,6 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter writer)
|
|
||||||
{
|
|
||||||
if (packet is MqttConnectPacket connectPacket)
|
|
||||||
{
|
|
||||||
return Serialize(connectPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttConnAckPacket connAckPacket)
|
|
||||||
{
|
|
||||||
return Serialize(connAckPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttDisconnectPacket disconnectPacket)
|
|
||||||
{
|
|
||||||
return Serialize(disconnectPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPingReqPacket pingReqPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pingReqPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPingRespPacket pingRespPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pingRespPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPublishPacket publishPacket)
|
|
||||||
{
|
|
||||||
return Serialize(publishPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPubAckPacket pubAckPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pubAckPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPubRecPacket pubRecPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pubRecPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPubRelPacket pubRelPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pubRelPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPubCompPacket pubCompPacket)
|
|
||||||
{
|
|
||||||
return Serialize(pubCompPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttSubscribePacket subscribePacket)
|
|
||||||
{
|
|
||||||
return Serialize(subscribePacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttSubAckPacket subAckPacket)
|
|
||||||
{
|
|
||||||
return Serialize(subAckPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
|
||||||
{
|
|
||||||
return Serialize(unsubscribePacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttUnsubAckPacket unsubAckPacket)
|
|
||||||
{
|
|
||||||
return Serialize(unsubAckPacket, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MqttProtocolViolationException("Packet type invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
|
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
|
||||||
{
|
{
|
||||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||||
@@ -121,102 +46,90 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter writer)
|
||||||
|
{
|
||||||
|
switch (packet)
|
||||||
|
{
|
||||||
|
case MqttConnectPacket connectPacket: return Serialize(connectPacket, writer);
|
||||||
|
case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, writer);
|
||||||
|
case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
|
||||||
|
case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
|
||||||
|
case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
|
||||||
|
case MqttPublishPacket publishPacket: return Serialize(publishPacket, writer);
|
||||||
|
case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, writer);
|
||||||
|
case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, writer);
|
||||||
|
case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, writer);
|
||||||
|
case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, writer);
|
||||||
|
case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, writer);
|
||||||
|
case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, writer);
|
||||||
|
case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, writer);
|
||||||
|
case MqttUnsubAckPacket unsubAckPacket: return Serialize(unsubAckPacket, writer);
|
||||||
|
default: throw new MqttProtocolViolationException("Packet type invalid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static MqttBasePacket Deserialize(MqttPacketHeader header, MqttPacketReader reader)
|
private static MqttBasePacket Deserialize(MqttPacketHeader header, MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
switch (header.ControlPacketType)
|
switch (header.ControlPacketType)
|
||||||
{
|
{
|
||||||
case MqttControlPacketType.Connect:
|
case MqttControlPacketType.Connect: return DeserializeConnect(reader);
|
||||||
{
|
case MqttControlPacketType.ConnAck: return DeserializeConnAck(reader);
|
||||||
return DeserializeConnect(reader);
|
case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket();
|
||||||
}
|
case MqttControlPacketType.Publish: return DeserializePublish(reader, header);
|
||||||
|
case MqttControlPacketType.PubAck: return DeserializePubAck(reader);
|
||||||
case MqttControlPacketType.ConnAck:
|
case MqttControlPacketType.PubRec: return DeserializePubRec(reader);
|
||||||
{
|
case MqttControlPacketType.PubRel: return DeserializePubRel(reader);
|
||||||
return DeserializeConnAck(reader);
|
case MqttControlPacketType.PubComp: return DeserializePubComp(reader);
|
||||||
}
|
case MqttControlPacketType.PingReq: return new MqttPingReqPacket();
|
||||||
|
case MqttControlPacketType.PingResp: return new MqttPingRespPacket();
|
||||||
case MqttControlPacketType.Disconnect:
|
case MqttControlPacketType.Subscribe: return DeserializeSubscribe(reader);
|
||||||
{
|
case MqttControlPacketType.SubAck: return DeserializeSubAck(reader);
|
||||||
return new MqttDisconnectPacket();
|
case MqttControlPacketType.Unsubscibe: return DeserializeUnsubscribe(reader);
|
||||||
}
|
case MqttControlPacketType.UnsubAck: return DeserializeUnsubAck(reader);
|
||||||
|
default: throw new MqttProtocolViolationException($"Packet type ({(int)header.ControlPacketType}) not supported.");
|
||||||
case MqttControlPacketType.Publish:
|
|
||||||
{
|
|
||||||
return DeserializePublish(reader, header);
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PubAck:
|
|
||||||
{
|
|
||||||
return new MqttPubAckPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = reader.ReadUInt16()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PubRec:
|
|
||||||
{
|
|
||||||
return new MqttPubRecPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = reader.ReadUInt16()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PubRel:
|
|
||||||
{
|
|
||||||
return new MqttPubRelPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = reader.ReadUInt16()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PubComp:
|
|
||||||
{
|
|
||||||
return new MqttPubCompPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = reader.ReadUInt16()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PingReq:
|
|
||||||
{
|
|
||||||
return new MqttPingReqPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.PingResp:
|
|
||||||
{
|
|
||||||
return new MqttPingRespPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.Subscribe:
|
|
||||||
{
|
|
||||||
return DeserializeSubscribe(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.SubAck:
|
|
||||||
{
|
|
||||||
return DeserializeSubAck(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.Unsubscibe:
|
|
||||||
{
|
|
||||||
return DeserializeUnsubscribe(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttControlPacketType.UnsubAck:
|
|
||||||
{
|
|
||||||
return new MqttUnsubAckPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = reader.ReadUInt16()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException($"Packet type ({(int)header.ControlPacketType}) not supported.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MqttBasePacket DeserializeUnsubAck(MqttPacketReader reader)
|
||||||
|
{
|
||||||
|
return new MqttUnsubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = reader.ReadUInt16()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttBasePacket DeserializePubComp(MqttPacketReader reader)
|
||||||
|
{
|
||||||
|
return new MqttPubCompPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = reader.ReadUInt16()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttBasePacket DeserializePubRel(MqttPacketReader reader)
|
||||||
|
{
|
||||||
|
return new MqttPubRelPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = reader.ReadUInt16()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttBasePacket DeserializePubRec(MqttPacketReader reader)
|
||||||
|
{
|
||||||
|
return new MqttPubRecPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = reader.ReadUInt16()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttBasePacket DeserializePubAck(MqttPacketReader reader)
|
||||||
|
{
|
||||||
|
return new MqttPubAckPacket
|
||||||
|
{
|
||||||
|
PacketIdentifier = reader.ReadUInt16()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketReader reader)
|
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var packet = new MqttUnsubscribePacket
|
var packet = new MqttUnsubscribePacket
|
||||||
@@ -297,7 +210,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
throw new MqttProtocolViolationException("Protocol name is not supported.");
|
throw new MqttProtocolViolationException("Protocol name is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocolLevel = reader.ReadByte();
|
reader.ReadByte(); // Skip protocol level
|
||||||
var connectFlags = reader.ReadByte();
|
var connectFlags = reader.ReadByte();
|
||||||
|
|
||||||
var connectFlagsReader = new ByteReader(connectFlags);
|
var connectFlagsReader = new ByteReader(connectFlags);
|
||||||
@@ -320,11 +233,13 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
if (willFlag)
|
if (willFlag)
|
||||||
{
|
{
|
||||||
packet.WillMessage = new MqttApplicationMessage(
|
packet.WillMessage = new MqttApplicationMessage
|
||||||
reader.ReadStringWithLengthPrefix(),
|
{
|
||||||
reader.ReadWithLengthPrefix(),
|
Topic = reader.ReadStringWithLengthPrefix(),
|
||||||
(MqttQualityOfServiceLevel)willQoS,
|
Payload = reader.ReadWithLengthPrefix(),
|
||||||
willRetain);
|
QualityOfServiceLevel = (MqttQualityOfServiceLevel)willQoS,
|
||||||
|
Retain = willRetain
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usernameFlag)
|
if (usernameFlag)
|
||||||
@@ -372,6 +287,8 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
private static void ValidateConnectPacket(MqttConnectPacket packet)
|
private static void ValidateConnectPacket(MqttConnectPacket packet)
|
||||||
{
|
{
|
||||||
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
|
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
|
||||||
@@ -380,6 +297,8 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
private static void ValidatePublishPacket(MqttPublishPacket packet)
|
private static void ValidatePublishPacket(MqttPublishPacket packet)
|
||||||
{
|
{
|
||||||
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
|
|
||||||
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
|
throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
|
||||||
@@ -467,21 +386,6 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte Serialize(MqttDisconnectPacket packet, MqttPacketWriter writer)
|
|
||||||
{
|
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte Serialize(MqttPingReqPacket packet, MqttPacketWriter writer)
|
|
||||||
{
|
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte Serialize(MqttPingRespPacket packet, MqttPacketWriter writer)
|
|
||||||
{
|
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter writer)
|
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter writer)
|
||||||
{
|
{
|
||||||
ValidatePublishPacket(packet);
|
ValidatePublishPacket(packet);
|
||||||
@@ -593,14 +497,13 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte Serialize(MqttUnsubAckPacket packet, MqttPacketWriter writer)
|
private static byte Serialize(IMqttPacketWithIdentifier packet, BinaryWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write(packet.PacketIdentifier);
|
writer.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte SerializeEmptyPacketAsync(MqttControlPacketType type, MqttPacketWriter writer)
|
private static byte SerializeEmptyPacket(MqttControlPacketType type)
|
||||||
{
|
{
|
||||||
return MqttPacketWriter.BuildFixedHeader(type);
|
return MqttPacketWriter.BuildFixedHeader(type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public interface IMqttClientSesssionFactory
|
||||||
|
{
|
||||||
|
MqttClientSession CreateClientSession(string sessionId, MqttClientSessionsManager mqttClientSessionsManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public interface IMqttServer
|
public interface IMqttServer
|
||||||
{
|
{
|
||||||
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
|
||||||
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
||||||
|
|
||||||
IReadOnlyList<ConnectedMqttClient> GetConnectedClients();
|
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
|
IList<ConnectedMqttClient> GetConnectedClients();
|
||||||
void Publish(MqttApplicationMessage applicationMessage);
|
void Publish(MqttApplicationMessage applicationMessage);
|
||||||
|
|
||||||
Task StartAsync();
|
Task StartAsync();
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
public interface IMqttServerFactory
|
public interface IMqttServerFactory
|
||||||
{
|
{
|
||||||
IMqttServer CreateMqttServer(MqttServerOptions options);
|
IMqttServer CreateMqttServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
using MQTTnet.Core.Server;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core.Adapter
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientConnectedEventArgs : EventArgs
|
public class MqttClientConnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
using MQTTnet.Core.Server;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core.Adapter
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
public class MqttClientDisconnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
@@ -3,10 +3,10 @@ using System.Collections.Concurrent;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
@@ -15,9 +15,11 @@ namespace MQTTnet.Core.Server
|
|||||||
private readonly BlockingCollection<MqttPublishPacket> _pendingPublishPackets = new BlockingCollection<MqttPublishPacket>();
|
private readonly BlockingCollection<MqttPublishPacket> _pendingPublishPackets = new BlockingCollection<MqttPublishPacket>();
|
||||||
private readonly MqttClientSession _session;
|
private readonly MqttClientSession _session;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
private readonly ILogger<MqttClientPendingMessagesQueue> _logger;
|
||||||
|
|
||||||
public MqttClientPendingMessagesQueue(MqttServerOptions options, MqttClientSession session)
|
public MqttClientPendingMessagesQueue(MqttServerOptions options, MqttClientSession session, ILogger<MqttClientPendingMessagesQueue> logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
@@ -50,7 +52,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientPendingMessagesQueue), exception, "Unhandled exception while sending pending publish packets.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while sending pending publish packets.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,18 +68,18 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
if (exception is MqttCommunicationTimedOutException)
|
if (exception is MqttCommunicationTimedOutException)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed due to timeout.");
|
_logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to timeout.");
|
||||||
}
|
}
|
||||||
else if (exception is MqttCommunicationException)
|
else if (exception is MqttCommunicationException)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed due to communication exception.");
|
_logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to communication exception.");
|
||||||
}
|
}
|
||||||
if (exception is OperationCanceledException)
|
if (exception is OperationCanceledException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed.");
|
_logger.LogError(new EventId(), exception, "Sending publish packet failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
|
|||||||
@@ -2,20 +2,22 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Internal;
|
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public sealed class MqttClientRetainedMessagesManager
|
public sealed class MqttClientRetainedMessagesManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, MqttPublishPacket> _retainedMessages = new Dictionary<string, MqttPublishPacket>();
|
private readonly Dictionary<string, MqttApplicationMessage> _retainedMessages = new Dictionary<string, MqttApplicationMessage>();
|
||||||
|
private readonly ILogger<MqttClientRetainedMessagesManager> _logger;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
|
||||||
public MqttClientRetainedMessagesManager(MqttServerOptions options)
|
public MqttClientRetainedMessagesManager(IOptions<MqttServerOptions> options, ILogger<MqttClientRetainedMessagesManager> logger)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadMessagesAsync()
|
public async Task LoadMessagesAsync()
|
||||||
@@ -33,35 +35,35 @@ namespace MQTTnet.Core.Server
|
|||||||
_retainedMessages.Clear();
|
_retainedMessages.Clear();
|
||||||
foreach (var retainedMessage in retainedMessages)
|
foreach (var retainedMessage in retainedMessages)
|
||||||
{
|
{
|
||||||
_retainedMessages[retainedMessage.Topic] = retainedMessage.ToPublishPacket();
|
_retainedMessages[retainedMessage.Topic] = retainedMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientRetainedMessagesManager), exception, "Unhandled exception while loading retained messages.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while loading retained messages.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleMessageAsync(string clientId, MqttPublishPacket publishPacket)
|
public async Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
List<MqttPublishPacket> allRetainedMessages;
|
List<MqttApplicationMessage> allRetainedMessages;
|
||||||
lock (_retainedMessages)
|
lock (_retainedMessages)
|
||||||
{
|
{
|
||||||
if (publishPacket.Payload?.Any() == false)
|
if (applicationMessage.Payload?.Any() == false)
|
||||||
{
|
{
|
||||||
_retainedMessages.Remove(publishPacket.Topic);
|
_retainedMessages.Remove(applicationMessage.Topic);
|
||||||
MqttNetTrace.Information(nameof(MqttClientRetainedMessagesManager), "Client '{0}' cleared retained message for topic '{1}'.", clientId, publishPacket.Topic);
|
_logger.LogInformation("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_retainedMessages[publishPacket.Topic] = publishPacket;
|
_retainedMessages[applicationMessage.Topic] = applicationMessage;
|
||||||
MqttNetTrace.Information(nameof(MqttClientRetainedMessagesManager), "Client '{0}' updated retained message for topic '{1}'.", clientId, publishPacket.Topic);
|
_logger.LogInformation("Client '{0}' updated retained message for topic '{1}'.", clientId, applicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
allRetainedMessages = new List<MqttPublishPacket>(_retainedMessages.Values);
|
allRetainedMessages = new List<MqttApplicationMessage>(_retainedMessages.Values);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -69,18 +71,18 @@ namespace MQTTnet.Core.Server
|
|||||||
// ReSharper disable once UseNullPropagation
|
// ReSharper disable once UseNullPropagation
|
||||||
if (_options.Storage != null)
|
if (_options.Storage != null)
|
||||||
{
|
{
|
||||||
await _options.Storage.SaveRetainedMessagesAsync(allRetainedMessages.Select(p => p.ToApplicationMessage()).ToList());
|
await _options.Storage.SaveRetainedMessagesAsync(allRetainedMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientRetainedMessagesManager), exception, "Unhandled exception while saving retained messages.");
|
_logger.LogError(new EventId(), exception, "Unhandled exception while saving retained messages.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MqttPublishPacket> GetMessages(MqttSubscribePacket subscribePacket)
|
public List<MqttApplicationMessage> GetMessages(MqttSubscribePacket subscribePacket)
|
||||||
{
|
{
|
||||||
var retainedMessages = new List<MqttPublishPacket>();
|
var retainedMessages = new List<MqttApplicationMessage>();
|
||||||
lock (_retainedMessages)
|
lock (_retainedMessages)
|
||||||
{
|
{
|
||||||
foreach (var retainedMessage in _retainedMessages.Values)
|
foreach (var retainedMessage in _retainedMessages.Values)
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Internal;
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
@@ -20,17 +20,20 @@ namespace MQTTnet.Core.Server
|
|||||||
private readonly MqttClientSessionsManager _mqttClientSessionsManager;
|
private readonly MqttClientSessionsManager _mqttClientSessionsManager;
|
||||||
private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
|
private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
private readonly ILogger<MqttClientSession> _logger;
|
||||||
|
|
||||||
private IMqttCommunicationAdapter _adapter;
|
private IMqttCommunicationAdapter _adapter;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private MqttApplicationMessage _willMessage;
|
private MqttApplicationMessage _willMessage;
|
||||||
|
|
||||||
public MqttClientSession(string clientId, MqttServerOptions options, MqttClientSessionsManager mqttClientSessionsManager)
|
public MqttClientSession(string clientId, MqttClientSessionsManager mqttClientSessionsManager, ILogger<MqttClientSession> logger, ILogger<MqttClientPendingMessagesQueue> msgQueueLogger)
|
||||||
{
|
{
|
||||||
ClientId = clientId;
|
ClientId = clientId;
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = mqttClientSessionsManager.Options;
|
||||||
_mqttClientSessionsManager = mqttClientSessionsManager ?? throw new ArgumentNullException(nameof(mqttClientSessionsManager));
|
_mqttClientSessionsManager = mqttClientSessionsManager ?? throw new ArgumentNullException(nameof(mqttClientSessionsManager));
|
||||||
_pendingMessagesQueue = new MqttClientPendingMessagesQueue(options, this);
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
_pendingMessagesQueue = new MqttClientPendingMessagesQueue(mqttClientSessionsManager.Options, this, msgQueueLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ClientId { get; }
|
public string ClientId { get; }
|
||||||
@@ -58,11 +61,11 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
catch (MqttCommunicationException exception)
|
catch (MqttCommunicationException exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClientSession), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientSession), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
if (_willMessage != null)
|
if (_willMessage != null)
|
||||||
{
|
{
|
||||||
_mqttClientSessionsManager.DispatchPublishPacket(this, _willMessage.ToPublishPacket());
|
_mqttClientSessionsManager.DispatchApplicationMessage(this, _willMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cancellationTokenSource?.Cancel(false);
|
_cancellationTokenSource?.Cancel(false);
|
||||||
@@ -79,7 +82,7 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
_adapter = null;
|
_adapter = null;
|
||||||
|
|
||||||
MqttNetTrace.Information(nameof(MqttClientSession), "Client '{0}': Disconnected.", ClientId);
|
_logger.LogInformation("Client '{0}': Disconnected.", ClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
|
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
|
||||||
@@ -92,7 +95,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
_pendingMessagesQueue.Enqueue(publishPacket);
|
_pendingMessagesQueue.Enqueue(publishPacket);
|
||||||
MqttNetTrace.Verbose(nameof(MqttClientSession), "Client '{0}': Enqueued pending publish packet.", ClientId);
|
_logger.LogTrace("Client '{0}': Enqueued pending publish packet.", ClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -116,12 +119,12 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
catch (MqttCommunicationException exception)
|
catch (MqttCommunicationException exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClientSession), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientSession), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +166,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MqttNetTrace.Warning(nameof(MqttClientSession), "Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
|
_logger.LogWarning("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,45 +176,54 @@ namespace MQTTnet.Core.Server
|
|||||||
var retainedMessages = _mqttClientSessionsManager.RetainedMessagesManager.GetMessages(subscribePacket);
|
var retainedMessages = _mqttClientSessionsManager.RetainedMessagesManager.GetMessages(subscribePacket);
|
||||||
foreach (var publishPacket in retainedMessages)
|
foreach (var publishPacket in retainedMessages)
|
||||||
{
|
{
|
||||||
EnqueuePublishPacket(publishPacket);
|
EnqueuePublishPacket(publishPacket.ToPublishPacket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleIncomingPublishPacketAsync(IMqttCommunicationAdapter adapter, MqttPublishPacket publishPacket)
|
private async Task HandleIncomingPublishPacketAsync(IMqttCommunicationAdapter adapter, MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (publishPacket.Retain)
|
var applicationMessage = publishPacket.ToApplicationMessage();
|
||||||
|
_options.ApplicationMessageInterceptor?.Invoke(applicationMessage);
|
||||||
|
|
||||||
|
if (applicationMessage.Retain)
|
||||||
{
|
{
|
||||||
await _mqttClientSessionsManager.RetainedMessagesManager.HandleMessageAsync(ClientId, publishPacket);
|
await _mqttClientSessionsManager.RetainedMessagesManager.HandleMessageAsync(ClientId, applicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
switch (applicationMessage.QualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
_mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket);
|
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||||
return;
|
{
|
||||||
|
_mqttClientSessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
|
{
|
||||||
|
_mqttClientSessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
||||||
|
|
||||||
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token,
|
||||||
|
new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||||
|
{
|
||||||
|
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
||||||
|
lock (_unacknowledgedPublishPackets)
|
||||||
|
{
|
||||||
|
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mqttClientSessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
||||||
|
|
||||||
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token,
|
||||||
|
new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
|
||||||
{
|
|
||||||
_mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket);
|
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
|
||||||
{
|
|
||||||
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
|
||||||
lock (_unacknowledgedPublishPackets)
|
|
||||||
{
|
|
||||||
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
_mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket);
|
|
||||||
|
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MqttCommunicationException("Received a not supported QoS level.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task HandleIncomingPubRelPacketAsync(IMqttCommunicationAdapter adapter, MqttPubRelPacket pubRelPacket)
|
private Task HandleIncomingPubRelPacketAsync(IMqttCommunicationAdapter adapter, MqttPubRelPacket pubRelPacket)
|
||||||
|
|||||||
@@ -4,24 +4,28 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
using MQTTnet.Core.Internal;
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public sealed class MqttClientSessionsManager
|
public sealed class MqttClientSessionsManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
||||||
private readonly MqttServerOptions _options;
|
private readonly ILogger<MqttClientSessionsManager> _logger;
|
||||||
|
private readonly IMqttClientSesssionFactory _mqttClientSesssionFactory;
|
||||||
|
|
||||||
public MqttClientSessionsManager(MqttServerOptions options)
|
public MqttClientSessionsManager(IOptions<MqttServerOptions> options, ILogger<MqttClientSessionsManager> logger, MqttClientRetainedMessagesManager retainedMessagesManager, IMqttClientSesssionFactory mqttClientSesssionFactory)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
RetainedMessagesManager = new MqttClientRetainedMessagesManager(options);
|
Options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
RetainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_mqttClientSesssionFactory = mqttClientSesssionFactory ?? throw new ArgumentNullException(nameof(mqttClientSesssionFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
@@ -29,13 +33,14 @@ namespace MQTTnet.Core.Server
|
|||||||
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
||||||
|
|
||||||
public MqttClientRetainedMessagesManager RetainedMessagesManager { get; }
|
public MqttClientRetainedMessagesManager RetainedMessagesManager { get; }
|
||||||
|
public MqttServerOptions Options { get; }
|
||||||
|
|
||||||
public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter)
|
public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter)
|
||||||
{
|
{
|
||||||
var clientId = string.Empty;
|
var clientId = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
if (!(await clientAdapter.ReceivePacketAsync(Options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
||||||
}
|
}
|
||||||
@@ -48,7 +53,7 @@ namespace MQTTnet.Core.Server
|
|||||||
var connectReturnCode = ValidateConnection(connectPacket);
|
var connectReturnCode = ValidateConnection(connectPacket);
|
||||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||||
{
|
{
|
||||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
await clientAdapter.SendPacketsAsync(Options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
||||||
{
|
{
|
||||||
ConnectReturnCode = connectReturnCode
|
ConnectReturnCode = connectReturnCode
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
@@ -58,7 +63,7 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
var clientSession = GetOrCreateClientSession(connectPacket);
|
var clientSession = GetOrCreateClientSession(connectPacket);
|
||||||
|
|
||||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
await clientAdapter.SendPacketsAsync(Options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
||||||
{
|
{
|
||||||
ConnectReturnCode = connectReturnCode,
|
ConnectReturnCode = connectReturnCode,
|
||||||
IsSessionPresent = clientSession.IsExistingSession
|
IsSessionPresent = clientSession.IsExistingSession
|
||||||
@@ -70,17 +75,20 @@ namespace MQTTnet.Core.Server
|
|||||||
ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
|
ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false);
|
using (_logger.BeginScope(clientId))
|
||||||
|
{
|
||||||
|
await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttServer), exception, exception.Message);
|
_logger.LogError(new EventId(), exception, exception.Message);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
await clientAdapter.DisconnectAsync(Options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -103,7 +111,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<ConnectedMqttClient> GetConnectedClients()
|
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||||
{
|
{
|
||||||
lock (_clientSessions)
|
lock (_clientSessions)
|
||||||
{
|
{
|
||||||
@@ -115,32 +123,32 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
public void DispatchApplicationMessage(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage());
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, applicationMessage);
|
||||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message");
|
_logger.LogError(new EventId(), exception, "Error while processing application message");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_clientSessions)
|
lock (_clientSessions)
|
||||||
{
|
{
|
||||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
foreach (var clientSession in _clientSessions.Values.ToList())
|
||||||
{
|
{
|
||||||
clientSession.EnqueuePublishPacket(publishPacket);
|
clientSession.EnqueuePublishPacket(applicationMessage.ToPublishPacket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
||||||
{
|
{
|
||||||
if (_options.ConnectionValidator != null)
|
if (Options.ConnectionValidator != null)
|
||||||
{
|
{
|
||||||
return _options.ConnectionValidator(connectPacket);
|
return Options.ConnectionValidator(connectPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MqttConnectReturnCode.ConnectionAccepted;
|
return MqttConnectReturnCode.ConnectionAccepted;
|
||||||
@@ -158,11 +166,11 @@ namespace MQTTnet.Core.Server
|
|||||||
_clientSessions.Remove(connectPacket.ClientId);
|
_clientSessions.Remove(connectPacket.ClientId);
|
||||||
clientSession.Dispose();
|
clientSession.Dispose();
|
||||||
clientSession = null;
|
clientSession = null;
|
||||||
MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Disposed existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.LogTrace("Disposed existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.LogTrace("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +179,10 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
isExistingSession = false;
|
isExistingSession = false;
|
||||||
|
|
||||||
clientSession = new MqttClientSession(connectPacket.ClientId, _options, this);
|
clientSession = _mqttClientSesssionFactory.CreateClientSession(connectPacket.ClientId, this);
|
||||||
_clientSessions[connectPacket.ClientId] = clientSession;
|
_clientSessions[connectPacket.ClientId] = clientSession;
|
||||||
|
|
||||||
MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Created a new session for client '{0}'.", connectPacket.ClientId);
|
_logger.LogTrace("Created a new session for client '{0}'.", connectPacket.ClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
|
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
|
||||||
|
|||||||
@@ -3,31 +3,39 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
using Microsoft.Extensions.Logging;
|
||||||
using MQTTnet.Core.Internal;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public sealed class MqttServer : IMqttServer
|
public class MqttServer : IMqttServer
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<MqttServer> _logger;
|
||||||
private readonly MqttClientSessionsManager _clientSessionsManager;
|
private readonly MqttClientSessionsManager _clientSessionsManager;
|
||||||
private readonly ICollection<IMqttServerAdapter> _adapters;
|
private readonly ICollection<IMqttServerAdapter> _adapters;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
public MqttServer(MqttServerOptions options, ICollection<IMqttServerAdapter> adapters)
|
public MqttServer(IOptions<MqttServerOptions> options, IEnumerable<IMqttServerAdapter> adapters, ILogger<MqttServer> logger, MqttClientSessionsManager clientSessionsManager)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_adapters = adapters ?? throw new ArgumentNullException(nameof(adapters));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_clientSessionsManager = clientSessionsManager ?? throw new ArgumentNullException(nameof(clientSessionsManager));
|
||||||
|
|
||||||
|
if (adapters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(adapters));
|
||||||
|
}
|
||||||
|
_adapters = adapters.ToList();
|
||||||
|
|
||||||
_clientSessionsManager = new MqttClientSessionsManager(options);
|
|
||||||
_clientSessionsManager.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
|
_clientSessionsManager.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
|
||||||
_clientSessionsManager.ClientConnected += OnClientConnected;
|
_clientSessionsManager.ClientConnected += OnClientConnected;
|
||||||
_clientSessionsManager.ClientDisconnected += OnClientDisconnected;
|
_clientSessionsManager.ClientDisconnected += OnClientDisconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<ConnectedMqttClient> GetConnectedClients()
|
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||||
{
|
{
|
||||||
return _clientSessionsManager.GetConnectedClients();
|
return _clientSessionsManager.GetConnectedClients();
|
||||||
}
|
}
|
||||||
@@ -40,7 +48,8 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
_clientSessionsManager.DispatchPublishPacket(null, applicationMessage.ToPublishPacket());
|
_options.ApplicationMessageInterceptor?.Invoke(applicationMessage);
|
||||||
|
_clientSessionsManager.DispatchApplicationMessage(null, applicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartAsync()
|
public async Task StartAsync()
|
||||||
@@ -57,7 +66,7 @@ namespace MQTTnet.Core.Server
|
|||||||
await adapter.StartAsync(_options);
|
await adapter.StartAsync(_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttNetTrace.Information(nameof(MqttServer), "Started.");
|
_logger.LogInformation("Started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync()
|
public async Task StopAsync()
|
||||||
@@ -74,23 +83,23 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
_clientSessionsManager.Clear();
|
_clientSessionsManager.Clear();
|
||||||
|
|
||||||
MqttNetTrace.Information(nameof(MqttServer), "Stopped.");
|
_logger.LogInformation("Stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClientAccepted(IMqttCommunicationAdapter adapter)
|
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
Task.Run(() =>_clientSessionsManager.RunClientSessionAsync(adapter), _cancellationTokenSource.Token);
|
eventArgs.SessionTask = Task.Run(async () => await _clientSessionsManager.RunClientSessionAsync(eventArgs.Client), _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClientConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
private void OnClientConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttServer), "Client '{0}': Connected.", eventArgs.Client.ClientId);
|
_logger.LogInformation("Client '{0}': Connected.", eventArgs.Client.ClientId);
|
||||||
ClientConnected?.Invoke(this, eventArgs);
|
ClientConnected?.Invoke(this, eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClientDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
|
private void OnClientDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
MqttNetTrace.Information(nameof(MqttServer), "Client '{0}': Disconnected.", eventArgs.Client.ClientId);
|
_logger.LogInformation("Client '{0}': Disconnected.", eventArgs.Client.ClientId);
|
||||||
ClientDisconnected?.Invoke(this, eventArgs);
|
ClientDisconnected?.Invoke(this, eventArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
||||||
|
|
||||||
|
public Func<MqttApplicationMessage, MqttApplicationMessage> ApplicationMessageInterceptor { get; set; }
|
||||||
|
|
||||||
public IMqttServerStorage Storage { get; set; }
|
public IMqttServerStorage Storage { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
public static class MqttTopicFilterComparer
|
public static class MqttTopicFilterComparer
|
||||||
{
|
{
|
||||||
private const char TopicLevelSeparator = '/';
|
private static readonly char[] TopicLevelSeparator = { '/' };
|
||||||
|
|
||||||
public static bool IsMatch(string topic, string filter)
|
public static bool IsMatch(string topic, string filter)
|
||||||
{
|
{
|
||||||
@@ -16,19 +16,15 @@ namespace MQTTnet.Core.Server
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fragmentsTopic = topic.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
var fragmentsTopic = topic.Split(TopicLevelSeparator, StringSplitOptions.None);
|
||||||
var fragmentsFilter = filter.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
var fragmentsFilter = filter.Split(TopicLevelSeparator, StringSplitOptions.None);
|
||||||
|
|
||||||
for (var i = 0; i < fragmentsFilter.Length; i++)
|
for (var i = 0; i < fragmentsFilter.Length; i++)
|
||||||
{
|
{
|
||||||
if (fragmentsFilter[i] == "+")
|
switch (fragmentsFilter[i])
|
||||||
{
|
{
|
||||||
continue;
|
case "+": continue;
|
||||||
}
|
case "#" when i == fragmentsFilter.Length - 1: return true;
|
||||||
|
|
||||||
if (fragmentsFilter[i] == "#" && i == fragmentsFilter.Length - 1)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >= fragmentsTopic.Length)
|
if (i >= fragmentsTopic.Length)
|
||||||
@@ -36,18 +32,13 @@ namespace MQTTnet.Core.Server
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i]))
|
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i], StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fragmentsTopic.Length > fragmentsFilter.Length)
|
return fragmentsTopic.Length <= fragmentsFilter.Length;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core
|
||||||
{
|
{
|
||||||
public sealed class TopicFilter
|
public sealed class TopicFilter
|
||||||
{
|
{
|
||||||
@@ -13,5 +13,15 @@ namespace MQTTnet.Core.Packets
|
|||||||
public string Topic { get; }
|
public string Topic { get; }
|
||||||
|
|
||||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
|
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return QualityOfServiceLevel.GetHashCode() ^ (Topic ?? string.Empty).GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Topic + "@" + QualityOfServiceLevel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using MQTTnet.Core.Protocol;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core
|
||||||
|
{
|
||||||
|
public class TopicFilterBuilder
|
||||||
|
{
|
||||||
|
private string _topic;
|
||||||
|
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||||
|
|
||||||
|
public TopicFilterBuilder WithTopic(string topic)
|
||||||
|
{
|
||||||
|
_topic = topic;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TopicFilterBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = qualityOfServiceLevel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TopicFilterBuilder WithAtLeastOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TopicFilterBuilder WithAtMostOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TopicFilterBuilder WithExactlyOnceQoS()
|
||||||
|
{
|
||||||
|
_qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TopicFilter Build()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_topic))
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException("Topic is not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TopicFilter(_topic, _qualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
-39
@@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26730.16
|
VisualStudioVersion = 15.0.27004.2005
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.NetFramework", "Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj", "{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.UniversalWindows", "Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj", "{BD60C727-D8E8-40C3-B8E3-C95A864AE611}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.UniversalWindows", "Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj", "{BD60C727-D8E8-40C3-B8E3-C95A864AE611}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||||
@@ -15,8 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core", "MQTTnet.Core\MQTTnet.Core.csproj", "{2ECB99E4-72D0-4C23-99BA-93D511D3967D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core", "MQTTnet.Core\MQTTnet.Core.csproj", "{2ECB99E4-72D0-4C23-99BA-93D511D3967D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.NetFramework", "Tests\MQTTnet.TestApp.NetFramework\MQTTnet.TestApp.NetFramework.csproj", "{D9D74F33-6943-49B2-B765-7BD589082098}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetStandard", "Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetStandard", "Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||||
@@ -29,11 +25,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
LICENSE = LICENSE
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspnetCore", "Frameworks\MQTTnet.AspnetCore\MQTTnet.AspnetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -62,22 +63,6 @@ Global
|
|||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.Build.0 = Release|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.Build.0 = Release|x86
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|ARM.ActiveCfg = Debug|ARM
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
@@ -110,22 +95,6 @@ Global
|
|||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.Build.0 = Release|x64
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.Build.0 = Release|x86
|
|
||||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
@@ -180,18 +149,50 @@ Global
|
|||||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||||
{D9D74F33-6943-49B2-B765-7BD589082098} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
|
||||||
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
|
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
|
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[](https://bettercodehub.com/)
|
[](https://bettercodehub.com/)
|
||||||
|
|
||||||
# MQTTnet
|
# MQTTnet
|
||||||
MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
|
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
@@ -18,21 +18,26 @@ MQTTnet is a .NET library for MQTT based communication. It provides a MQTT clien
|
|||||||
* Performance optimized (processing ~27.000 messages / second)*
|
* Performance optimized (processing ~27.000 messages / second)*
|
||||||
* Interfaces included for mocking and testing
|
* Interfaces included for mocking and testing
|
||||||
* Access to internal trace messages
|
* Access to internal trace messages
|
||||||
* Unit tested (62+ tests)
|
* Unit tested (67+ tests)
|
||||||
|
|
||||||
\* Tested on local machine with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetFramework_.
|
\* Tested on local machine with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetFramework_.
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
* Communication via TCP (+TLS) or WS (WebSocket) supported
|
||||||
|
* Included core _MqttClient_ with low level functionality
|
||||||
|
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
|
||||||
* Rx support (via another project)
|
* Rx support (via another project)
|
||||||
* Communication via TCP (+TLS) or WS (WebSocket)
|
* Compatible with Microsoft Azure IoT Hub
|
||||||
|
|
||||||
### Server (broker)
|
### Server (broker)
|
||||||
* List of connected clients available
|
* List of connected clients available
|
||||||
* Supports connected clients with different protocol versions at the same time
|
* Supports connected clients with different protocol versions at the same time
|
||||||
* Able to publish its own messages (no loopback client required)
|
* Able to publish its own messages (no loopback client required)
|
||||||
* Able to receive every messages (no loopback client required)
|
* Able to receive every message (no loopback client required)
|
||||||
* Extensible client credential validation
|
* Extensible client credential validation
|
||||||
* Retained messages are supported including persisting via interface methods (own implementation required)
|
* Retained messages are supported including persisting via interface methods (own implementation required)
|
||||||
|
* WebSockets supported (via ASP.NET Core 2.0)
|
||||||
|
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||||
|
|
||||||
# Supported frameworks
|
# Supported frameworks
|
||||||
* .NET Standard 1.3+
|
* .NET Standard 1.3+
|
||||||
@@ -53,12 +58,34 @@ This library is available as a nuget package: https://www.nuget.org/packages/MQT
|
|||||||
Please find examples and the documentation at the Wiki of this repository (https://github.com/chkr1011/MQTTnet/wiki).
|
Please find examples and the documentation at the Wiki of this repository (https://github.com/chkr1011/MQTTnet/wiki).
|
||||||
|
|
||||||
# Contributions
|
# Contributions
|
||||||
If you want to contribute to this project just create a pull request.
|
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
|
||||||
|
|
||||||
# References
|
# References
|
||||||
This library is used in the following projects:
|
This library is used in the following projects:
|
||||||
|
|
||||||
* MQTT Client Rx (Wrapper for Reactive Extensions, https://github.com/1iveowl/MQTTClient.rx)
|
* MQTT Client Rx (Wrapper for Reactive Extensions, https://github.com/1iveowl/MQTTClient.rx)
|
||||||
* HA4IoT (Open Source Home Automation system for .NET, https://github.com/chkr1011/HA4IoT)
|
* Wirehome (Open Source Home Automation system for .NET, https://github.com/chkr1011/Wirehome)
|
||||||
|
|
||||||
If you use this library and want to see your project here please let me know.
|
If you use this library and want to see your project here please let me know.
|
||||||
|
|
||||||
|
# MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Christian Kratky
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -55,7 +55,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var r = await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
var iis = new int[0];
|
var iis = new int[0];
|
||||||
return iis[1];
|
return iis[1];
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="1.1.18" />
|
<PackageReference Include="MSTest.TestAdapter" Version="1.1.18" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="1.1.18" />
|
<PackageReference Include="MSTest.TestFramework" Version="1.1.18" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Core.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class MqttApplicationMessageBuilderTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void CreateApplicationMessage_TopicOnly()
|
||||||
|
{
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("Abc").Build();
|
||||||
|
Assert.AreEqual("Abc", message.Topic);
|
||||||
|
Assert.IsFalse(message.Retain);
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CreateApplicationMessage_TimeStampPayload()
|
||||||
|
{
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("xyz").WithPayload(TimeSpan.FromSeconds(360).ToString()).Build();
|
||||||
|
Assert.AreEqual("xyz", message.Topic);
|
||||||
|
Assert.IsFalse(message.Retain);
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||||
|
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "00:06:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CreateApplicationMessage_StreamPayload()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream(Encoding.UTF8.GetBytes("xHello")) { Position = 1 };
|
||||||
|
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("123").WithPayload(stream).Build();
|
||||||
|
Assert.AreEqual("123", message.Topic);
|
||||||
|
Assert.IsFalse(message.Retain);
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||||
|
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CreateApplicationMessage_Retained()
|
||||||
|
{
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("lol").WithRetainFlag().Build();
|
||||||
|
Assert.AreEqual("lol", message.Topic);
|
||||||
|
Assert.IsTrue(message.Retain);
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CreateApplicationMessage_QosLevel2()
|
||||||
|
{
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("rofl").WithRetainFlag().WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||||
|
Assert.AreEqual("rofl", message.Topic);
|
||||||
|
Assert.IsTrue(message.Retain);
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.ExactlyOnce, message.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Tests
|
namespace MQTTnet.Core.Tests
|
||||||
@@ -12,7 +13,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
_adapter = adapter;
|
_adapter = adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMqttCommunicationAdapter CreateMqttCommunicationAdapter(MqttClientOptions options)
|
public IMqttCommunicationAdapter CreateClientMqttCommunicationAdapter(IMqttClientOptions options)
|
||||||
|
{
|
||||||
|
return _adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter CreateServerMqttCommunicationAdapter(IMqttCommunicationChannel channel)
|
||||||
{
|
{
|
||||||
return _adapter;
|
return _adapter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Core.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class MqttLoggerProviderTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestLoggerCallback()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddLogging();
|
||||||
|
|
||||||
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
using ((IDisposable)serviceProvider)
|
||||||
|
{
|
||||||
|
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||||
|
|
||||||
|
loggerFactory.AddMqttTrace();
|
||||||
|
|
||||||
|
var expectedMsg = "Hello World!";
|
||||||
|
MqttNetTraceMessage msg = null;
|
||||||
|
|
||||||
|
MqttNetTrace.TraceMessagePublished += (sender, args) =>
|
||||||
|
{
|
||||||
|
msg = args.TraceMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
var logger = loggerFactory.CreateLogger<MqttLoggerProviderTest>();
|
||||||
|
|
||||||
|
logger.LogInformation(expectedMsg);
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedMsg, msg.Message);
|
||||||
|
|
||||||
|
var expectedException = new Exception("bad stuff");
|
||||||
|
|
||||||
|
logger.LogError(new EventId(), expectedException, expectedException.Message);
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedException, msg.Exception);
|
||||||
|
Assert.AreEqual(expectedException.Message, msg.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
@@ -55,11 +53,13 @@ namespace MQTTnet.Core.Tests
|
|||||||
Username = "USER",
|
Username = "USER",
|
||||||
KeepAlivePeriod = 123,
|
KeepAlivePeriod = 123,
|
||||||
CleanSession = true,
|
CleanSession = true,
|
||||||
WillMessage = new MqttApplicationMessage(
|
WillMessage = new MqttApplicationMessage
|
||||||
"My/last/will",
|
{
|
||||||
Encoding.UTF8.GetBytes("Good byte."),
|
Topic = "My/last/will",
|
||||||
MqttQualityOfServiceLevel.AtLeastOnce,
|
Payload = Encoding.UTF8.GetBytes("Good byte."),
|
||||||
true)
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
|
Retain = true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SerializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw==");
|
SerializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw==");
|
||||||
@@ -90,11 +90,13 @@ namespace MQTTnet.Core.Tests
|
|||||||
Username = "USER",
|
Username = "USER",
|
||||||
KeepAlivePeriod = 123,
|
KeepAlivePeriod = 123,
|
||||||
CleanSession = true,
|
CleanSession = true,
|
||||||
WillMessage = new MqttApplicationMessage(
|
WillMessage = new MqttApplicationMessage
|
||||||
"My/last/will",
|
{
|
||||||
Encoding.UTF8.GetBytes("Good byte."),
|
Topic = "My/last/will",
|
||||||
MqttQualityOfServiceLevel.AtLeastOnce,
|
Payload = Encoding.UTF8.GetBytes("Good byte."),
|
||||||
true)
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
|
Retain = true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DeserializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw==");
|
DeserializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw==");
|
||||||
@@ -387,45 +389,6 @@ namespace MQTTnet.Core.Tests
|
|||||||
DeserializeAndCompare(p, "sAIAew==");
|
DeserializeAndCompare(p, "sAIAew==");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class TestChannel : IMqttCommunicationChannel
|
|
||||||
{
|
|
||||||
private readonly MemoryStream _stream = new MemoryStream();
|
|
||||||
|
|
||||||
public Stream ReceiveStream => _stream;
|
|
||||||
|
|
||||||
public Stream RawReceiveStream => _stream;
|
|
||||||
|
|
||||||
public Stream SendStream => _stream;
|
|
||||||
|
|
||||||
public bool IsConnected { get; } = true;
|
|
||||||
|
|
||||||
public TestChannel()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestChannel(byte[] initialData)
|
|
||||||
{
|
|
||||||
_stream.Write(initialData, 0, initialData.Length);
|
|
||||||
_stream.Position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ConnectAsync()
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ToArray()
|
|
||||||
{
|
|
||||||
return _stream.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
private static void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
||||||
{
|
{
|
||||||
var serializer = new MqttPacketSerializer { ProtocolVersion = protocolVersion };
|
var serializer = new MqttPacketSerializer { ProtocolVersion = protocolVersion };
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
@@ -6,6 +8,7 @@ using MQTTnet.Core.Client;
|
|||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Tests
|
namespace MQTTnet.Core.Tests
|
||||||
{
|
{
|
||||||
@@ -49,10 +52,15 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_WillMessage()
|
public async Task MqttServer_WillMessage()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var willMessage = new MqttApplicationMessage("My/last/will", new byte[0], MqttQualityOfServiceLevel.AtMostOnce, false);
|
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
var c2 = await serverAdapter.ConnectTestClient(s, "c2", willMessage);
|
var c2 = await serverAdapter.ConnectTestClient(s, "c2", willMessage);
|
||||||
|
|
||||||
@@ -73,7 +81,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_Unsubscribe()
|
public async Task MqttServer_Unsubscribe()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -82,7 +95,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
var receivedMessagesCount = 0;
|
var receivedMessagesCount = 0;
|
||||||
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||||
|
|
||||||
var message = new MqttApplicationMessage("a", new byte[0], MqttQualityOfServiceLevel.AtLeastOnce, false);
|
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
|
||||||
|
|
||||||
await c2.PublishAsync(message);
|
await c2.PublishAsync(message);
|
||||||
Assert.AreEqual(0, receivedMessagesCount);
|
Assert.AreEqual(0, receivedMessagesCount);
|
||||||
@@ -109,7 +122,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_Publish()
|
public async Task MqttServer_Publish()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -117,7 +135,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
var receivedMessagesCount = 0;
|
var receivedMessagesCount = 0;
|
||||||
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||||
|
|
||||||
var message = new MqttApplicationMessage("a", new byte[0], MqttQualityOfServiceLevel.AtLeastOnce, false);
|
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
|
||||||
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
||||||
|
|
||||||
s.Publish(message);
|
s.Publish(message);
|
||||||
@@ -132,7 +150,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_NoRetainedMessage()
|
public async Task MqttServer_NoRetainedMessage()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -155,7 +178,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_RetainedMessage()
|
public async Task MqttServer_RetainedMessage()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -178,7 +206,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
public async Task MqttServer_ClearRetainedMessage()
|
public async Task MqttServer_ClearRetainedMessage()
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -204,7 +237,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
var storage = new TestStorage();
|
var storage = new TestStorage();
|
||||||
|
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions { Storage = storage }, new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -213,7 +251,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
|
|
||||||
await s.StopAsync();
|
await s.StopAsync();
|
||||||
|
|
||||||
s = new MqttServer(new MqttServerOptions { Storage = storage }, new List<IMqttServerAdapter> { serverAdapter });
|
s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c2 = await serverAdapter.ConnectTestClient(s, "c2");
|
var c2 = await serverAdapter.ConnectTestClient(s, "c2");
|
||||||
@@ -228,6 +266,46 @@ namespace MQTTnet.Core.Tests
|
|||||||
Assert.AreEqual(1, receivedMessagesCount);
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MqttServer_InterceptMessage()
|
||||||
|
{
|
||||||
|
var options = new MqttServerOptions
|
||||||
|
{
|
||||||
|
ApplicationMessageInterceptor = message =>
|
||||||
|
{
|
||||||
|
message.Payload = Encoding.ASCII.GetBytes("extended");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
|
await s.StartAsync();
|
||||||
|
|
||||||
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
|
var c2 = await serverAdapter.ConnectTestClient(s, "c2");
|
||||||
|
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("test").Build());
|
||||||
|
|
||||||
|
var isIntercepted = false;
|
||||||
|
c2.ApplicationMessageReceived += (sender, args) =>
|
||||||
|
{
|
||||||
|
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(args.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
var m = new MqttApplicationMessageBuilder().WithTopic("test").Build();
|
||||||
|
await c1.PublishAsync(m);
|
||||||
|
await c1.DisconnectAsync();
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.IsTrue(isIntercepted);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestStorage : IMqttServerStorage
|
private class TestStorage : IMqttServerStorage
|
||||||
{
|
{
|
||||||
private IList<MqttApplicationMessage> _messages = new List<MqttApplicationMessage>();
|
private IList<MqttApplicationMessage> _messages = new List<MqttApplicationMessage>();
|
||||||
@@ -235,7 +313,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
|
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
_messages = messages;
|
_messages = messages;
|
||||||
return Task.FromResult(0);
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
|
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
|
||||||
@@ -252,7 +330,12 @@ namespace MQTTnet.Core.Tests
|
|||||||
int expectedReceivedMessagesCount)
|
int expectedReceivedMessagesCount)
|
||||||
{
|
{
|
||||||
var serverAdapter = new TestMqttServerAdapter();
|
var serverAdapter = new TestMqttServerAdapter();
|
||||||
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { serverAdapter });
|
var services = new ServiceCollection()
|
||||||
|
.AddMqttServer()
|
||||||
|
.AddSingleton<IMqttServerAdapter>(serverAdapter)
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var s = services.GetRequiredService<IMqttServer>();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
var c1 = await serverAdapter.ConnectTestClient(s, "c1");
|
||||||
@@ -261,8 +344,8 @@ namespace MQTTnet.Core.Tests
|
|||||||
var receivedMessagesCount = 0;
|
var receivedMessagesCount = 0;
|
||||||
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||||
|
|
||||||
await c1.SubscribeAsync(new TopicFilter(topicFilter, filterQualityOfServiceLevel));
|
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic(topicFilter).WithQualityOfServiceLevel(filterQualityOfServiceLevel).Build());
|
||||||
await c2.PublishAsync(new MqttApplicationMessage(topic, new byte[0], qualityOfServiceLevel, false));
|
await c2.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(new byte[0]).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
await c1.UnsubscribeAsync(topicFilter);
|
await c1.UnsubscribeAsync(topicFilter);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user