mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 17:23:04 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c90bdbfb93 | ||
|
|
598ed66a3b | ||
|
|
59b0b79a32 | ||
|
|
0172606747 | ||
|
|
65c2a0fd7c | ||
|
|
68926cef20 | ||
|
|
447ca6cabb | ||
|
|
0b0239bcf1 | ||
|
|
e021d5a56b | ||
|
|
36abf8450e | ||
|
|
d67b4a0b35 | ||
|
|
e4a376acdd | ||
|
|
8cfdb2fbf2 | ||
|
|
886b2d6b42 | ||
|
|
2193281c6a | ||
|
|
eb7ee420fc | ||
|
|
5a1d953278 | ||
|
|
67c291e15a | ||
|
|
3722bc9ae6 | ||
|
|
1fb0944e2b | ||
|
|
61c7ee0958 | ||
|
|
9c5e5b5e10 | ||
|
|
b3e11d47b7 | ||
|
|
31419fef03 | ||
|
|
b2e3686f59 | ||
|
|
9b90ae0c81 | ||
|
|
39beb4912b | ||
|
|
98d7bc980c | ||
|
|
2ff449b241 | ||
|
|
83f6ae4e54 | ||
|
|
d26d29e9a6 | ||
|
|
d0e8a0c4e5 | ||
|
|
35344b27c5 | ||
|
|
6b6b7f90c8 | ||
|
|
eeb3242573 | ||
|
|
cd5c295484 | ||
|
|
4ffe6dfc2b | ||
|
|
072c1c521a | ||
|
|
314fca3f89 | ||
|
|
dae5a06734 | ||
|
|
4b62236638 | ||
|
|
5117f9b30d | ||
|
|
818ffcab29 | ||
|
|
efe2ceebe0 | ||
|
|
67f9c07b44 | ||
|
|
c5978f5845 | ||
|
|
929cdb971a | ||
|
|
1317fa3e2a | ||
|
|
8a65850f1a | ||
|
|
5c1eceb8a9 | ||
|
|
2db289a658 | ||
|
|
201844a45b | ||
|
|
93a3bbd849 | ||
|
|
86dddd81c7 |
+64
-21
@@ -1,5 +1,7 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
@@ -15,12 +17,12 @@
|
|||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
[Rr]elease/
|
[Rr]elease/
|
||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
[Xx]64/
|
x64/
|
||||||
[Xx]86/
|
x86/
|
||||||
[Bb]uild/
|
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
@@ -40,9 +42,11 @@ TestResult.xml
|
|||||||
[Rr]eleasePS/
|
[Rr]eleasePS/
|
||||||
dlldata.c
|
dlldata.c
|
||||||
|
|
||||||
# DNX
|
# .NET Core
|
||||||
project.lock.json
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
@@ -81,6 +85,7 @@ ipch/
|
|||||||
*.sdf
|
*.sdf
|
||||||
*.cachefile
|
*.cachefile
|
||||||
*.VC.db
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
# Visual Studio profiler
|
# Visual Studio profiler
|
||||||
*.psess
|
*.psess
|
||||||
@@ -108,6 +113,10 @@ _TeamCity*
|
|||||||
# DotCover is a Code Coverage Tool
|
# DotCover is a Code Coverage Tool
|
||||||
*.dotCover
|
*.dotCover
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
# NCrunch
|
# NCrunch
|
||||||
_NCrunch_*
|
_NCrunch_*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
@@ -139,13 +148,16 @@ publish/
|
|||||||
# Publish Web Output
|
# Publish Web Output
|
||||||
*.[Pp]ublish.xml
|
*.[Pp]ublish.xml
|
||||||
*.azurePubxml
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
# TODO: Un-comment the next line if you do not want to checkin
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
# your web deploy settings because they may include unencrypted
|
*.pubxml
|
||||||
# passwords
|
|
||||||
#*.pubxml
|
|
||||||
*.publishproj
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
# NuGet Packages
|
# NuGet Packages
|
||||||
*.nupkg
|
*.nupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
# The packages folder can be ignored because of Package Restore
|
||||||
@@ -154,7 +166,7 @@ publish/
|
|||||||
!**/packages/build/
|
!**/packages/build/
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
#!**/packages/repositories.config
|
#!**/packages/repositories.config
|
||||||
# NuGet v3's project.json files produces more ignoreable files
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
*.nuget.props
|
*.nuget.props
|
||||||
*.nuget.targets
|
*.nuget.targets
|
||||||
|
|
||||||
@@ -166,12 +178,11 @@ csx/
|
|||||||
ecf/
|
ecf/
|
||||||
rcf/
|
rcf/
|
||||||
|
|
||||||
# Microsoft Azure ApplicationInsights config file
|
# Windows Store app package directories and files
|
||||||
ApplicationInsights.config
|
|
||||||
|
|
||||||
# Windows Store app package directory
|
|
||||||
AppPackages/
|
AppPackages/
|
||||||
BundleArtifacts/
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
|
||||||
# Visual Studio cache files
|
# Visual Studio cache files
|
||||||
# files ending in .cache can be ignored
|
# files ending in .cache can be ignored
|
||||||
@@ -181,16 +192,19 @@ BundleArtifacts/
|
|||||||
|
|
||||||
# Others
|
# Others
|
||||||
ClientBin/
|
ClientBin/
|
||||||
[Ss]tyle[Cc]op.*
|
|
||||||
~$*
|
~$*
|
||||||
*~
|
*~
|
||||||
*.dbmdl
|
*.dbmdl
|
||||||
*.dbproj.schemaview
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
*.pfx
|
*.pfx
|
||||||
*.publishsettings
|
*.publishsettings
|
||||||
node_modules/
|
|
||||||
orleans.codegen.cs
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
@@ -205,6 +219,7 @@ UpgradeLog*.htm
|
|||||||
# SQL Server files
|
# SQL Server files
|
||||||
*.mdf
|
*.mdf
|
||||||
*.ldf
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
# Business Intelligence projects
|
# Business Intelligence projects
|
||||||
*.rdl.data
|
*.rdl.data
|
||||||
@@ -219,6 +234,10 @@ FakesAssemblies/
|
|||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
# Node.js Tools for Visual Studio
|
||||||
.ntvs_analysis.dat
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
# Visual Studio 6 build log
|
||||||
*.plg
|
*.plg
|
||||||
@@ -226,6 +245,9 @@ FakesAssemblies/
|
|||||||
# Visual Studio 6 workspace options file
|
# Visual Studio 6 workspace options file
|
||||||
*.opt
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
# Visual Studio LightSwitch build output
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
@@ -234,13 +256,34 @@ FakesAssemblies/
|
|||||||
**/*.Server/ModelManifest.xml
|
**/*.Server/ModelManifest.xml
|
||||||
_Pvt_Extensions
|
_Pvt_Extensions
|
||||||
|
|
||||||
# LightSwitch generated files
|
|
||||||
GeneratedArtifacts/
|
|
||||||
ModelManifest.xml
|
|
||||||
|
|
||||||
# Paket dependency manager
|
# Paket dependency manager
|
||||||
.paket/paket.exe
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
nuget.exe
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
Build/nuget.exe
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<package >
|
||||||
|
<metadata>
|
||||||
|
<id>MQTTnet</id>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
<authors>Christian Kratky</authors>
|
||||||
|
<owners>Christian Kratky</owners>
|
||||||
|
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||||
|
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||||
|
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
<description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||||
|
<releaseNotes>* [Server] Added support for MQTT protocol version 3.1.0
|
||||||
|
* [Server] Providing the used protocol version of connected clients
|
||||||
|
* [Client] Added support for protocol version 3.1.0
|
||||||
|
* [Core] Several minor performance improvements
|
||||||
|
* [Core] Fixed an issue with connection management (Thanks to wuzhenda; Zuendelmeister)
|
||||||
|
</releaseNotes>
|
||||||
|
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
||||||
|
<tags>MQTT MQTTClient MQTTServer MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Queue Hardware Arduino</tags>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<group targetFramework="netstandard1.3">
|
||||||
|
<dependency id="NETStandard.Library" version="1.6.1" />
|
||||||
|
<dependency id="System.Net.Security" version="4.3.1" />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group targetFramework="uap10.0">
|
||||||
|
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.3.3" />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</metadata>
|
||||||
|
|
||||||
|
<files>
|
||||||
|
<!-- .NET Standard 1.3 -->
|
||||||
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.Core.*" target="lib\netstandard1.3\"/>
|
||||||
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||||
|
|
||||||
|
<!-- Universal Windows -->
|
||||||
|
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.Core.*" target="lib\uap10.0\"/>
|
||||||
|
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.*" target="lib\uap10.0\"/>
|
||||||
|
|
||||||
|
<!-- .NET Framework -->
|
||||||
|
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.Core.*" target="lib\net45\"/>
|
||||||
|
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.*" target="lib\net45\"/>
|
||||||
|
|
||||||
|
</files>
|
||||||
|
</package>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
param([string]$version)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
|
||||||
|
|
||||||
|
$msbuild = "MSBuild.exe"
|
||||||
|
&$msbuild ..\Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj /t:Build /p:Configuration="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"
|
||||||
|
|
||||||
|
Remove-Item .\NuGet -Force -Recurse
|
||||||
|
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||||
|
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
|
||||||
<Authors>Christian Kratky</Authors>
|
|
||||||
<Company>Christian Kratky</Company>
|
|
||||||
<Product>MQTTnet</Product>
|
|
||||||
<Description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</Description>
|
|
||||||
<Copyright>Copyright © Christian Kratky 2016-2017</Copyright>
|
|
||||||
<Version>2.1.1.0</Version>
|
|
||||||
<AssemblyVersion>2.1.1.0</AssemblyVersion>
|
|
||||||
<FileVersion>2.1.1.0</FileVersion>
|
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
|
||||||
<RootNamespace>MQTTnet</RootNamespace>
|
|
||||||
<PackageId>MQTTnet.NetCore</PackageId>
|
|
||||||
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
|
|
||||||
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
|
|
||||||
<PackageTags>MQTT MQTTClient MQTTServer MQTTBroker Broker</PackageTags>
|
|
||||||
<PackageIconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</PackageIconUrl>
|
|
||||||
<PackageLicenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</PackageLicenseUrl>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x64'" />
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x86'" />
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private Socket _socket;
|
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
|
||||||
|
|
||||||
public void Start(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (_socket != null) throw new InvalidOperationException("Server is already started.");
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
_socket.Bind(new IPEndPoint(IPAddress.Any, options.Port));
|
|
||||||
_socket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(async () => await AcceptConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
_socket?.Dispose();
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var clientSocket = await _socket.AcceptAsync();
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket), new DefaultMqttV311PacketSerializer());
|
|
||||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttServerFactory
|
|
||||||
{
|
|
||||||
public MqttServer CreateMqttServer(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
return new MqttServer(options, new MqttServerAdapter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
|
||||||
{
|
|
||||||
private readonly Socket _socket;
|
|
||||||
|
|
||||||
public MqttTcpChannel()
|
|
||||||
{
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttTcpChannel(Socket socket)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _socket.ConnectAsync(options.Server, options.Port);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_socket.Dispose();
|
|
||||||
await Task.FromResult(0);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _socket.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ReadAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var buffer2 = new ArraySegment<byte>(buffer);
|
|
||||||
await _socket.ReceiveAsync(buffer2, SocketFlags.None);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
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 EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
public void Start(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
|
_cancellationTokenSource?.Cancel(false);
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
|
_defaultEndpointSocket?.Dispose();
|
||||||
|
_defaultEndpointSocket = null;
|
||||||
|
|
||||||
|
_tlsEndpointSocket?.Dispose();
|
||||||
|
_tlsEndpointSocket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null);
|
||||||
|
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer());
|
||||||
|
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (!(exception is ObjectDisposedException))
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at default endpoint.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null);
|
||||||
|
|
||||||
|
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
||||||
|
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false);
|
||||||
|
|
||||||
|
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer());
|
||||||
|
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at TLS endpoint.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
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 MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace MQTTnet.Implementations
|
||||||
|
{
|
||||||
|
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||||
|
{
|
||||||
|
private Socket _socket;
|
||||||
|
private SslStream _sslStream;
|
||||||
|
|
||||||
|
public MqttTcpChannel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
||||||
|
{
|
||||||
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
|
_sslStream = sslStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConnectAsync(MqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
{
|
||||||
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, options.Server, options.GetPort(), null);
|
||||||
|
|
||||||
|
if (options.TlsOptions.UseTls)
|
||||||
|
{
|
||||||
|
_sslStream = new SslStream(new NetworkStream(_socket, true));
|
||||||
|
await _sslStream.AuthenticateAsClientAsync(options.Server, LoadCertificates(options), SslProtocols.Tls12, options.TlsOptions.CheckCertificateRevocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DisconnectAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WriteAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_sslStream != null)
|
||||||
|
{
|
||||||
|
return _sslStream.WriteAsync(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.Factory.FromAsync(
|
||||||
|
// ReSharper disable once AssignNullToNotNullAttribute
|
||||||
|
_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, null),
|
||||||
|
_socket.EndSend);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ReadAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_sslStream != null)
|
||||||
|
{
|
||||||
|
return _sslStream.ReadAsync(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.Factory.FromAsync(
|
||||||
|
// ReSharper disable once AssignNullToNotNullAttribute
|
||||||
|
_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, null),
|
||||||
|
_socket.EndReceive);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_socket?.Dispose();
|
||||||
|
_sslStream?.Dispose();
|
||||||
|
|
||||||
|
_socket = null;
|
||||||
|
_sslStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,10 +101,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="MqttClientFactory.cs" />
|
<Compile Include="MqttClientFactory.cs" />
|
||||||
<Compile Include="MqttServerAdapter.cs" />
|
|
||||||
<Compile Include="MqttServerFactory.cs" />
|
<Compile Include="MqttServerFactory.cs" />
|
||||||
|
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="MqttTcpChannel.cs" />
|
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
|
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public class MqttClientFactory
|
public class MqttClientFactory
|
||||||
{
|
{
|
||||||
public MqttClient CreateMqttClient(MqttClientOptions options)
|
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
|
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new MqttPacketSerializer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private Socket _socket;
|
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
|
||||||
|
|
||||||
public void Start(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (_socket != null) throw new InvalidOperationException("Server is already started.");
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
_socket.Bind(new IPEndPoint(IPAddress.Any, options.Port));
|
|
||||||
_socket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(async () => await AcceptConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
_socket?.Dispose();
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null);
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket), new DefaultMqttV311PacketSerializer());
|
|
||||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public class MqttServerFactory
|
public class MqttServerFactory
|
||||||
{
|
{
|
||||||
public MqttServer CreateMqttServer(MqttServerOptions options)
|
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
return new MqttServer(options, new MqttServerAdapter());
|
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using MQTTnet.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
|
||||||
{
|
|
||||||
private readonly Socket _socket;
|
|
||||||
|
|
||||||
public MqttTcpChannel()
|
|
||||||
{
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttTcpChannel(Socket socket)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, options.Server, options.Port, null);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Factory.FromAsync(_socket.BeginDisconnect, _socket.EndDisconnect, true, null);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Factory.FromAsync(
|
|
||||||
// ReSharper disable once AssignNullToNotNullAttribute
|
|
||||||
_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, null),
|
|
||||||
_socket.EndSend);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ReadAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Factory.FromAsync(
|
|
||||||
// ReSharper disable once AssignNullToNotNullAttribute
|
|
||||||
_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, null),
|
|
||||||
_socket.EndReceive);
|
|
||||||
}
|
|
||||||
catch (SocketException exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,5 +11,5 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
[assembly: Guid("a480ef90-0eaa-4d9a-b271-47a9c47f6f7d")]
|
[assembly: Guid("a480ef90-0eaa-4d9a-b271-47a9c47f6f7d")]
|
||||||
[assembly: AssemblyVersion("2.1.1.0")]
|
[assembly: AssemblyVersion("2.1.5.1")]
|
||||||
[assembly: AssemblyFileVersion("2.1.1.0")]
|
[assembly: AssemblyFileVersion("2.1.5.1")]
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
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 EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
public void Start(MqttServerOptions 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
|
_cancellationTokenSource?.Cancel(false);
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
|
_defaultEndpointSocket?.Dispose();
|
||||||
|
_defaultEndpointSocket = null;
|
||||||
|
|
||||||
|
_tlsEndpointSocket?.Dispose();
|
||||||
|
_tlsEndpointSocket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clientSocket = await _defaultEndpointSocket.AcceptAsync();
|
||||||
|
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer());
|
||||||
|
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at default endpoint.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clientSocket = await _defaultEndpointSocket.AcceptAsync();
|
||||||
|
|
||||||
|
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
||||||
|
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false);
|
||||||
|
|
||||||
|
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer());
|
||||||
|
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at TLS endpoint.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
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 MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace MQTTnet.Implementations
|
||||||
|
{
|
||||||
|
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||||
|
{
|
||||||
|
private Socket _socket;
|
||||||
|
private SslStream _sslStream;
|
||||||
|
|
||||||
|
public MqttTcpChannel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
||||||
|
{
|
||||||
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
|
_sslStream = sslStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConnectAsync(MqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
{
|
||||||
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _socket.ConnectAsync(options.Server, options.GetPort());
|
||||||
|
|
||||||
|
if (options.TlsOptions.UseTls)
|
||||||
|
{
|
||||||
|
_sslStream = new SslStream(new NetworkStream(_socket, true));
|
||||||
|
await _sslStream.AuthenticateAsClientAsync(options.Server, LoadCertificates(options), SslProtocols.Tls12, options.TlsOptions.CheckCertificateRevocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DisconnectAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WriteAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_sslStream != null)
|
||||||
|
{
|
||||||
|
return _sslStream.WriteAsync(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _socket.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ReadAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_sslStream != null)
|
||||||
|
{
|
||||||
|
return _sslStream.ReadAsync(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_socket?.Dispose();
|
||||||
|
_sslStream?.Dispose();
|
||||||
|
|
||||||
|
_socket = null;
|
||||||
|
_sslStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard1.3</TargetFramework>
|
||||||
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
|
<RootNamespace>MQTTnet</RootNamespace>
|
||||||
|
<AssemblyVersion>2.1.5.1</AssemblyVersion>
|
||||||
|
<FileVersion>2.1.5.1</FileVersion>
|
||||||
|
<Version>0.0.0.0</Version>
|
||||||
|
<Company />
|
||||||
|
<Product />
|
||||||
|
<Description />
|
||||||
|
<Authors />
|
||||||
|
<PackageId />
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Net.Security" Version="4.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+3
-2
@@ -2,16 +2,17 @@
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public class MqttClientFactory
|
public class MqttClientFactory
|
||||||
{
|
{
|
||||||
public MqttClient CreateMqttClient(MqttClientOptions options)
|
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
|
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new MqttPacketSerializer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
|
namespace MQTTnet
|
||||||
|
{
|
||||||
|
public class MqttServerFactory
|
||||||
|
{
|
||||||
|
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,64 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
using MQTTnet.Core.Diagnostics;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Core.Server;
|
||||||
|
using Windows.Networking.Sockets;
|
||||||
|
|
||||||
|
namespace MQTTnet.Implementations
|
||||||
|
{
|
||||||
|
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||||
|
{
|
||||||
|
private StreamSocketListener _defaultEndpointSocket;
|
||||||
|
|
||||||
|
private bool _isRunning;
|
||||||
|
|
||||||
|
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
public void Start(MqttServerOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
||||||
|
_isRunning = true;
|
||||||
|
|
||||||
|
if (options.DefaultEndpointOptions.IsEnabled)
|
||||||
|
{
|
||||||
|
_defaultEndpointSocket = new StreamSocketListener();
|
||||||
|
_defaultEndpointSocket.BindServiceNameAsync(options.GetDefaultEndpointPort().ToString(), SocketProtectionLevel.PlainSocket).GetAwaiter().GetResult();
|
||||||
|
_defaultEndpointSocket.ConnectionReceived += AcceptDefaultEndpointConnectionsAsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.TlsEndpointOptions.IsEnabled)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("TLS servers are not supported for UWP apps.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
|
_defaultEndpointSocket?.Dispose();
|
||||||
|
_defaultEndpointSocket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer());
|
||||||
|
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(args.Socket.Information.RemoteAddress.ToString(), clientAdapter));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at default endpoint.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.Networking;
|
||||||
|
using Windows.Networking.Sockets;
|
||||||
|
using Windows.Security.Cryptography.Certificates;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
using MQTTnet.Core.Channel;
|
||||||
|
using MQTTnet.Core.Client;
|
||||||
|
using MQTTnet.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace MQTTnet.Implementations
|
||||||
|
{
|
||||||
|
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||||
|
{
|
||||||
|
private StreamSocket _socket;
|
||||||
|
|
||||||
|
public MqttTcpChannel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTcpChannel(StreamSocket socket)
|
||||||
|
{
|
||||||
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConnectAsync(MqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
{
|
||||||
|
_socket = new StreamSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.TlsOptions.UseTls)
|
||||||
|
{
|
||||||
|
await _socket.ConnectAsync(new HostName(options.Server), options.GetPort().ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_socket.Control.ClientCertificate = LoadCertificate(options);
|
||||||
|
|
||||||
|
if (!options.TlsOptions.CheckCertificateRevocation)
|
||||||
|
{
|
||||||
|
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain);
|
||||||
|
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _socket.ConnectAsync(new HostName(options.Server), options.GetPort().ToString(), SocketProtectionLevel.Tls12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DisconnectAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WriteAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _socket.OutputStream.WriteAsync(buffer.AsBuffer());
|
||||||
|
await _socket.OutputStream.FlushAsync();
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ReadAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None);
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_socket?.Dispose();
|
||||||
|
|
||||||
|
_socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Certificate LoadCertificate(MqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.TlsOptions.Certificates.Count > 1)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Only one client certificate is supported for UWP.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,12 @@
|
|||||||
<AssemblyName>MQTTnet</AssemblyName>
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||||
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>
|
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<RuntimeIdentifiers>win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot</RuntimeIdentifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<NoWarn>CS1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
@@ -35,13 +37,14 @@
|
|||||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<NoWarn>CS1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -52,7 +55,7 @@
|
|||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -64,7 +67,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>ARM</PlatformTarget>
|
<PlatformTarget>ARM</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -75,7 +78,7 @@
|
|||||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>ARM</PlatformTarget>
|
<PlatformTarget>ARM</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -87,7 +90,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -98,7 +101,7 @@
|
|||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<NoWarn>;2008</NoWarn>
|
<NoWarn>;2008,CS1591</NoWarn>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
@@ -106,15 +109,11 @@
|
|||||||
<GenerateLibraryLayout>false</GenerateLibraryLayout>
|
<GenerateLibraryLayout>false</GenerateLibraryLayout>
|
||||||
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
|
|
||||||
<None Include="project.json" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="MqttClientFactory.cs" />
|
<Compile Include="MqttClientFactory.cs" />
|
||||||
<Compile Include="MqttServerAdapter.cs" />
|
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
||||||
<Compile Include="MqttServerFactory.cs" />
|
<Compile Include="MqttServerFactory.cs" />
|
||||||
<Compile Include="MqttTcpChannel.cs" />
|
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
|
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -124,6 +123,11 @@
|
|||||||
<Name>MQTTnet.Core</Name>
|
<Name>MQTTnet.Core</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
|
<Version>5.3.3</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Serializer;
|
using MQTTnet.Core.Serializer;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public class MqttClientFactory
|
public class MqttClientFactory
|
||||||
{
|
{
|
||||||
public MqttClient CreateMqttClient(MqttClientOptions options)
|
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
|
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new MqttPacketSerializer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Windows.Networking.Sockets;
|
|
||||||
using MQTTnet.Core.Adapter;
|
|
||||||
using MQTTnet.Core.Serializer;
|
|
||||||
using MQTTnet.Core.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private StreamSocketListener _socket;
|
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
|
||||||
|
|
||||||
public void Start(MqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (_socket != null) throw new InvalidOperationException("Server is already started.");
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
_socket = new StreamSocketListener();
|
|
||||||
_socket.BindServiceNameAsync(options.Port.ToString()).AsTask().Wait();
|
|
||||||
_socket.ConnectionReceived += ConnectionReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
|
|
||||||
{
|
|
||||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new DefaultMqttV311PacketSerializer());
|
|
||||||
|
|
||||||
var identifier = $"{args.Socket.Information.RemoteAddress}:{args.Socket.Information.RemotePort}";
|
|
||||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(identifier, clientAdapter));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
|
|
||||||
if (_socket != null)
|
|
||||||
{
|
|
||||||
_socket.ConnectionReceived -= ConnectionReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
_socket?.Dispose();
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public class MqttServerFactory
|
public class MqttServerFactory
|
||||||
{
|
{
|
||||||
public MqttServer CreateMqttServer(MqttServerOptions options)
|
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
||||||
{
|
{
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
return new MqttServer(options, new MqttServerAdapter());
|
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices.WindowsRuntime;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Windows.Networking;
|
|
||||||
using Windows.Networking.Sockets;
|
|
||||||
using Windows.Storage.Streams;
|
|
||||||
using MQTTnet.Core.Channel;
|
|
||||||
using MQTTnet.Core.Client;
|
|
||||||
using Buffer = Windows.Storage.Streams.Buffer;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
|
||||||
{
|
|
||||||
private readonly StreamSocket _socket;
|
|
||||||
|
|
||||||
public MqttTcpChannel()
|
|
||||||
{
|
|
||||||
_socket = new StreamSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttTcpChannel(StreamSocket socket)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
await _socket.ConnectAsync(new HostName(options.Server), options.Port.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
await _socket.CancelIOAsync();
|
|
||||||
_socket.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
|
||||||
|
|
||||||
await _socket.OutputStream.WriteAsync(buffer.AsBuffer());
|
|
||||||
await _socket.OutputStream.FlushAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ReadAsync(byte[] buffer)
|
|
||||||
{
|
|
||||||
var buffer2 = new Buffer((uint)buffer.Length);
|
|
||||||
await _socket.InputStream.ReadAsync(buffer2, (uint)buffer.Length, InputStreamOptions.None);
|
|
||||||
|
|
||||||
var array2 = buffer2.ToArray();
|
|
||||||
Array.Copy(array2, buffer, array2.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.1.1.0")]
|
[assembly: AssemblyVersion("2.1.5.1")]
|
||||||
[assembly: AssemblyFileVersion("2.1.1.0")]
|
[assembly: AssemblyFileVersion("2.1.5.1")]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0"
|
|
||||||
},
|
|
||||||
"frameworks": {
|
|
||||||
"uap10.0": {},
|
|
||||||
"netstandard1.1": {}
|
|
||||||
},
|
|
||||||
"runtimes": {
|
|
||||||
"win10-arm": {},
|
|
||||||
"win10-arm-aot": {},
|
|
||||||
"win10-x86": {},
|
|
||||||
"win10-x86-aot": {},
|
|
||||||
"win10-x64": {},
|
|
||||||
"win10-x64-aot": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Adapter
|
namespace MQTTnet.Core.Adapter
|
||||||
{
|
{
|
||||||
@@ -14,5 +15,7 @@ namespace MQTTnet.Core.Adapter
|
|||||||
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout);
|
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout);
|
||||||
|
|
||||||
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout);
|
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout);
|
||||||
|
|
||||||
|
IMqttPacketSerializer PacketSerializer { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,19 @@ namespace MQTTnet.Core.Adapter
|
|||||||
{
|
{
|
||||||
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
||||||
{
|
{
|
||||||
private readonly IMqttPacketSerializer _serializer;
|
|
||||||
private readonly IMqttCommunicationChannel _channel;
|
private readonly IMqttCommunicationChannel _channel;
|
||||||
|
|
||||||
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer)
|
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer)
|
||||||
{
|
{
|
||||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||||
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IMqttPacketSerializer PacketSerializer { get; }
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout)
|
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
var task = _channel.ConnectAsync(options);
|
await ExecuteWithTimeoutAsync(_channel.ConnectAsync(options), timeout);
|
||||||
if (await Task.WhenAny(Task.Delay(timeout), task) != task)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationTimedOutException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
public async Task DisconnectAsync()
|
||||||
@@ -38,21 +35,7 @@ namespace MQTTnet.Core.Adapter
|
|||||||
{
|
{
|
||||||
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"TX >>> {packet} [Timeout={timeout}]");
|
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"TX >>> {packet} [Timeout={timeout}]");
|
||||||
|
|
||||||
bool hasTimeout;
|
await ExecuteWithTimeoutAsync(PacketSerializer.SerializeAsync(packet, _channel), timeout);
|
||||||
try
|
|
||||||
{
|
|
||||||
var task = _serializer.SerializeAsync(packet, _channel);
|
|
||||||
hasTimeout = await Task.WhenAny(Task.Delay(timeout), task) != task;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasTimeout)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationTimedOutException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout)
|
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout)
|
||||||
@@ -60,20 +43,11 @@ namespace MQTTnet.Core.Adapter
|
|||||||
MqttBasePacket packet;
|
MqttBasePacket packet;
|
||||||
if (timeout > TimeSpan.Zero)
|
if (timeout > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
var workerTask = _serializer.DeserializeAsync(_channel);
|
packet = await ExecuteWithTimeoutAsync(PacketSerializer.DeserializeAsync(_channel), timeout);
|
||||||
var timeoutTask = Task.Delay(timeout);
|
|
||||||
var hasTimeout = Task.WhenAny(timeoutTask, workerTask) == timeoutTask;
|
|
||||||
|
|
||||||
if (hasTimeout)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationTimedOutException();
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = workerTask.Result;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
packet = await _serializer.DeserializeAsync(_channel);
|
packet = await PacketSerializer.DeserializeAsync(_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet == null)
|
if (packet == null)
|
||||||
@@ -84,5 +58,35 @@ namespace MQTTnet.Core.Adapter
|
|||||||
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"RX <<< {packet}");
|
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"RX <<< {packet}");
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<TResult> ExecuteWithTimeoutAsync<TResult>(Task<TResult> task, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
var timeoutTask = Task.Delay(timeout);
|
||||||
|
if (await Task.WhenAny(timeoutTask, task) == timeoutTask)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationTimedOutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsFaulted)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(task.Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task ExecuteWithTimeoutAsync(Task task, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
var timeoutTask = Task.Delay(timeout);
|
||||||
|
if (await Task.WhenAny(timeoutTask, task) == timeoutTask)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationTimedOutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsFaulted)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(task.Exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Core.Packets;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public interface IMqttClient
|
||||||
|
{
|
||||||
|
bool IsConnected { get; }
|
||||||
|
|
||||||
|
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
event EventHandler Connected;
|
||||||
|
event EventHandler Disconnected;
|
||||||
|
|
||||||
|
Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null);
|
||||||
|
Task DisconnectAsync();
|
||||||
|
Task PublishAsync(MqttApplicationMessage applicationMessage);
|
||||||
|
Task<IList<MqttSubscribeResult>> SubscribeAsync(IList<TopicFilter> topicFilters);
|
||||||
|
Task<IList<MqttSubscribeResult>> SubscribeAsync(params TopicFilter[] topicFilters);
|
||||||
|
Task Unsubscribe(IList<string> topicFilters);
|
||||||
|
Task Unsubscribe(params string[] topicFilters);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MQTTnet.Core
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttApplicationMessageReceivedEventArgs : EventArgs
|
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttApplicationMessageReceivedEventArgs(MqttApplicationMessage applicationMessage)
|
public MqttApplicationMessageReceivedEventArgs(MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -13,15 +12,15 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttClient
|
public class MqttClient : IMqttClient
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingExactlyOncePublishPackets = new ConcurrentDictionary<ushort, MqttPublishPacket>();
|
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||||
private readonly HashSet<ushort> _processedPublishPackets = new HashSet<ushort>();
|
|
||||||
|
|
||||||
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
|
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
|
||||||
private readonly MqttClientOptions _options;
|
private readonly MqttClientOptions _options;
|
||||||
private readonly IMqttCommunicationAdapter _adapter;
|
private readonly IMqttCommunicationAdapter _adapter;
|
||||||
|
|
||||||
|
private bool _disconnectedEventSuspended;
|
||||||
private int _latestPacketIdentifier;
|
private int _latestPacketIdentifier;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
@@ -29,6 +28,8 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||||||
|
|
||||||
|
_adapter.PacketSerializer.ProtocolVersion = options.ProtocolVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler Connected;
|
public event EventHandler Connected;
|
||||||
@@ -48,45 +49,64 @@ namespace MQTTnet.Core.Client
|
|||||||
throw new MqttProtocolViolationException("It is not allowed to connect with a server after the connection is established.");
|
throw new MqttProtocolViolationException("It is not allowed to connect with a server after the connection is established.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectPacket = new MqttConnectPacket
|
try
|
||||||
{
|
{
|
||||||
ClientId = _options.ClientId,
|
_disconnectedEventSuspended = false;
|
||||||
Username = _options.UserName,
|
|
||||||
Password = _options.Password,
|
|
||||||
CleanSession = _options.CleanSession,
|
|
||||||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
|
|
||||||
WillMessage = willApplicationMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
await _adapter.ConnectAsync(_options, _options.DefaultCommunicationTimeout);
|
await _adapter.ConnectAsync(_options, _options.DefaultCommunicationTimeout);
|
||||||
MqttTrace.Verbose(nameof(MqttClient), "Connection with server established.");
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
MqttTrace.Verbose(nameof(MqttClient), "Connection with server established.");
|
||||||
_latestPacketIdentifier = 0;
|
|
||||||
_processedPublishPackets.Clear();
|
|
||||||
_packetDispatcher.Reset();
|
|
||||||
IsConnected = true;
|
|
||||||
|
|
||||||
Task.Run(() => ReceivePackets(_cancellationTokenSource.Token), _cancellationTokenSource.Token).Forget();
|
var connectPacket = new MqttConnectPacket
|
||||||
|
{
|
||||||
|
ClientId = _options.ClientId,
|
||||||
|
Username = _options.UserName,
|
||||||
|
Password = _options.Password,
|
||||||
|
CleanSession = _options.CleanSession,
|
||||||
|
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
|
||||||
|
WillMessage = willApplicationMessage
|
||||||
|
};
|
||||||
|
|
||||||
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket);
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
_latestPacketIdentifier = 0;
|
||||||
{
|
_packetDispatcher.Reset();
|
||||||
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
|
||||||
|
StartReceivePackets();
|
||||||
|
|
||||||
|
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket);
|
||||||
|
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||||
|
{
|
||||||
|
await DisconnectInternalAsync();
|
||||||
|
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
StartSendKeepAliveMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttTrace.Verbose(nameof(MqttClient), "MQTT connection with server established.");
|
||||||
|
|
||||||
|
IsConnected = true;
|
||||||
|
Connected?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
Task.Run(() => SendKeepAliveMessagesAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token).Forget();
|
await DisconnectInternalAsync();
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Connected?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
public async Task DisconnectAsync()
|
||||||
{
|
{
|
||||||
await SendAsync(new MqttDisconnectPacket());
|
try
|
||||||
await DisconnectInternalAsync();
|
{
|
||||||
|
await SendAsync(new MqttDisconnectPacket());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await DisconnectInternalAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<MqttSubscribeResult>> SubscribeAsync(params TopicFilter[] topicFilters)
|
public Task<IList<MqttSubscribeResult>> SubscribeAsync(params TopicFilter[] topicFilters)
|
||||||
@@ -100,6 +120,7 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||||
|
|
||||||
ThrowIfNotConnected();
|
ThrowIfNotConnected();
|
||||||
|
|
||||||
var subscribePacket = new MqttSubscribePacket
|
var subscribePacket = new MqttSubscribePacket
|
||||||
@@ -149,6 +170,7 @@ namespace MQTTnet.Core.Client
|
|||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
|
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||||
await SendAsync(publishPacket);
|
await SendAsync(publishPacket);
|
||||||
}
|
}
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
@@ -159,8 +181,8 @@ namespace MQTTnet.Core.Client
|
|||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
{
|
{
|
||||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
|
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
|
||||||
await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
|
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
|
||||||
await SendAsync(publishPacket.CreateResponse<MqttPubCompPacket>());
|
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,45 +197,48 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
await _adapter.DisconnectAsync();
|
await _adapter.DisconnectAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
MqttTrace.Warning(nameof(MqttClient), exception, "Error while disconnecting.");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_cancellationTokenSource?.Cancel();
|
_cancellationTokenSource?.Cancel(false);
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
Disconnected?.Invoke(this, EventArgs.Empty);
|
|
||||||
|
if (!_disconnectedEventSuspended)
|
||||||
|
{
|
||||||
|
_disconnectedEventSuspended = true;
|
||||||
|
Disconnected?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ProcessReceivedPacket(MqttBasePacket mqttPacket)
|
private Task ProcessReceivedPacketAsync(MqttBasePacket mqttPacket)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (mqttPacket is MqttPingReqPacket)
|
if (mqttPacket is MqttPingReqPacket)
|
||||||
{
|
{
|
||||||
await SendAsync(new MqttPingRespPacket());
|
return SendAsync(new MqttPingRespPacket());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mqttPacket is MqttDisconnectPacket)
|
if (mqttPacket is MqttDisconnectPacket)
|
||||||
{
|
{
|
||||||
await DisconnectAsync();
|
return DisconnectAsync();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = mqttPacket as MqttPublishPacket;
|
if (mqttPacket is MqttPublishPacket publishPacket)
|
||||||
if (publishPacket != null)
|
|
||||||
{
|
{
|
||||||
await ProcessReceivedPublishPacket(publishPacket);
|
return ProcessReceivedPublishPacket(publishPacket);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRelPacket = mqttPacket as MqttPubRelPacket;
|
if (mqttPacket is MqttPubRelPacket pubRelPacket)
|
||||||
if (pubRelPacket != null)
|
|
||||||
{
|
{
|
||||||
await ProcessReceivedPubRelPacket(pubRelPacket);
|
return ProcessReceivedPubRelPacket(pubRelPacket);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetDispatcher.Dispatch(mqttPacket);
|
_packetDispatcher.Dispatch(mqttPacket);
|
||||||
@@ -222,57 +247,61 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
MqttTrace.Error(nameof(MqttClient), exception, "Error while processing received packet.");
|
MqttTrace.Error(nameof(MqttClient), exception, "Error while processing received packet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
|
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (publishPacket.QualityOfServiceLevel != MqttQualityOfServiceLevel.AtMostOnce)
|
var applicationMessage = publishPacket.ToApplicationMessage();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_processedPublishPackets.Add(publishPacket.PacketIdentifier);
|
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while handling application message.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var applicationMessage = new MqttApplicationMessage(
|
|
||||||
publishPacket.Topic,
|
|
||||||
publishPacket.Payload,
|
|
||||||
publishPacket.QualityOfServiceLevel,
|
|
||||||
publishPacket.Retain
|
|
||||||
);
|
|
||||||
|
|
||||||
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
|
private Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
FireApplicationMessageReceivedEvent(publishPacket);
|
||||||
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
{
|
{
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
FireApplicationMessageReceivedEvent(publishPacket);
|
||||||
{
|
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
|
||||||
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
}
|
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
|
||||||
{
|
|
||||||
_pendingExactlyOncePublishPackets[publishPacket.PacketIdentifier] = publishPacket;
|
|
||||||
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
FireApplicationMessageReceivedEvent(publishPacket);
|
||||||
|
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
|
private async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
|
||||||
{
|
{
|
||||||
MqttPublishPacket originalPublishPacket;
|
lock (_unacknowledgedPublishPackets)
|
||||||
if (!_pendingExactlyOncePublishPackets.TryRemove(pubRelPacket.PacketIdentifier, out originalPublishPacket))
|
|
||||||
{
|
{
|
||||||
throw new MqttCommunicationException();
|
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendAsync(originalPublishPacket.CreateResponse<MqttPubCompPacket>());
|
await SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
|
||||||
|
|
||||||
FireApplicationMessageReceivedEvent(originalPublishPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SendAsync(MqttBasePacket packet)
|
private Task SendAsync(MqttBasePacket packet)
|
||||||
@@ -290,18 +319,15 @@ namespace MQTTnet.Core.Client
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pi1 = requestPacket as IPacketWithIdentifier;
|
var pi1 = requestPacket as IMqttPacketWithIdentifier;
|
||||||
var pi2 = p as IPacketWithIdentifier;
|
var pi2 = p as IMqttPacketWithIdentifier;
|
||||||
|
|
||||||
if (pi1 != null && pi2 != null)
|
if (pi1 == null || pi2 == null)
|
||||||
{
|
{
|
||||||
if (pi1.PacketIdentifier != pi2.PacketIdentifier)
|
return true;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return pi1.PacketIdentifier == pi2.PacketIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _adapter.SendPacketAsync(requestPacket, _options.DefaultCommunicationTimeout);
|
await _adapter.SendPacketAsync(requestPacket, _options.DefaultCommunicationTimeout);
|
||||||
@@ -328,15 +354,16 @@ namespace MQTTnet.Core.Client
|
|||||||
catch (MqttCommunicationException exception)
|
catch (MqttCommunicationException exception)
|
||||||
{
|
{
|
||||||
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication error while receiving packets.");
|
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication error while receiving packets.");
|
||||||
|
await DisconnectInternalAsync();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttTrace.Warning(nameof(MqttClient), exception, "Error while sending/receiving keep alive packets.");
|
MqttTrace.Warning(nameof(MqttClient), exception, "Error while sending/receiving keep alive packets.");
|
||||||
|
await DisconnectInternalAsync();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
MqttTrace.Information(nameof(MqttClient), "Stopped sending keep alive packets.");
|
MqttTrace.Information(nameof(MqttClient), "Stopped sending keep alive packets.");
|
||||||
await DisconnectInternalAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,25 +374,47 @@ namespace MQTTnet.Core.Client
|
|||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var mqttPacket = await _adapter.ReceivePacketAsync(TimeSpan.Zero);
|
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero);
|
||||||
MqttTrace.Information(nameof(MqttClient), $"Received <<< {mqttPacket}");
|
MqttTrace.Information(nameof(MqttClient), $"Received <<< {packet}");
|
||||||
|
|
||||||
Task.Run(() => ProcessReceivedPacket(mqttPacket), cancellationToken).Forget();
|
StartProcessReceivedPacket(packet, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (MqttCommunicationException exception)
|
catch (MqttCommunicationException exception)
|
||||||
{
|
{
|
||||||
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication error while receiving packets.");
|
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while receiving packets.");
|
||||||
|
await DisconnectInternalAsync();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MqttTrace.Error(nameof(MqttClient), exception, "Error while receiving packets.");
|
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while receiving packets.");
|
||||||
|
await DisconnectInternalAsync();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
MqttTrace.Information(nameof(MqttClient), "Stopped receiving packets.");
|
MqttTrace.Information(nameof(MqttClient), "Stopped receiving packets.");
|
||||||
await DisconnectInternalAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
Task.Run(() => ProcessReceivedPacketAsync(packet), cancellationToken);
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartReceivePackets()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
Task.Run(() => ReceivePackets(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartSendKeepAliveMessages()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
Task.Run(() => SendKeepAliveMessagesAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Client
|
namespace MQTTnet.Core.Client
|
||||||
{
|
{
|
||||||
public class MqttClientOptions
|
public sealed class MqttClientOptions
|
||||||
{
|
{
|
||||||
public string Server { get; set; }
|
public string Server { get; set; }
|
||||||
|
|
||||||
public int Port { get; set; } = 1883;
|
public int? Port { get; set; }
|
||||||
|
|
||||||
|
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||||
|
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
|
|
||||||
@@ -19,5 +22,7 @@ namespace MQTTnet.Core.Client
|
|||||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5);
|
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public static class MqttClientOptionsExtensions
|
||||||
|
{
|
||||||
|
public static int GetPort(this MqttClientOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
if (options.Port.HasValue)
|
||||||
|
{
|
||||||
|
return options.Port.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !options.TlsOptions.UseTls ? 1883 : 8883;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Client
|
||||||
|
{
|
||||||
|
public sealed class MqttClientTlsOptions
|
||||||
|
{
|
||||||
|
public bool UseTls { get; set; }
|
||||||
|
|
||||||
|
public bool CheckCertificateRevocation { get; set; }
|
||||||
|
|
||||||
|
public List<byte[]> Certificates { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Diagnostics
|
namespace MQTTnet.Core.Diagnostics
|
||||||
{
|
{
|
||||||
public class MqttTraceMessagePublishedEventArgs : EventArgs
|
public sealed class MqttTraceMessagePublishedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttTraceMessagePublishedEventArgs(int threadId, string source, MqttTraceLevel level, string message, Exception exception)
|
public MqttTraceMessagePublishedEventArgs(int threadId, string source, MqttTraceLevel level, string message, Exception exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace MQTTnet.Core.Exceptions
|
|||||||
{
|
{
|
||||||
public class MqttCommunicationException : Exception
|
public class MqttCommunicationException : Exception
|
||||||
{
|
{
|
||||||
public MqttCommunicationException()
|
protected MqttCommunicationException()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,5 +17,10 @@ namespace MQTTnet.Core.Exceptions
|
|||||||
: base(message)
|
: base(message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttCommunicationException(string message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Exceptions
|
namespace MQTTnet.Core.Exceptions
|
||||||
{
|
{
|
||||||
public class MqttCommunicationTimedOutException : MqttCommunicationException
|
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Exceptions
|
namespace MQTTnet.Core.Exceptions
|
||||||
{
|
{
|
||||||
public class MqttProtocolViolationException : Exception
|
public sealed class MqttProtocolViolationException : Exception
|
||||||
{
|
{
|
||||||
public MqttProtocolViolationException(string message)
|
public MqttProtocolViolationException(string message)
|
||||||
: base(message)
|
: base(message)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Internal
|
namespace MQTTnet.Core.Internal
|
||||||
{
|
{
|
||||||
public class AsyncGate
|
public sealed class AsyncGate
|
||||||
{
|
{
|
||||||
private readonly Queue<TaskCompletionSource<bool>> _waitingTasks = new Queue<TaskCompletionSource<bool>>();
|
private readonly Queue<TaskCompletionSource<bool>> _waitingTasks = new Queue<TaskCompletionSource<bool>>();
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ namespace MQTTnet.Core.Internal
|
|||||||
{
|
{
|
||||||
internal static class MqttApplicationMessageExtensions
|
internal static class MqttApplicationMessageExtensions
|
||||||
{
|
{
|
||||||
|
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
|
||||||
|
{
|
||||||
|
return new MqttApplicationMessage(
|
||||||
|
publishPacket.Topic,
|
||||||
|
publishPacket.Payload,
|
||||||
|
publishPacket.QualityOfServiceLevel,
|
||||||
|
publishPacket.Retain
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
if (applicationMessage == null)
|
if (applicationMessage == null)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Core.Internal
|
|
||||||
{
|
|
||||||
internal static class TaskExtensions
|
|
||||||
{
|
|
||||||
public static void Forget(this Task task)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,20 +5,20 @@
|
|||||||
<AssemblyName>MQTTnet.Core</AssemblyName>
|
<AssemblyName>MQTTnet.Core</AssemblyName>
|
||||||
<RootNamespace>MQTTnet.Core</RootNamespace>
|
<RootNamespace>MQTTnet.Core</RootNamespace>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
<Description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</Description>
|
<Description></Description>
|
||||||
<Product>MQTTnet</Product>
|
<Product></Product>
|
||||||
<Company>Christian Kratky</Company>
|
<Company></Company>
|
||||||
<Authors>Christian Kratky</Authors>
|
<Authors></Authors>
|
||||||
<Version>2.1.2.0</Version>
|
<Version>0.0.0.0</Version>
|
||||||
<PackageId>MQTTnet.Core</PackageId>
|
<PackageId></PackageId>
|
||||||
<Copyright>Copyright © Christian Kratky 2016-2017</Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
|
<PackageProjectUrl></PackageProjectUrl>
|
||||||
<PackageIconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</PackageIconUrl>
|
<PackageIconUrl></PackageIconUrl>
|
||||||
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
|
<RepositoryUrl></RepositoryUrl>
|
||||||
<PackageTags>MQTT MQTTClient MQTTServer MQTTBroker Broker</PackageTags>
|
<PackageTags></PackageTags>
|
||||||
<FileVersion>2.1.2.0</FileVersion>
|
<FileVersion>2.1.5.1</FileVersion>
|
||||||
<AssemblyVersion>2.1.2.0</AssemblyVersion>
|
<AssemblyVersion>2.1.5.1</AssemblyVersion>
|
||||||
<PackageLicenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl></PackageLicenseUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core
|
namespace MQTTnet.Core
|
||||||
{
|
{
|
||||||
public class MqttApplicationMessage
|
public sealed class MqttApplicationMessage
|
||||||
{
|
{
|
||||||
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public interface IPacketWithIdentifier
|
public interface IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
ushort PacketIdentifier { get; set; }
|
ushort PacketIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,6 @@
|
|||||||
using System;
|
namespace MQTTnet.Core.Packets
|
||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
|
||||||
{
|
{
|
||||||
public abstract class MqttBasePacket
|
public abstract class MqttBasePacket
|
||||||
{
|
{
|
||||||
public TResponsePacket CreateResponse<TResponsePacket>()
|
|
||||||
{
|
|
||||||
var responsePacket = Activator.CreateInstance<TResponsePacket>();
|
|
||||||
var responsePacketWithIdentifier = responsePacket as IPacketWithIdentifier;
|
|
||||||
if (responsePacketWithIdentifier != null)
|
|
||||||
{
|
|
||||||
var requestPacketWithIdentifier = this as IPacketWithIdentifier;
|
|
||||||
if (requestPacketWithIdentifier == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
|
|
||||||
}
|
|
||||||
|
|
||||||
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
return responsePacket;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttBasePublishPacket : MqttBasePacket, IPacketWithIdentifier
|
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnAckPacket : MqttBasePacket
|
public sealed class MqttConnAckPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public bool IsSessionPresent { get; set; }
|
public bool IsSessionPresent { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttConnectPacket: MqttBasePacket
|
public sealed class MqttConnectPacket: MqttBasePacket
|
||||||
{
|
{
|
||||||
|
public MqttProtocolVersion ProtocolVersion { get; set; }
|
||||||
|
|
||||||
public string ClientId { get; set; }
|
public string ClientId { get; set; }
|
||||||
|
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttDisconnectPacket : MqttBasePacket
|
public sealed class MqttDisconnectPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Packets
|
||||||
|
{
|
||||||
|
public static class MqttPacketExtensions
|
||||||
|
{
|
||||||
|
public static TResponsePacket CreateResponse<TResponsePacket>(this MqttBasePacket packet)
|
||||||
|
{
|
||||||
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
|
|
||||||
|
var responsePacket = Activator.CreateInstance<TResponsePacket>();
|
||||||
|
|
||||||
|
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
|
||||||
|
{
|
||||||
|
var requestPacketWithIdentifier = packet as IMqttPacketWithIdentifier;
|
||||||
|
if (requestPacketWithIdentifier == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
|
||||||
|
}
|
||||||
|
|
||||||
|
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return responsePacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPingReqPacket : MqttBasePacket
|
public sealed class MqttPingReqPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPingRespPacket : MqttBasePacket
|
public sealed class MqttPingRespPacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubAckPacket : MqttBasePublishPacket
|
public sealed class MqttPubAckPacket : MqttBasePublishPacket
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubCompPacket : MqttBasePublishPacket
|
public sealed class MqttPubCompPacket : MqttBasePublishPacket
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRecPacket : MqttBasePublishPacket
|
public sealed class MqttPubRecPacket : MqttBasePublishPacket
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPubRelPacket : MqttBasePublishPacket
|
public sealed class MqttPubRelPacket : MqttBasePublishPacket
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttPublishPacket : MqttBasePublishPacket
|
public sealed class MqttPublishPacket : MqttBasePublishPacket
|
||||||
{
|
{
|
||||||
public bool Retain { get; set; }
|
public bool Retain { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubAckPacket : MqttBasePacket, IPacketWithIdentifier
|
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttSubscribePacket : MqttBasePacket, IPacketWithIdentifier
|
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubAckPacket : MqttBasePacket, IPacketWithIdentifier
|
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class MqttUnsubscribePacket : MqttBasePacket, IPacketWithIdentifier
|
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||||
{
|
{
|
||||||
public ushort PacketIdentifier { get; set; }
|
public ushort PacketIdentifier { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Packets
|
namespace MQTTnet.Core.Packets
|
||||||
{
|
{
|
||||||
public class TopicFilter
|
public sealed class TopicFilter
|
||||||
{
|
{
|
||||||
public TopicFilter(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
public TopicFilter(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Serializer
|
namespace MQTTnet.Core.Serializer
|
||||||
{
|
{
|
||||||
public class ByteReader
|
public sealed class ByteReader
|
||||||
{
|
{
|
||||||
private readonly int _source;
|
private readonly int _source;
|
||||||
private int _index;
|
private int _index;
|
||||||
@@ -24,7 +24,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte Read(int count)
|
public int Read(int count)
|
||||||
{
|
{
|
||||||
if (_index + count > 8)
|
if (_index + count > 8)
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
_index++;
|
_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte)result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Serializer
|
namespace MQTTnet.Core.Serializer
|
||||||
{
|
{
|
||||||
public class ByteWriter
|
public sealed class ByteWriter
|
||||||
{
|
{
|
||||||
private int _index;
|
private int _index;
|
||||||
private int _byte;
|
private int _byte;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace MQTTnet.Core.Serializer
|
|||||||
{
|
{
|
||||||
public interface IMqttPacketSerializer
|
public interface IMqttPacketSerializer
|
||||||
{
|
{
|
||||||
|
MqttProtocolVersion ProtocolVersion { get; set; }
|
||||||
|
|
||||||
Task SerializeAsync(MqttBasePacket mqttPacket, IMqttCommunicationChannel destination);
|
Task SerializeAsync(MqttBasePacket mqttPacket, IMqttCommunicationChannel destination);
|
||||||
|
|
||||||
Task<MqttBasePacket> DeserializeAsync(IMqttCommunicationChannel source);
|
Task<MqttBasePacket> DeserializeAsync(IMqttCommunicationChannel source);
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ namespace MQTTnet.Core.Serializer
|
|||||||
{
|
{
|
||||||
public sealed class MqttPacketReader : IDisposable
|
public sealed class MqttPacketReader : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MemoryStream _remainingData = new MemoryStream();
|
private readonly MemoryStream _remainingData = new MemoryStream(1024);
|
||||||
private readonly IMqttCommunicationChannel _source;
|
private readonly IMqttCommunicationChannel _source;
|
||||||
|
|
||||||
|
private int _remainingLength;
|
||||||
|
|
||||||
public MqttPacketReader(IMqttCommunicationChannel source)
|
public MqttPacketReader(IMqttCommunicationChannel source)
|
||||||
{
|
{
|
||||||
_source = source ?? throw new ArgumentNullException(nameof(source));
|
_source = source ?? throw new ArgumentNullException(nameof(source));
|
||||||
@@ -22,8 +24,6 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
public byte FixedHeader { get; private set; }
|
public byte FixedHeader { get; private set; }
|
||||||
|
|
||||||
public int RemainingLength { get; private set; }
|
|
||||||
|
|
||||||
public bool EndOfRemainingData => _remainingData.Position == _remainingData.Length;
|
public bool EndOfRemainingData => _remainingData.Position == _remainingData.Length;
|
||||||
|
|
||||||
public async Task ReadToEndAsync()
|
public async Task ReadToEndAsync()
|
||||||
@@ -31,54 +31,18 @@ namespace MQTTnet.Core.Serializer
|
|||||||
await ReadFixedHeaderAsync();
|
await ReadFixedHeaderAsync();
|
||||||
await ReadRemainingLengthAsync();
|
await ReadRemainingLengthAsync();
|
||||||
|
|
||||||
if (RemainingLength == 0)
|
if (_remainingLength == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = new byte[RemainingLength];
|
var buffer = new byte[_remainingLength];
|
||||||
await _source.ReadAsync(buffer);
|
await ReadFromSourceAsync(buffer);
|
||||||
|
|
||||||
_remainingData.Write(buffer, 0, buffer.Length);
|
_remainingData.Write(buffer, 0, buffer.Length);
|
||||||
_remainingData.Position = 0;
|
_remainingData.Position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReadFixedHeaderAsync()
|
|
||||||
{
|
|
||||||
FixedHeader = await ReadStreamByteAsync();
|
|
||||||
|
|
||||||
var byteReader = new ByteReader(FixedHeader);
|
|
||||||
byteReader.Read(4);
|
|
||||||
ControlPacketType = (MqttControlPacketType)byteReader.Read(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<byte> ReadStreamByteAsync()
|
|
||||||
{
|
|
||||||
var buffer = new byte[1];
|
|
||||||
await _source.ReadAsync(buffer);
|
|
||||||
return buffer[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ReadRemainingLengthAsync()
|
|
||||||
{
|
|
||||||
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
|
||||||
var multiplier = 1;
|
|
||||||
var value = 0;
|
|
||||||
byte encodedByte;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
encodedByte = await ReadStreamByteAsync();
|
|
||||||
value += (encodedByte & 127) * multiplier;
|
|
||||||
multiplier *= 128;
|
|
||||||
if (multiplier > 128 * 128 * 128)
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("Remaining length is ivalid.");
|
|
||||||
}
|
|
||||||
} while ((encodedByte & 128) != 0);
|
|
||||||
|
|
||||||
RemainingLength = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<byte> ReadRemainingDataByteAsync()
|
public async Task<byte> ReadRemainingDataByteAsync()
|
||||||
{
|
{
|
||||||
return (await ReadRemainingDataAsync(1))[0];
|
return (await ReadRemainingDataAsync(1))[0];
|
||||||
@@ -109,7 +73,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
public Task<byte[]> ReadRemainingDataAsync()
|
public Task<byte[]> ReadRemainingDataAsync()
|
||||||
{
|
{
|
||||||
return ReadRemainingDataAsync(RemainingLength - (int)_remainingData.Position);
|
return ReadRemainingDataAsync(_remainingLength - (int)_remainingData.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> ReadRemainingDataAsync(int length)
|
public async Task<byte[]> ReadRemainingDataAsync(int length)
|
||||||
@@ -120,6 +84,54 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ReadRemainingLengthAsync()
|
||||||
|
{
|
||||||
|
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
||||||
|
var multiplier = 1;
|
||||||
|
var value = 0;
|
||||||
|
byte encodedByte;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
encodedByte = await ReadStreamByteAsync();
|
||||||
|
value += (encodedByte & 127) * multiplier;
|
||||||
|
multiplier *= 128;
|
||||||
|
if (multiplier > 128 * 128 * 128)
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException("Remaining length is ivalid.");
|
||||||
|
}
|
||||||
|
} while ((encodedByte & 128) != 0);
|
||||||
|
|
||||||
|
_remainingLength = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task ReadFromSourceAsync(byte[] buffer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _source.ReadAsync(buffer);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<byte> ReadStreamByteAsync()
|
||||||
|
{
|
||||||
|
var buffer = new byte[1];
|
||||||
|
await ReadFromSourceAsync(buffer);
|
||||||
|
return buffer[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReadFixedHeaderAsync()
|
||||||
|
{
|
||||||
|
FixedHeader = await ReadStreamByteAsync();
|
||||||
|
|
||||||
|
var byteReader = new ByteReader(FixedHeader);
|
||||||
|
byteReader.Read(4);
|
||||||
|
ControlPacketType = (MqttControlPacketType)byteReader.Read(4);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_remainingData?.Dispose();
|
_remainingData?.Dispose();
|
||||||
|
|||||||
+90
-76
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Channel;
|
using MQTTnet.Core.Channel;
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
@@ -10,93 +9,84 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Serializer
|
namespace MQTTnet.Core.Serializer
|
||||||
{
|
{
|
||||||
public class DefaultMqttV311PacketSerializer : IMqttPacketSerializer
|
public sealed class MqttPacketSerializer : IMqttPacketSerializer
|
||||||
{
|
{
|
||||||
|
private static byte[] ProtocolVersionV311Name { get; } = Encoding.UTF8.GetBytes("MQTT");
|
||||||
|
private static byte[] ProtocolVersionV310Name { get; } = Encoding.UTF8.GetBytes("MQIs");
|
||||||
|
|
||||||
|
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||||
|
|
||||||
public Task SerializeAsync(MqttBasePacket packet, IMqttCommunicationChannel destination)
|
public Task SerializeAsync(MqttBasePacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||||
|
|
||||||
var connectPacket = packet as MqttConnectPacket;
|
if (packet is MqttConnectPacket connectPacket)
|
||||||
if (connectPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(connectPacket, destination);
|
return SerializeAsync(connectPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var connAckPacket = packet as MqttConnAckPacket;
|
if (packet is MqttConnAckPacket connAckPacket)
|
||||||
if (connAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(connAckPacket, destination);
|
return SerializeAsync(connAckPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var disconnectPacket = packet as MqttDisconnectPacket;
|
if (packet is MqttDisconnectPacket disconnectPacket)
|
||||||
if (disconnectPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(disconnectPacket, destination);
|
return SerializeAsync(disconnectPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pingReqPacket = packet as MqttPingReqPacket;
|
if (packet is MqttPingReqPacket pingReqPacket)
|
||||||
if (pingReqPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pingReqPacket, destination);
|
return SerializeAsync(pingReqPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pingRespPacket = packet as MqttPingRespPacket;
|
if (packet is MqttPingRespPacket pingRespPacket)
|
||||||
if (pingRespPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pingRespPacket, destination);
|
return SerializeAsync(pingRespPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = packet as MqttPublishPacket;
|
if (packet is MqttPublishPacket publishPacket)
|
||||||
if (publishPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(publishPacket, destination);
|
return SerializeAsync(publishPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubAckPacket = packet as MqttPubAckPacket;
|
if (packet is MqttPubAckPacket pubAckPacket)
|
||||||
if (pubAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pubAckPacket, destination);
|
return SerializeAsync(pubAckPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRecPacket = packet as MqttPubRecPacket;
|
if (packet is MqttPubRecPacket pubRecPacket)
|
||||||
if (pubRecPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pubRecPacket, destination);
|
return SerializeAsync(pubRecPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRelPacket = packet as MqttPubRelPacket;
|
if (packet is MqttPubRelPacket pubRelPacket)
|
||||||
if (pubRelPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pubRelPacket, destination);
|
return SerializeAsync(pubRelPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubCompPacket = packet as MqttPubCompPacket;
|
if (packet is MqttPubCompPacket pubCompPacket)
|
||||||
if (pubCompPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(pubCompPacket, destination);
|
return SerializeAsync(pubCompPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscribePacket = packet as MqttSubscribePacket;
|
if (packet is MqttSubscribePacket subscribePacket)
|
||||||
if (subscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(subscribePacket, destination);
|
return SerializeAsync(subscribePacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var subAckPacket = packet as MqttSubAckPacket;
|
if (packet is MqttSubAckPacket subAckPacket)
|
||||||
if (subAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(subAckPacket, destination);
|
return SerializeAsync(subAckPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var unsubscribePacket = packet as MqttUnsubscribePacket;
|
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
||||||
if (unsubscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(unsubscribePacket, destination);
|
return SerializeAsync(unsubscribePacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
var unsubAckPacket = packet as MqttUnsubAckPacket;
|
if (packet is MqttUnsubAckPacket unsubAckPacket)
|
||||||
if (unsubAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return SerializeAsync(unsubAckPacket, destination);
|
return SerializeAsync(unsubAckPacket, destination);
|
||||||
}
|
}
|
||||||
@@ -201,13 +191,13 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Packet type not supported.");
|
throw new MqttProtocolViolationException($"Packet type ({(int)mqttPacketReader.ControlPacketType}) not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializeUnsubscribeAsync(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializeUnsubscribeAsync(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var packet = new MqttUnsubscribePacket
|
var packet = new MqttUnsubscribePacket
|
||||||
{
|
{
|
||||||
@@ -222,7 +212,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializeSubscribeAsync(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializeSubscribeAsync(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var packet = new MqttSubscribePacket
|
var packet = new MqttSubscribePacket
|
||||||
{
|
{
|
||||||
@@ -239,7 +229,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializePublishAsync(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializePublishAsync(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var fixedHeader = new ByteReader(reader.FixedHeader);
|
var fixedHeader = new ByteReader(reader.FixedHeader);
|
||||||
var retain = fixedHeader.Read();
|
var retain = fixedHeader.Read();
|
||||||
@@ -267,18 +257,24 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializeConnectAsync(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializeConnectAsync(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var packet = new MqttConnectPacket();
|
await reader.ReadRemainingDataAsync(2); // Skip 2 bytes
|
||||||
|
|
||||||
await reader.ReadRemainingDataByteAsync();
|
|
||||||
await reader.ReadRemainingDataByteAsync();
|
|
||||||
|
|
||||||
|
MqttProtocolVersion protocolVersion;
|
||||||
var protocolName = await reader.ReadRemainingDataAsync(4);
|
var protocolName = await reader.ReadRemainingDataAsync(4);
|
||||||
|
if (protocolName.SequenceEqual(ProtocolVersionV310Name))
|
||||||
if (Encoding.UTF8.GetString(protocolName, 0, protocolName.Length) != "MQTT")
|
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Protocol name is not 'MQTT'.");
|
await reader.ReadRemainingDataAsync(2);
|
||||||
|
protocolVersion = MqttProtocolVersion.V310;
|
||||||
|
}
|
||||||
|
else if (protocolName.SequenceEqual(ProtocolVersionV311Name))
|
||||||
|
{
|
||||||
|
protocolVersion = MqttProtocolVersion.V311;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException("Protocol name is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocolLevel = await reader.ReadRemainingDataByteAsync();
|
var protocolLevel = await reader.ReadRemainingDataByteAsync();
|
||||||
@@ -286,7 +282,13 @@ namespace MQTTnet.Core.Serializer
|
|||||||
|
|
||||||
var connectFlagsReader = new ByteReader(connectFlags);
|
var connectFlagsReader = new ByteReader(connectFlags);
|
||||||
connectFlagsReader.Read(); // Reserved.
|
connectFlagsReader.Read(); // Reserved.
|
||||||
packet.CleanSession = connectFlagsReader.Read();
|
|
||||||
|
var packet = new MqttConnectPacket
|
||||||
|
{
|
||||||
|
ProtocolVersion = protocolVersion,
|
||||||
|
CleanSession = connectFlagsReader.Read()
|
||||||
|
};
|
||||||
|
|
||||||
var willFlag = connectFlagsReader.Read();
|
var willFlag = connectFlagsReader.Read();
|
||||||
var willQoS = connectFlagsReader.Read(2);
|
var willQoS = connectFlagsReader.Read(2);
|
||||||
var willRetain = connectFlagsReader.Read();
|
var willRetain = connectFlagsReader.Read();
|
||||||
@@ -319,7 +321,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializeSubAck(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializeSubAck(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var packet = new MqttSubAckPacket
|
var packet = new MqttSubAckPacket
|
||||||
{
|
{
|
||||||
@@ -334,7 +336,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MqttBasePacket> DeserializeConnAck(MqttPacketReader reader)
|
private static async Task<MqttBasePacket> DeserializeConnAck(MqttPacketReader reader)
|
||||||
{
|
{
|
||||||
var variableHeader1 = await reader.ReadRemainingDataByteAsync();
|
var variableHeader1 = await reader.ReadRemainingDataByteAsync();
|
||||||
var variableHeader2 = await reader.ReadRemainingDataByteAsync();
|
var variableHeader2 = await reader.ReadRemainingDataByteAsync();
|
||||||
@@ -348,7 +350,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateConnectPacket(MqttConnectPacket packet)
|
private static void ValidateConnectPacket(MqttConnectPacket packet)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
||||||
{
|
{
|
||||||
@@ -356,7 +358,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidatePublishPacket(MqttPublishPacket packet)
|
private static void ValidatePublishPacket(MqttPublishPacket packet)
|
||||||
{
|
{
|
||||||
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
||||||
{
|
{
|
||||||
@@ -364,8 +366,6 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly byte[] MqttPrefix = Encoding.UTF8.GetBytes("MQTT");
|
|
||||||
|
|
||||||
private Task SerializeAsync(MqttConnectPacket packet, IMqttCommunicationChannel destination)
|
private Task SerializeAsync(MqttConnectPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
ValidateConnectPacket(packet);
|
ValidateConnectPacket(packet);
|
||||||
@@ -374,9 +374,19 @@ namespace MQTTnet.Core.Serializer
|
|||||||
{
|
{
|
||||||
// Write variable header
|
// Write variable header
|
||||||
output.Write(0x00, 0x04); // 3.1.2.1 Protocol Name
|
output.Write(0x00, 0x04); // 3.1.2.1 Protocol Name
|
||||||
output.Write(MqttPrefix);
|
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||||
output.Write(0x04); // 3.1.2.2 Protocol Level
|
{
|
||||||
|
output.Write(ProtocolVersionV311Name);
|
||||||
|
output.Write(0x04); // 3.1.2.2 Protocol Level (4)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.Write(ProtocolVersionV310Name);
|
||||||
|
output.Write(0x64);
|
||||||
|
output.Write(0x70);
|
||||||
|
output.Write(0x03); // Protocol Level (3)
|
||||||
|
}
|
||||||
|
|
||||||
var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags
|
var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags
|
||||||
connectFlags.Write(false); // Reserved
|
connectFlags.Write(false); // Reserved
|
||||||
connectFlags.Write(packet.CleanSession);
|
connectFlags.Write(packet.CleanSession);
|
||||||
@@ -426,8 +436,12 @@ namespace MQTTnet.Core.Serializer
|
|||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
var connectAcknowledgeFlags = new ByteWriter();
|
var connectAcknowledgeFlags = new ByteWriter();
|
||||||
connectAcknowledgeFlags.Write(packet.IsSessionPresent);
|
|
||||||
|
|
||||||
|
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||||
|
{
|
||||||
|
connectAcknowledgeFlags.Write(packet.IsSessionPresent);
|
||||||
|
}
|
||||||
|
|
||||||
output.Write(connectAcknowledgeFlags);
|
output.Write(connectAcknowledgeFlags);
|
||||||
output.Write((byte)packet.ConnectReturnCode);
|
output.Write((byte)packet.ConnectReturnCode);
|
||||||
|
|
||||||
@@ -436,22 +450,33 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttDisconnectPacket packet, IMqttCommunicationChannel destination)
|
private static async Task SerializeAsync(MqttPubRelPacket packet, IMqttCommunicationChannel destination)
|
||||||
|
{
|
||||||
|
using (var output = new MqttPacketWriter())
|
||||||
|
{
|
||||||
|
output.Write(packet.PacketIdentifier);
|
||||||
|
|
||||||
|
output.InjectFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||||
|
await output.WriteToAsync(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task SerializeAsync(MqttDisconnectPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, destination);
|
return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPingReqPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPingReqPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, destination);
|
return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPingRespPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPingRespPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, destination);
|
return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPublishPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPublishPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
ValidatePublishPacket(packet);
|
ValidatePublishPacket(packet);
|
||||||
|
|
||||||
@@ -486,7 +511,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPubAckPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPubAckPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -497,7 +522,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPubRecPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPubRecPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -508,18 +533,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SerializeAsync(MqttPubRelPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttPubCompPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
|
||||||
using (var output = new MqttPacketWriter())
|
|
||||||
{
|
|
||||||
output.Write(packet.PacketIdentifier);
|
|
||||||
|
|
||||||
output.InjectFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
|
||||||
await output.WriteToAsync(destination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task SerializeAsync(MqttPubCompPacket packet, IMqttCommunicationChannel destination)
|
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -530,7 +544,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttSubscribePacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttSubscribePacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -550,7 +564,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttSubAckPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttSubAckPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -569,7 +583,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttUnsubscribePacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttUnsubscribePacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -588,7 +602,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeAsync(MqttUnsubAckPacket packet, IMqttCommunicationChannel destination)
|
private static Task SerializeAsync(MqttUnsubAckPacket packet, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -599,7 +613,7 @@ namespace MQTTnet.Core.Serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SerializeEmptyPacketAsync(MqttControlPacketType type, IMqttCommunicationChannel destination)
|
private static Task SerializeEmptyPacketAsync(MqttControlPacketType type, IMqttCommunicationChannel destination)
|
||||||
{
|
{
|
||||||
using (var output = new MqttPacketWriter())
|
using (var output = new MqttPacketWriter())
|
||||||
{
|
{
|
||||||
@@ -9,46 +9,13 @@ namespace MQTTnet.Core.Serializer
|
|||||||
{
|
{
|
||||||
public sealed class MqttPacketWriter : IDisposable
|
public sealed class MqttPacketWriter : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MemoryStream _buffer = new MemoryStream(512);
|
private readonly MemoryStream _buffer = new MemoryStream(1024);
|
||||||
|
|
||||||
public void InjectFixedHeader(byte fixedHeader)
|
|
||||||
{
|
|
||||||
if (_buffer.Length == 0)
|
|
||||||
{
|
|
||||||
Write(fixedHeader);
|
|
||||||
Write(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var backupBuffer = _buffer.ToArray();
|
|
||||||
var remainingLength = (int)_buffer.Length;
|
|
||||||
|
|
||||||
_buffer.SetLength(0);
|
|
||||||
|
|
||||||
_buffer.WriteByte(fixedHeader);
|
|
||||||
|
|
||||||
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
|
||||||
var x = remainingLength;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
var encodedByte = x % 128;
|
|
||||||
x = x / 128;
|
|
||||||
if (x > 0)
|
|
||||||
{
|
|
||||||
encodedByte = encodedByte | 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer.WriteByte((byte)encodedByte);
|
|
||||||
} while (x > 0);
|
|
||||||
|
|
||||||
_buffer.Write(backupBuffer, 0, backupBuffer.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InjectFixedHeader(MqttControlPacketType packetType, byte flags = 0)
|
public void InjectFixedHeader(MqttControlPacketType packetType, byte flags = 0)
|
||||||
{
|
{
|
||||||
var fixedHeader = (byte)((byte)packetType << 4);
|
var fixedHeader = (int)packetType << 4;
|
||||||
fixedHeader |= flags;
|
fixedHeader |= flags;
|
||||||
InjectFixedHeader(fixedHeader);
|
InjectFixedHeader((byte)fixedHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(byte value)
|
public void Write(byte value)
|
||||||
@@ -101,5 +68,38 @@ namespace MQTTnet.Core.Serializer
|
|||||||
{
|
{
|
||||||
_buffer?.Dispose();
|
_buffer?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InjectFixedHeader(byte fixedHeader)
|
||||||
|
{
|
||||||
|
if (_buffer.Length == 0)
|
||||||
|
{
|
||||||
|
Write(fixedHeader);
|
||||||
|
Write(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var backupBuffer = _buffer.ToArray();
|
||||||
|
var remainingLength = (int)_buffer.Length;
|
||||||
|
|
||||||
|
_buffer.SetLength(0);
|
||||||
|
|
||||||
|
_buffer.WriteByte(fixedHeader);
|
||||||
|
|
||||||
|
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
||||||
|
var x = remainingLength;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var encodedByte = x % 128;
|
||||||
|
x = x / 128;
|
||||||
|
if (x > 0)
|
||||||
|
{
|
||||||
|
encodedByte = encodedByte | 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer.WriteByte((byte)encodedByte);
|
||||||
|
} while (x > 0);
|
||||||
|
|
||||||
|
_buffer.Write(backupBuffer, 0, backupBuffer.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace MQTTnet.Core.Serializer
|
||||||
|
{
|
||||||
|
public enum MqttProtocolVersion
|
||||||
|
{
|
||||||
|
V311,
|
||||||
|
V310
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public class ConnectedMqttClient
|
||||||
|
{
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
|
||||||
|
public MqttProtocolVersion ProtocolVersion { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class GetOrCreateClientSessionResult
|
public sealed class GetOrCreateClientSessionResult
|
||||||
{
|
{
|
||||||
public bool IsExistingSession { get; set; }
|
public bool IsExistingSession { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public interface IMqttServer
|
||||||
|
{
|
||||||
|
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
IList<ConnectedMqttClient> GetConnectedClients();
|
||||||
|
void InjectClient(string identifier, IMqttCommunicationAdapter adapter);
|
||||||
|
void Publish(MqttApplicationMessage applicationMessage);
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage)
|
||||||
|
{
|
||||||
|
ClientId = clientId;
|
||||||
|
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ClientId { get; }
|
||||||
|
|
||||||
|
public MqttApplicationMessage ApplicationMessage { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ using MQTTnet.Core.Packets;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientMessageQueue
|
public sealed class MqttClientMessageQueue
|
||||||
{
|
{
|
||||||
private readonly List<MqttClientPublishPacketContext> _pendingPublishPackets = new List<MqttClientPublishPacketContext>();
|
private readonly List<MqttClientPublishPacketContext> _pendingPublishPackets = new List<MqttClientPublishPacketContext>();
|
||||||
private readonly AsyncGate _gate = new AsyncGate();
|
private readonly AsyncGate _gate = new AsyncGate();
|
||||||
@@ -35,7 +35,7 @@ namespace MQTTnet.Core.Server
|
|||||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
Task.Run(() => SendPendingPublishPacketsAsync(_cancellationTokenSource.Token));
|
Task.Run(() => SendPendingPublishPacketsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
@@ -45,14 +45,13 @@ namespace MQTTnet.Core.Server
|
|||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enqueue(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
public void Enqueue(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
|
|
||||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||||
|
|
||||||
lock (_pendingPublishPackets)
|
lock (_pendingPublishPackets)
|
||||||
{
|
{
|
||||||
_pendingPublishPackets.Add(new MqttClientPublishPacketContext(senderClientSession, publishPacket));
|
_pendingPublishPackets.Add(new MqttClientPublishPacketContext(publishPacket));
|
||||||
_gate.Set();
|
_gate.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,13 @@ using MQTTnet.Core.Packets;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientPublishPacketContext
|
public sealed class MqttClientPublishPacketContext
|
||||||
{
|
{
|
||||||
public MqttClientPublishPacketContext(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
public MqttClientPublishPacketContext(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
SenderClientSession = senderClientSession ?? throw new ArgumentNullException(nameof(senderClientSession));
|
|
||||||
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttClientSession SenderClientSession { get; }
|
|
||||||
|
|
||||||
public MqttPublishPacket PublishPacket { get; }
|
public MqttPublishPacket PublishPacket { get; }
|
||||||
|
|
||||||
public int SendTries { get; set; }
|
public int SendTries { get; set; }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
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;
|
||||||
@@ -13,7 +13,7 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientSession : IDisposable
|
public sealed class MqttClientSession : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingIncomingPublications = new ConcurrentDictionary<ushort, MqttPublishPacket>();
|
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||||
|
|
||||||
private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager();
|
private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager();
|
||||||
private readonly MqttClientMessageQueue _messageQueue;
|
private readonly MqttClientMessageQueue _messageQueue;
|
||||||
@@ -21,19 +21,23 @@ namespace MQTTnet.Core.Server
|
|||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private IMqttCommunicationAdapter _adapter;
|
|
||||||
private string _identifier;
|
private string _identifier;
|
||||||
private MqttApplicationMessage _willApplicationMessage;
|
private MqttApplicationMessage _willApplicationMessage;
|
||||||
|
|
||||||
public MqttClientSession(MqttServerOptions options, Action<MqttClientSession, MqttPublishPacket> publishPacketReceivedCallback)
|
public MqttClientSession(string clientId, MqttServerOptions options, Action<MqttClientSession, MqttPublishPacket> publishPacketReceivedCallback)
|
||||||
{
|
{
|
||||||
|
ClientId = clientId;
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_publishPacketReceivedCallback = publishPacketReceivedCallback ?? throw new ArgumentNullException(nameof(publishPacketReceivedCallback));
|
_publishPacketReceivedCallback = publishPacketReceivedCallback ?? throw new ArgumentNullException(nameof(publishPacketReceivedCallback));
|
||||||
|
|
||||||
_messageQueue = new MqttClientMessageQueue(options);
|
_messageQueue = new MqttClientMessageQueue(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected => _adapter != null;
|
public string ClientId { get; }
|
||||||
|
|
||||||
|
public bool IsConnected => Adapter != null;
|
||||||
|
|
||||||
|
public IMqttCommunicationAdapter Adapter { get; private set; }
|
||||||
|
|
||||||
public async Task RunAsync(string identifier, MqttApplicationMessage willApplicationMessage, IMqttCommunicationAdapter adapter)
|
public async Task RunAsync(string identifier, MqttApplicationMessage willApplicationMessage, IMqttCommunicationAdapter adapter)
|
||||||
{
|
{
|
||||||
@@ -44,7 +48,7 @@ namespace MQTTnet.Core.Server
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_identifier = identifier;
|
_identifier = identifier;
|
||||||
_adapter = adapter;
|
Adapter = adapter;
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
_messageQueue.Start(adapter);
|
_messageQueue.Start(adapter);
|
||||||
@@ -70,23 +74,22 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
_messageQueue.Stop();
|
_messageQueue.Stop();
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
_adapter = null;
|
Adapter = null;
|
||||||
|
|
||||||
MqttTrace.Information(nameof(MqttClientSession), $"Client '{_identifier}': Disconnected.");
|
MqttTrace.Information(nameof(MqttClientSession), $"Client '{_identifier}': Disconnected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnqueuePublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
|
|
||||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||||
|
|
||||||
if (!_subscriptionsManager.IsTopicSubscribed(publishPacket))
|
if (!_subscriptionsManager.IsSubscribed(publishPacket))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_messageQueue.Enqueue(senderClientSession, publishPacket);
|
_messageQueue.Enqueue(publishPacket);
|
||||||
MqttTrace.Verbose(nameof(MqttClientSession), $"Client '{_identifier}: Enqueued pending publish packet.");
|
MqttTrace.Verbose(nameof(MqttClientSession), $"Client '{_identifier}: Enqueued pending publish packet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,39 +101,40 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
private Task HandleIncomingPacketAsync(MqttBasePacket packet)
|
private Task HandleIncomingPacketAsync(MqttBasePacket packet)
|
||||||
{
|
{
|
||||||
var subscribePacket = packet as MqttSubscribePacket;
|
if (packet is MqttSubscribePacket subscribePacket)
|
||||||
if (subscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
|
return Adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
var unsubscribePacket = packet as MqttUnsubscribePacket;
|
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
||||||
if (unsubscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
|
return Adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = packet as MqttPublishPacket;
|
if (packet is MqttPublishPacket publishPacket)
|
||||||
if (publishPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPublishPacketAsync(publishPacket);
|
return HandleIncomingPublishPacketAsync(publishPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRelPacket = packet as MqttPubRelPacket;
|
if (packet is MqttPubRelPacket pubRelPacket)
|
||||||
if (pubRelPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPubRelPacketAsync(pubRelPacket);
|
return HandleIncomingPubRelPacketAsync(pubRelPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubAckPacket = packet as MqttPubAckPacket;
|
if (packet is MqttPubRecPacket pubRecPacket)
|
||||||
if (pubAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPubAckPacketAsync(pubAckPacket);
|
return Adapter.SendPacketAsync(pubRecPacket.CreateResponse<MqttPubRelPacket>(), _options.DefaultCommunicationTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
|
||||||
|
{
|
||||||
|
// Discard message.
|
||||||
|
return Task.FromResult((object)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet is MqttPingReqPacket)
|
if (packet is MqttPingReqPacket)
|
||||||
{
|
{
|
||||||
return _adapter.SendPacketAsync(new MqttPingRespPacket(), _options.DefaultCommunicationTimeout);
|
return Adapter.SendPacketAsync(new MqttPingRespPacket(), _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
||||||
@@ -145,39 +149,44 @@ namespace MQTTnet.Core.Server
|
|||||||
return Task.FromResult((object)null);
|
return Task.FromResult((object)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleIncomingPubAckPacketAsync(MqttPubAckPacket pubAckPacket)
|
private Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||||
{
|
|
||||||
await Task.FromResult((object)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
|
||||||
{
|
{
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
_publishPacketReceivedCallback(this, publishPacket);
|
_publishPacketReceivedCallback(this, publishPacket);
|
||||||
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
|
||||||
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
{
|
{
|
||||||
await _adapter.SendPacketAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
|
||||||
_publishPacketReceivedCallback(this, publishPacket);
|
_publishPacketReceivedCallback(this, publishPacket);
|
||||||
|
return Adapter.SendPacketAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
|
||||||
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
{
|
{
|
||||||
_pendingIncomingPublications[publishPacket.PacketIdentifier] = publishPacket;
|
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
||||||
await _adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
lock (_unacknowledgedPublishPackets)
|
||||||
|
{
|
||||||
|
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
_publishPacketReceivedCallback(this, publishPacket);
|
||||||
|
|
||||||
|
return Adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
|
private Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
|
||||||
{
|
{
|
||||||
MqttPublishPacket publishPacket;
|
lock (_unacknowledgedPublishPackets)
|
||||||
if (!_pendingIncomingPublications.TryRemove(pubRelPacket.PacketIdentifier, out publishPacket))
|
|
||||||
{
|
{
|
||||||
return;
|
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
return Adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||||
_publishPacketReceivedCallback(this, publishPacket);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ using System.Threading.Tasks;
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Diagnostics;
|
using MQTTnet.Core.Diagnostics;
|
||||||
using MQTTnet.Core.Exceptions;
|
using MQTTnet.Core.Exceptions;
|
||||||
|
using MQTTnet.Core.Internal;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSessionsManager
|
public sealed class MqttClientSessionsManager
|
||||||
{
|
{
|
||||||
private readonly object _syncRoot = new object();
|
private readonly object _syncRoot = new object();
|
||||||
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
||||||
@@ -21,6 +22,8 @@ namespace MQTTnet.Core.Server
|
|||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs)
|
public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -31,6 +34,9 @@ namespace MQTTnet.Core.Server
|
|||||||
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].");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch to the required protocol version before sending any response.
|
||||||
|
eventArgs.ClientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
|
||||||
|
|
||||||
var connectReturnCode = ValidateConnection(connectPacket);
|
var connectReturnCode = ValidateConnection(connectPacket);
|
||||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||||
{
|
{
|
||||||
@@ -70,11 +76,15 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<string> GetConnectedClients()
|
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||||
{
|
{
|
||||||
lock (_syncRoot)
|
lock (_syncRoot)
|
||||||
{
|
{
|
||||||
return _clientSessions.Where(s => s.Value.IsConnected).Select(s => s.Key).ToList();
|
return _clientSessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
|
||||||
|
{
|
||||||
|
ClientId = s.Value.ClientId,
|
||||||
|
ProtocolVersion = s.Value.Adapter.PacketSerializer.ProtocolVersion
|
||||||
|
}).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +124,7 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
isExistingSession = false;
|
isExistingSession = false;
|
||||||
|
|
||||||
clientSession = new MqttClientSession(_options, DispatchPublishPacket);
|
clientSession = new MqttClientSession(connectPacket.ClientId, _options, DispatchPublishPacket);
|
||||||
_clientSessions[connectPacket.ClientId] = clientSession;
|
_clientSessions[connectPacket.ClientId] = clientSession;
|
||||||
|
|
||||||
MqttTrace.Verbose(nameof(MqttClientSessionsManager), $"Created a new session for client '{connectPacket.ClientId}'.");
|
MqttTrace.Verbose(nameof(MqttClientSessionsManager), $"Created a new session for client '{connectPacket.ClientId}'.");
|
||||||
@@ -124,11 +134,24 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
try
|
||||||
{
|
{
|
||||||
clientSession.EnqueuePublishPacket(senderClientSession, publishPacket);
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage());
|
||||||
|
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MqttTrace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message");
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
foreach (var clientSession in _clientSessions.Values.ToList())
|
||||||
|
{
|
||||||
|
clientSession.EnqueuePublishPacket(publishPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSubscriptionsManager
|
public sealed class MqttClientSubscriptionsManager
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new ConcurrentDictionary<string, MqttQualityOfServiceLevel>();
|
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||||
|
|
||||||
public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket)
|
public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket)
|
||||||
{
|
{
|
||||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||||
|
|
||||||
var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
|
var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
|
||||||
foreach (var topicFilter in subscribePacket.TopicFilters)
|
|
||||||
|
lock (_subscribedTopics)
|
||||||
{
|
{
|
||||||
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
foreach (var topicFilter in subscribePacket.TopicFilters)
|
||||||
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
|
{
|
||||||
|
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
||||||
|
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return responsePacket;
|
return responsePacket;
|
||||||
@@ -27,32 +31,37 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||||
|
|
||||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
lock (_subscribedTopics)
|
||||||
{
|
{
|
||||||
MqttQualityOfServiceLevel _;
|
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||||
_subscribedTopics.TryRemove(topicFilter, out _);
|
{
|
||||||
|
_subscribedTopics.Remove(topicFilter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
|
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTopicSubscribed(MqttPublishPacket publishPacket)
|
public bool IsSubscribed(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||||
|
|
||||||
foreach (var subscribedTopic in _subscribedTopics)
|
lock (_subscribedTopics)
|
||||||
{
|
{
|
||||||
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
|
foreach (var subscribedTopic in _subscribedTopics)
|
||||||
{
|
{
|
||||||
continue;
|
if (publishPacket.QualityOfServiceLevel > subscribedTopic.Value)
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (subscribedTopic.Value < publishPacket.QualityOfServiceLevel)
|
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -4,32 +4,43 @@ 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.Diagnostics;
|
||||||
|
using MQTTnet.Core.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttServer
|
public sealed class MqttServer : IMqttServer
|
||||||
{
|
{
|
||||||
private readonly MqttClientSessionsManager _clientSessionsManager;
|
private readonly MqttClientSessionsManager _clientSessionsManager;
|
||||||
private readonly IMqttServerAdapter _adapter;
|
private readonly ICollection<IMqttServerAdapter> _adapters;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly MqttServerOptions _options;
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
public MqttServer(MqttServerOptions options, IMqttServerAdapter adapter)
|
public MqttServer(MqttServerOptions options, ICollection<IMqttServerAdapter> adapters)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
_adapters = adapters ?? throw new ArgumentNullException(nameof(adapters));
|
||||||
|
|
||||||
_clientSessionsManager = new MqttClientSessionsManager(options);
|
_clientSessionsManager = new MqttClientSessionsManager(options);
|
||||||
|
_clientSessionsManager.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<string> GetConnectedClients()
|
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||||
{
|
{
|
||||||
return _clientSessionsManager.GetConnectedClients();
|
return _clientSessionsManager.GetConnectedClients();
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
|
public void Publish(MqttApplicationMessage applicationMessage)
|
||||||
|
{
|
||||||
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
|
_clientSessionsManager.DispatchPublishPacket(null, applicationMessage.ToPublishPacket());
|
||||||
|
}
|
||||||
|
|
||||||
public void InjectClient(string identifier, IMqttCommunicationAdapter adapter)
|
public void InjectClient(string identifier, IMqttCommunicationAdapter adapter)
|
||||||
{
|
{
|
||||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||||
@@ -45,19 +56,26 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
_adapter.ClientConnected += OnClientConnected;
|
foreach (var adapter in _adapters)
|
||||||
_adapter.Start(_options);
|
{
|
||||||
|
adapter.ClientConnected += OnClientConnected;
|
||||||
|
adapter.Start(_options);
|
||||||
|
}
|
||||||
|
|
||||||
MqttTrace.Information(nameof(MqttServer), "Started.");
|
MqttTrace.Information(nameof(MqttServer), "Started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource?.Cancel();
|
_cancellationTokenSource?.Cancel(false);
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
_adapter.ClientConnected -= OnClientConnected;
|
foreach (var adapter in _adapters)
|
||||||
_adapter.Stop();
|
{
|
||||||
|
adapter.ClientConnected -= OnClientConnected;
|
||||||
|
adapter.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
_clientSessionsManager.Clear();
|
_clientSessionsManager.Clear();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public sealed class MqttServerDefaultEndpointOptions
|
||||||
|
{
|
||||||
|
public bool IsEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
public int? Port { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,15 @@ using MQTTnet.Core.Protocol;
|
|||||||
|
|
||||||
namespace MQTTnet.Core.Server
|
namespace MQTTnet.Core.Server
|
||||||
{
|
{
|
||||||
public class MqttServerOptions
|
public sealed class MqttServerOptions
|
||||||
{
|
{
|
||||||
public int Port { get; set; } = 1883;
|
public MqttServerDefaultEndpointOptions DefaultEndpointOptions { get; } = new MqttServerDefaultEndpointOptions();
|
||||||
|
|
||||||
|
public MqttServerTlsEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsEndpointOptions();
|
||||||
|
|
||||||
public int ConnectionBacklog { get; set; } = 10;
|
public int ConnectionBacklog { get; set; } = 10;
|
||||||
|
|
||||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public static class MqttServerOptionsExtensions
|
||||||
|
{
|
||||||
|
public static int GetTlsEndpointPort(this MqttServerOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
if (!options.TlsEndpointOptions.Port.HasValue)
|
||||||
|
{
|
||||||
|
return 8883;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.TlsEndpointOptions.Port.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetDefaultEndpointPort(this MqttServerOptions options)
|
||||||
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
if (!options.DefaultEndpointOptions.Port.HasValue)
|
||||||
|
{
|
||||||
|
return 1883;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.DefaultEndpointOptions.Port.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace MQTTnet.Core.Server
|
||||||
|
{
|
||||||
|
public sealed class MqttServerTlsEndpointOptions
|
||||||
|
{
|
||||||
|
public bool IsEnabled { get; set; }
|
||||||
|
|
||||||
|
public int? Port { get; set; }
|
||||||
|
|
||||||
|
public byte[] Certificate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>MQTTnet</id>
|
|
||||||
<version>2.1.2.0</version>
|
|
||||||
<authors>Christian Kratky</authors>
|
|
||||||
<owners>Christian Kratky</owners>
|
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
|
||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
|
||||||
<releaseNotes>* [Server] Added support for complex client IDs.
|
|
||||||
* [Server] Fixed an issue with not correctly removed old client sessions.
|
|
||||||
* [Server] Several minor performance improvements.
|
|
||||||
* [Server] An existing client session is no longer closed if a new client connection is invalid.
|
|
||||||
* [Client] Added support for sending "CleanSession" flag.</releaseNotes>
|
|
||||||
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
|
||||||
<tags>MQTT MQTTClient MQTTServer MQTTBroker Broker</tags>
|
|
||||||
</metadata>
|
|
||||||
|
|
||||||
<files>
|
|
||||||
<!-- Universal Windows -->
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.Core.dll" target="lib\uap10.0"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.dll" target="lib\uap10.0"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.pri" target="lib\uap10.0"/>
|
|
||||||
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\ARM\Release\MQTTnet.Core.dll" target="lib\uap10.0-arm"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\ARM\Release\MQTTnet.dll" target="lib\uap10.0-arm"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\ARM\Release\MQTTnet.pri" target="lib\uap10.0-arm"/>
|
|
||||||
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x64\Release\MQTTnet.Core.dll" target="lib\uap10.0-x64"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x64\Release\MQTTnet.dll" target="lib\uap10.0-x64"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x64\Release\MQTTnet.pri" target="lib\uap10.0-x64"/>
|
|
||||||
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x86\Release\MQTTnet.Core.dll" target="lib\uap10.0-x86"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x86\Release\MQTTnet.dll" target="lib\uap10.0-x86"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.UniversalWindows\bin\x86\Release\MQTTnet.pri" target="lib\uap10.0-x86"/>
|
|
||||||
|
|
||||||
<!-- .NET Framework -->
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.Core.dll" target="lib\net45"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.dll" target="lib\net45"/>
|
|
||||||
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\x86\Release\MQTTnet.Core.dll" target="lib\net45-x86"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\x86\Release\MQTTnet.dll" target="lib\net45-x86"/>
|
|
||||||
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\x86\Release\MQTTnet.Core.dll" target="lib\net45-x64"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetFramework\bin\x86\Release\MQTTnet.dll" target="lib\net45-x64"/>
|
|
||||||
|
|
||||||
<!-- .NET Core App -->
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetCoreApp\bin\Release\netcoreapp1.1\MQTTnet.Core.dll" target="lib\netcoreapp1.1"/>
|
|
||||||
<file src=".\Frameworks\MQTTnet.NetCoreApp\bin\Release\netcoreapp1.1\MQTTnet.dll" target="lib\netcoreapp1.1"/>
|
|
||||||
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
+71
-119
@@ -1,10 +1,8 @@
|
|||||||
|
|
||||||
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.26228.9
|
VisualStudioVersion = 15.0.26430.16
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestMqttClient", "Tests\MQTTnet.TestMqttClient\MQTTnet.TestMqttClient.csproj", "{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.NetFramework", "Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj", "{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}"
|
||||||
@@ -15,23 +13,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestMqttServer", "Tests\MQTTnet.TestMqttServer\MQTTnet.TestMqttServer.csproj", "{6F8C0C0C-59EC-4921-9267-370AE113C34F}"
|
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.Core", "MQTTnet.Core\MQTTnet.Core.csproj", "{2ECB99E4-72D0-4C23-99BA-93D511D3967D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.NetFramework", "Tests\MQTTnet.TestApp.NetFramework\MQTTnet.TestApp.NetFramework.csproj", "{D9D74F33-6943-49B2-B765-7BD589082098}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{002203AF-2565-4C0D-95ED-027FDEFE0C35}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetStandard", "Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
MQTTnet.nuspec = MQTTnet.nuspec
|
Build\build.ps1 = Build\build.ps1
|
||||||
|
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetCoreApp", "Frameworks\MQTTnet.NetCoreApp\MQTTnet.NetCoreApp.csproj", "{1A1B7F51-5328-4395-9D9C-07D70965825E}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
README.md = README.md
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Any CPU|Any CPU = Any CPU|Any CPU
|
|
||||||
Any CPU|ARM = Any CPU|ARM
|
|
||||||
Any CPU|x64 = Any CPU|x64
|
|
||||||
Any CPU|x86 = Any CPU|x86
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|ARM = Debug|ARM
|
Debug|ARM = Debug|ARM
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
@@ -42,38 +44,6 @@ Global
|
|||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Any CPU|x86.Build.0 = Any CPU|x86
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x64.Build.0 = Release|x64
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x86.Build.0 = Release|x86
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Any CPU|x86.Build.0 = Any CPU|x86
|
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
@@ -90,14 +60,6 @@ Global
|
|||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|x64
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|x64
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|x86
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|x86
|
||||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|x86
|
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|x86
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Any CPU|x86.Build.0 = Any CPU|x86
|
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|Any CPU.ActiveCfg = Debug|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|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.ActiveCfg = Debug|Any CPU
|
||||||
@@ -114,14 +76,6 @@ Global
|
|||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.Build.0 = 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.ActiveCfg = Release|x86
|
||||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.Build.0 = Release|x86
|
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.Build.0 = Release|x86
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|ARM.ActiveCfg = Any CPU|ARM
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|ARM.Build.0 = Any CPU|ARM
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Any CPU|x86.Build.0 = Any CPU|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
|
||||||
@@ -138,38 +92,6 @@ Global
|
|||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x64.Build.0 = Release|x64
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x64.Build.0 = Release|x64
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.ActiveCfg = Release|x86
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.ActiveCfg = Release|x86
|
||||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.Build.0 = Release|x86
|
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.Build.0 = Release|x86
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Any CPU|x86.Build.0 = Any CPU|x86
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|x64.Build.0 = Release|x64
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F}.Release|x86.Build.0 = Release|x86
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|x64.Build.0 = Any CPU|x64
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Any CPU|x86.Build.0 = Any CPU|x86
|
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
@@ -186,40 +108,70 @@ Global
|
|||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|x64
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|x64
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|x86
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|x86
|
||||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|x86
|
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|x86
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|Any CPU.ActiveCfg = Any CPU|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|Any CPU.Build.0 = Any CPU|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|ARM.ActiveCfg = Any CPU|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|ARM.Build.0 = Any CPU|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|x64.ActiveCfg = Any CPU|x64
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|x64.Build.0 = Any CPU|x64
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.Build.0 = Debug|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|x86.ActiveCfg = Any CPU|x86
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Any CPU|x86.Build.0 = Any CPU|x86
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.Build.0 = Debug|x86
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|ARM.Build.0 = Debug|Any CPU
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|x64.ActiveCfg = Debug|x64
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.ActiveCfg = Release|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|x64.Build.0 = Debug|x64
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.Build.0 = Release|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|x86.ActiveCfg = Debug|x86
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.ActiveCfg = Release|x86
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Debug|x86.Build.0 = Debug|x86
|
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.Build.0 = Release|x86
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|ARM.ActiveCfg = Release|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|ARM.Build.0 = Release|Any CPU
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|x64.ActiveCfg = Release|x64
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|x64.Build.0 = Release|x64
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|x86.ActiveCfg = Release|x86
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E}.Release|x86.Build.0 = Release|x86
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Deploy.0 = Debug|x64
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Deploy.0 = Debug|x86
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Deploy.0 = Release|ARM
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Build.0 = Release|x64
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Deploy.0 = Release|x64
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Build.0 = Release|x86
|
||||||
|
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Deploy.0 = Release|x86
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.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
|
||||||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
|
||||||
{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}
|
{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}
|
||||||
{6F8C0C0C-59EC-4921-9267-370AE113C34F} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
{D9D74F33-6943-49B2-B765-7BD589082098} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
{1A1B7F51-5328-4395-9D9C-07D70965825E} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
|
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -2,31 +2,61 @@
|
|||||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
[](https://www.nuget.org/packages/MQTTnet)
|
||||||
|
|
||||||
# MQTTnet
|
# MQTTnet
|
||||||
MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server. The implementation is based on the documentation from http://mqtt.org/.
|
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/.
|
||||||
|
|
||||||
## Supported frameworks
|
# Features
|
||||||
|
|
||||||
* .NET Standard 1.1+
|
## General
|
||||||
|
* Async support
|
||||||
|
* TLS 1.2 support for client and server (but not UWP servers)
|
||||||
|
* Extensible communication channels (i.e. In-Memory, TCP, TCP+SSL, WebSockets (not included in this project))
|
||||||
|
* Interfaces included for mocking and testing
|
||||||
|
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||||
|
* Access to internal trace messages
|
||||||
|
* Unit tested (50+ tests)
|
||||||
|
|
||||||
|
## Client
|
||||||
|
* Rx support (via another project)
|
||||||
|
|
||||||
|
## Server (broker)
|
||||||
|
* List of connected clients available
|
||||||
|
* Supports connected clients with different protocol versions at the same time
|
||||||
|
* Able to publish its own messages (no loopback client required)
|
||||||
|
* Able to receive every messages (no loopback client required)
|
||||||
|
* Extensible client credential validation
|
||||||
|
|
||||||
|
# Supported frameworks
|
||||||
|
* .NET Standard 1.3+
|
||||||
* .NET Core 1.1+
|
* .NET Core 1.1+
|
||||||
* .NET Core App 1.1+
|
* .NET Core App 1.1+
|
||||||
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
|
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
|
||||||
* Universal Windows (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU)
|
* Universal Windows (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU)
|
||||||
|
|
||||||
## Supported MQTT versions
|
# Supported MQTT versions
|
||||||
|
|
||||||
* 3.1.1
|
* 3.1.1
|
||||||
|
* 3.1.0
|
||||||
|
|
||||||
## Nuget
|
# Nuget
|
||||||
This library is available as a nuget package: https://www.nuget.org/packages/MQTTnet/
|
This library is available as a nuget package: https://www.nuget.org/packages/MQTTnet/
|
||||||
|
|
||||||
## 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.
|
||||||
|
|
||||||
# MqttClient
|
# References
|
||||||
## Example
|
This library is used in the following projects:
|
||||||
|
|
||||||
```c#
|
* 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)
|
||||||
|
|
||||||
|
If you use this library and want to see your project here please let me know.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
## MqttClient
|
||||||
|
|
||||||
|
```csharp
|
||||||
var options = new MqttClientOptions
|
var options = new MqttClientOptions
|
||||||
{
|
{
|
||||||
Server = "localhost"
|
Server = "localhost"
|
||||||
@@ -96,11 +126,9 @@ while (true)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# MqttServer
|
## MqttServer
|
||||||
|
|
||||||
## Example
|
```csharp
|
||||||
|
|
||||||
```c#
|
|
||||||
var options = new MqttServerOptions
|
var options = new MqttServerOptions
|
||||||
{
|
{
|
||||||
ConnectionValidator = p =>
|
ConnectionValidator = p =>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ByteReaderTests.cs" />
|
<Compile Include="ByteReaderTests.cs" />
|
||||||
<Compile Include="ByteWriterTests.cs" />
|
<Compile Include="ByteWriterTests.cs" />
|
||||||
<Compile Include="DefaultMqttV311PacketSerializerTests.cs" />
|
<Compile Include="MqttPacketSerializerTests.cs" />
|
||||||
<Compile Include="MqttServerTests.cs" />
|
<Compile Include="MqttServerTests.cs" />
|
||||||
<Compile Include="MqttSubscriptionsManagerTests.cs" />
|
<Compile Include="MqttSubscriptionsManagerTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
|||||||
+30
-4
@@ -12,8 +12,23 @@ using MQTTnet.Core.Serializer;
|
|||||||
namespace MQTTnet.Core.Tests
|
namespace MQTTnet.Core.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class DefaultMqttV311PacketSerializerTests
|
public class MqttPacketSerializerTests
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void SerializeV310_MqttConnectPacket()
|
||||||
|
{
|
||||||
|
var p = new MqttConnectPacket
|
||||||
|
{
|
||||||
|
ClientId = "XYZ",
|
||||||
|
Password = "PASS",
|
||||||
|
Username = "USER",
|
||||||
|
KeepAlivePeriod = 123,
|
||||||
|
CleanSession = true
|
||||||
|
};
|
||||||
|
|
||||||
|
SerializeAndCompare(p, "EB0ABE1RSXNkcAPCAHsAA1hZWgAEVVNFUgAEUEFTUw==", MqttProtocolVersion.V310);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void SerializeV311_MqttConnectPacket()
|
public void SerializeV311_MqttConnectPacket()
|
||||||
{
|
{
|
||||||
@@ -96,6 +111,17 @@ namespace MQTTnet.Core.Tests
|
|||||||
SerializeAndCompare(p, "IAIBBQ==");
|
SerializeAndCompare(p, "IAIBBQ==");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void SerializeV310_MqttConnAckPacket()
|
||||||
|
{
|
||||||
|
var p = new MqttConnAckPacket
|
||||||
|
{
|
||||||
|
ConnectReturnCode = MqttConnectReturnCode.ConnectionAccepted
|
||||||
|
};
|
||||||
|
|
||||||
|
SerializeAndCompare(p, "IAIAAA==", MqttProtocolVersion.V310);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void DeserializeV311_MqttConnAckPacket()
|
public void DeserializeV311_MqttConnAckPacket()
|
||||||
{
|
{
|
||||||
@@ -403,9 +429,9 @@ namespace MQTTnet.Core.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value)
|
private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
||||||
{
|
{
|
||||||
var serializer = new DefaultMqttV311PacketSerializer();
|
var serializer = new MqttPacketSerializer { ProtocolVersion = protocolVersion };
|
||||||
var channel = new TestChannel();
|
var channel = new TestChannel();
|
||||||
serializer.SerializeAsync(packet, channel).Wait();
|
serializer.SerializeAsync(packet, channel).Wait();
|
||||||
var buffer = channel.ToArray();
|
var buffer = channel.ToArray();
|
||||||
@@ -415,7 +441,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
|
|
||||||
private void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value)
|
private void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value)
|
||||||
{
|
{
|
||||||
var serializer = new DefaultMqttV311PacketSerializer();
|
var serializer = new MqttPacketSerializer();
|
||||||
|
|
||||||
var channel1 = new TestChannel();
|
var channel1 = new TestChannel();
|
||||||
serializer.SerializeAsync(packet, channel1).Wait();
|
serializer.SerializeAsync(packet, channel1).Wait();
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
@@ -46,11 +48,11 @@ namespace MQTTnet.Core.Tests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task MqttServer_WillMessage()
|
public async Task MqttServer_WillMessage()
|
||||||
{
|
{
|
||||||
var s = new MqttServer(new MqttServerOptions(), new TestMqttServerAdapter());
|
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { new TestMqttServerAdapter() });
|
||||||
s.Start();
|
s.Start();
|
||||||
|
|
||||||
var willMessage = new MqttApplicationMessage("My/last/will", new byte[0], MqttQualityOfServiceLevel.AtMostOnce, false);
|
var willMessage = new MqttApplicationMessage("My/last/will", new byte[0], MqttQualityOfServiceLevel.AtMostOnce, false);
|
||||||
var c1 = ConnectTestClient("c1", null, s);
|
var c1 = ConnectTestClient("c1", null, s);
|
||||||
var c2 = ConnectTestClient("c2", willMessage, s);
|
var c2 = ConnectTestClient("c2", willMessage, s);
|
||||||
|
|
||||||
var receivedMessagesCount = 0;
|
var receivedMessagesCount = 0;
|
||||||
@@ -66,7 +68,64 @@ namespace MQTTnet.Core.Tests
|
|||||||
Assert.AreEqual(1, receivedMessagesCount);
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MqttClient ConnectTestClient(string clientId, MqttApplicationMessage willMessage, MqttServer server)
|
[TestMethod]
|
||||||
|
public async Task MqttServer_Unsubscribe()
|
||||||
|
{
|
||||||
|
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { new TestMqttServerAdapter() });
|
||||||
|
s.Start();
|
||||||
|
|
||||||
|
var c1 = ConnectTestClient("c1", null, s);
|
||||||
|
var c2 = ConnectTestClient("c2", null, s);
|
||||||
|
|
||||||
|
var receivedMessagesCount = 0;
|
||||||
|
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||||
|
|
||||||
|
var message = new MqttApplicationMessage("a", new byte[0], MqttQualityOfServiceLevel.AtLeastOnce, false);
|
||||||
|
|
||||||
|
await c2.PublishAsync(message);
|
||||||
|
Assert.AreEqual(0, receivedMessagesCount);
|
||||||
|
|
||||||
|
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
||||||
|
await c2.PublishAsync(message);
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
|
|
||||||
|
await c1.Unsubscribe("a");
|
||||||
|
await c2.PublishAsync(message);
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
|
|
||||||
|
s.Stop();
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MqttServer_Publish()
|
||||||
|
{
|
||||||
|
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { new TestMqttServerAdapter() });
|
||||||
|
s.Start();
|
||||||
|
|
||||||
|
var c1 = ConnectTestClient("c1", null, s);
|
||||||
|
|
||||||
|
var receivedMessagesCount = 0;
|
||||||
|
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||||
|
|
||||||
|
var message = new MqttApplicationMessage("a", new byte[0], MqttQualityOfServiceLevel.AtLeastOnce, false);
|
||||||
|
await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce));
|
||||||
|
|
||||||
|
s.Publish(message);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
s.Stop();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, receivedMessagesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttClient ConnectTestClient(string clientId, MqttApplicationMessage willMessage, MqttServer server)
|
||||||
{
|
{
|
||||||
var adapterA = new TestMqttCommunicationAdapter();
|
var adapterA = new TestMqttCommunicationAdapter();
|
||||||
var adapterB = new TestMqttCommunicationAdapter();
|
var adapterB = new TestMqttCommunicationAdapter();
|
||||||
@@ -86,7 +145,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
MqttQualityOfServiceLevel filterQualityOfServiceLevel,
|
MqttQualityOfServiceLevel filterQualityOfServiceLevel,
|
||||||
int expectedReceivedMessagesCount)
|
int expectedReceivedMessagesCount)
|
||||||
{
|
{
|
||||||
var s = new MqttServer(new MqttServerOptions(), new TestMqttServerAdapter());
|
var s = new MqttServer(new MqttServerOptions(), new List<IMqttServerAdapter> { new TestMqttServerAdapter() });
|
||||||
s.Start();
|
s.Start();
|
||||||
|
|
||||||
var c1 = ConnectTestClient("c1", null, s);
|
var c1 = ConnectTestClient("c1", null, s);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.IsTrue(sm.IsTopicSubscribed(pp));
|
Assert.IsTrue(sm.IsSubscribed(pp));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -43,7 +43,7 @@ namespace MQTTnet.Core.Tests
|
|||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.IsFalse(sm.IsTopicSubscribed(pp));
|
Assert.IsFalse(sm.IsSubscribed(pp));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -62,13 +62,13 @@ namespace MQTTnet.Core.Tests
|
|||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.IsTrue(sm.IsTopicSubscribed(pp));
|
Assert.IsTrue(sm.IsSubscribed(pp));
|
||||||
|
|
||||||
var up = new MqttUnsubscribePacket();
|
var up = new MqttUnsubscribePacket();
|
||||||
up.TopicFilters.Add("A/B/C");
|
up.TopicFilters.Add("A/B/C");
|
||||||
sm.Unsubscribe(up);
|
sm.Unsubscribe(up);
|
||||||
|
|
||||||
Assert.IsFalse(sm.IsTopicSubscribed(pp));
|
Assert.IsFalse(sm.IsSubscribed(pp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||||||
using MQTTnet.Core.Adapter;
|
using MQTTnet.Core.Adapter;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
using MQTTnet.Core.Packets;
|
using MQTTnet.Core.Packets;
|
||||||
|
using MQTTnet.Core.Serializer;
|
||||||
|
|
||||||
namespace MQTTnet.Core.Tests
|
namespace MQTTnet.Core.Tests
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,8 @@ namespace MQTTnet.Core.Tests
|
|||||||
|
|
||||||
public TestMqttCommunicationAdapter Partner { get; set; }
|
public TestMqttCommunicationAdapter Partner { get; set; }
|
||||||
|
|
||||||
|
public IMqttPacketSerializer PacketSerializer { get; } = new MqttPacketSerializer();
|
||||||
|
|
||||||
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout)
|
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
await Task.FromResult(0);
|
await Task.FromResult(0);
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ namespace MQTTnet.Core.Tests
|
|||||||
{
|
{
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
|
|
||||||
|
public void FireClientConnectedEvent(MqttClientConnectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
ClientConnected?.Invoke(this, eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
public void Start(MqttServerOptions options)
|
public void Start(MqttServerOptions options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-22
@@ -4,10 +4,10 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProjectGuid>{6F8C0C0C-59EC-4921-9267-370AE113C34F}</ProjectGuid>
|
<ProjectGuid>{D9D74F33-6943-49B2-B765-7BD589082098}</ProjectGuid>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>MQTTnet.TestMqttServer</RootNamespace>
|
<RootNamespace>MQTTnet.TestApp.NetFramework</RootNamespace>
|
||||||
<AssemblyName>MQTTnet.TestMqttServer</AssemblyName>
|
<AssemblyName>MQTTnet.TestApp.NetFramework</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
@@ -31,43 +31,55 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<OutputPath>bin\Any CPU\</OutputPath>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
<Optimize>true</Optimize>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
</PropertyGroup>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x86'">
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<OutputPath>bin\x86\Any CPU\</OutputPath>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
</PropertyGroup>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x64'">
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<OutputPath>bin\x64\Any CPU\</OutputPath>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
+67
-8
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core;
|
using MQTTnet.Core;
|
||||||
using MQTTnet.Core.Client;
|
using MQTTnet.Core.Client;
|
||||||
@@ -9,16 +10,29 @@ using MQTTnet.Core.Packets;
|
|||||||
using MQTTnet.Core.Protocol;
|
using MQTTnet.Core.Protocol;
|
||||||
using MQTTnet.Core.Server;
|
using MQTTnet.Core.Server;
|
||||||
|
|
||||||
namespace MQTTnet.TestMqttClient
|
namespace MQTTnet.TestApp.NetFramework
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] arguments)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Task.Run(() => Run(arguments)).Wait();
|
Console.WriteLine("MQTTnet - TestApp.NetFramework");
|
||||||
|
Console.WriteLine("1 = Start client");
|
||||||
|
Console.WriteLine("2 = Start server");
|
||||||
|
var pressedKey = Console.ReadKey(true);
|
||||||
|
if (pressedKey.Key == ConsoleKey.D1)
|
||||||
|
{
|
||||||
|
Task.Run(() => RunClientAsync(args));
|
||||||
|
Thread.Sleep(Timeout.Infinite);
|
||||||
|
}
|
||||||
|
else if (pressedKey.Key == ConsoleKey.D2)
|
||||||
|
{
|
||||||
|
Task.Run(() => RunServerAsync(args));
|
||||||
|
Thread.Sleep(Timeout.Infinite);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task Run(string[] arguments)
|
private static async Task RunClientAsync(string[] arguments)
|
||||||
{
|
{
|
||||||
MqttTrace.TraceMessagePublished += (s, e) =>
|
MqttTrace.TraceMessagePublished += (s, e) =>
|
||||||
{
|
{
|
||||||
@@ -28,7 +42,7 @@ namespace MQTTnet.TestMqttClient
|
|||||||
Console.WriteLine(e.Exception);
|
Console.WriteLine(e.Exception);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var options = new MqttClientOptions
|
var options = new MqttClientOptions
|
||||||
@@ -61,7 +75,7 @@ namespace MQTTnet.TestMqttClient
|
|||||||
Console.WriteLine("### SUBSCRIBED ###");
|
Console.WriteLine("### SUBSCRIBED ###");
|
||||||
};
|
};
|
||||||
|
|
||||||
client.Disconnected += async (s, e) =>
|
client.Disconnected += async (s, e) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine("### DISCONNECTED FROM SERVER ###");
|
Console.WriteLine("### DISCONNECTED FROM SERVER ###");
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
@@ -80,9 +94,9 @@ namespace MQTTnet.TestMqttClient
|
|||||||
{
|
{
|
||||||
await client.ConnectAsync();
|
await client.ConnectAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine("### CONNECTING FAILED ###");
|
Console.WriteLine("### CONNECTING FAILED ###" + Environment.NewLine + exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("### WAITING FOR APPLICATION MESSAGES ###");
|
Console.WriteLine("### WAITING FOR APPLICATION MESSAGES ###");
|
||||||
@@ -106,5 +120,50 @@ namespace MQTTnet.TestMqttClient
|
|||||||
Console.WriteLine(exception);
|
Console.WriteLine(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunServerAsync(string[] arguments)
|
||||||
|
{
|
||||||
|
MqttTrace.TraceMessagePublished += (s, e) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($">> [{e.ThreadId}] [{e.Source}] [{e.Level}]: {e.Message}");
|
||||||
|
if (e.Exception != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Exception);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new MqttServerOptions
|
||||||
|
{
|
||||||
|
ConnectionValidator = p =>
|
||||||
|
{
|
||||||
|
if (p.ClientId == "SpecialClient")
|
||||||
|
{
|
||||||
|
if (p.Username != "USER" || p.Password != "PASS")
|
||||||
|
{
|
||||||
|
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MqttConnectReturnCode.ConnectionAccepted;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mqttServer = new MqttServerFactory().CreateMqttServer(options);
|
||||||
|
mqttServer.Start();
|
||||||
|
|
||||||
|
Console.WriteLine("Press any key to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
mqttServer.Stop();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyTitle("MQTTnet.TestMqttClient")]
|
[assembly: AssemblyTitle("MQTTnet.TestApp.NetFramework")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Christian Kratky")]
|
[assembly: AssemblyCompany("Christian Kratky")]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<Application
|
||||||
|
x:Class="MQTTnet.TestApp.UniversalWindows.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:MQTTnet.TestApp.UniversalWindows"
|
||||||
|
RequestedTheme="Light">
|
||||||
|
|
||||||
|
</Application>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user