Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/pending-appmsg-acknowledge

# Conflicts:
#	Source/MQTTnet/Client/MqttClient.cs
#	Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs
This commit is contained in:
tamas.kurucsai
2021-02-20 12:55:28 +01:00
114 changed files with 1823 additions and 2110 deletions
+81 -2
View File
@@ -1,5 +1,84 @@
[*.cs] [*.cs]
# CA1031: Do not catch general exception types # CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = none
# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.constants_rule.severity = warning
dotnet_naming_rule.constants_rule.style = upper_camel_case_style
dotnet_naming_rule.constants_rule.symbols = constants_symbols
dotnet_naming_rule.public_fields_rule.severity = warning
dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style
dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols
dotnet_naming_rule.static_readonly_rule.severity = warning
dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style
dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_style.lower_camel_case_style.required_prefix = _
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
dotnet_naming_symbols.constants_symbols.required_modifiers = const
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_default_private_modifier = implicit
resharper_place_accessorholder_attribute_on_same_line = false
resharper_use_indent_from_vs = false
# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning
[*]
charset = utf-8
end_of_line = crlf
trim_trailing_whitespace = false
insert_final_newline = false
indent_style = space
indent_size = 4
[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
indent_style = space
indent_size = 2
[{*.yaml,*.yml}]
indent_style = space
indent_size = 2
[{tsconfig.app.json,tsconfig.e2e.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json}]
indent_style = space
indent_size = 2
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 4
tab_width = 4
+11 -4
View File
@@ -19,10 +19,14 @@
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" /> <dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" />
</group> </group>
<group targetFramework="netcoreapp3.1"> <group targetFramework="netcoreapp3.1">
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</group> </group>
<group targetFramework="net5.0">
<dependency id="MQTTnet" version="$nugetVersion" />
</group>
</dependencies> </dependencies>
</metadata> </metadata>
@@ -33,10 +37,13 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" /> <file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\net5.0\MQTTnet.AspNetCore.*" target="lib\net5.0\" />
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/> <file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\" />
<!-- .NET Core App 3.1 --> <!-- .NET Core App 3.1 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\"/> <file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\" />
</files> </files>
</package> </package>
+12 -6
View File
@@ -13,7 +13,7 @@
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description> <description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies> <dependencies>
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</dependencies> </dependencies>
@@ -26,20 +26,26 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" /> <file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 --> <!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.1\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.1\" />
<!-- Universal Windows --> <!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\" />
<!-- .NET Framework --> <!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. --> <!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
+12 -6
View File
@@ -13,7 +13,7 @@
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description> <description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies> <dependencies>
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</dependencies> </dependencies>
@@ -26,20 +26,26 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" /> <file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net5.0\MQTTnet.Extensions.Rpc.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 --> <!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/> <file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/> <file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.1\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.1\" />
<!-- Universal Windows --> <!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/> <file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\" />
<!-- .NET Framework --> <!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/> <file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. --> <!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/> <file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
+14 -22
View File
@@ -13,24 +13,10 @@
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description> <description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies> <dependencies>
<group targetFramework="netstandard1.3"> <dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="WebSocket4Net" version="0.15.2" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
</dependencies> </dependencies>
</metadata> </metadata>
@@ -41,20 +27,26 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" /> <file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 --> <!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\"/> <file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\"/> <file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.1\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.1\" />
<!-- Universal Windows --> <!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\"/> <file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\" />
<!-- .NET Framework --> <!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\"/> <file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. --> <!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\"/> <file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
+21 -43
View File
@@ -12,50 +12,28 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description> <description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
<releaseNotes> <releaseNotes>
* [Core] Added .NET 5.0 TFMs (thanks to @StefanOssendorf, @JanEggers). * [Core] Fixed some issues in nuget packages.
* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal). * [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082).
* [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato). * [Client] Exposed Dup flag in application messages.
* [Core] Aligned the format of some log messages. * [RpcClient] Fixed an issue when using a custom application message reveived handler (#1093).
* [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick).
* [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect.
* [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!).
* [RpcClient] Moved the RPC topic validation to the topic generation strategy.
* [RcpClient] Added interface for _MqttRpcClient_.
* [Server] Reduced async tasks count by moving dedicated keep alive tasks per connection to shared one.
* [Server] Session takeover and keep alive timeout are now properly set in DISCONNECT packet.
* [Server] Fixed bug in PubRel packet generation (MQTTv5 only).
* [Server] Improved message processing performance (+ ~5%).
* [Server] Fix wrong usage of client session items for undelivered messages.
Git commit: $gitCommit Git commit: $gitCommit
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies> <dependencies>
<group targetFramework="net452" />
<group targetFramework="net461" />
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.3.0" /> <dependency id="NETStandard.Library" version="1.6.1" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netcoreapp3.1">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" /> <dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" /> <dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" /> <dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group> </group>
<group targetFramework="netstandard2.0" />
<group targetFramework="netstandard2.1" />
<group targetFramework="netcoreapp3.1" />
<group targetFramework="net5.0" />
<group targetFramework="uap10.0"> <group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" /> <dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
</group> </group>
@@ -70,28 +48,28 @@ Git commit: $gitCommit
<file src="..\Images\Logo_128x128.png" target="images\" /> <file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 --> <!-- .NET 5.0 -->
<file src="..\Source\MQTTnet\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0"/> <file src="..\Source\MQTTnet\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 --> <!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/> <file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/> <file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 --> <!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/> <file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\" />
<!-- .NET Standard 3.1 --> <!-- .NET Standard 3.1 -->
<file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\"/> <file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\" />
<!-- Universal Windows --> <!-- Universal Windows -->
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/> <file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\" />
<!-- .NET Framework --> <!-- .NET Framework -->
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/> <file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. --> <!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\"/> <file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
+9 -4
View File
@@ -31,10 +31,12 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the ASP.NET Core 2.0 extension # Build the ASP.NET Core 2.0 extension
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the RPC extension # Build the RPC extension
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
@@ -43,6 +45,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the Managed Client extension # Build the Managed Client extension
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
@@ -51,6 +54,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the WebSocket4Net extension # Build the WebSocket4Net extension
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
@@ -59,6 +63,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Create NuGet packages. # Create NuGet packages.
@@ -97,7 +102,7 @@ Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
# Build MQTTnet.Server Portable # Build MQTTnet.Server Portable
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --framework net5.0 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish" $source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination} If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem" Add-Type -assembly "system.io.compression.filesystem"
@@ -108,7 +113,7 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Linux-x64 # Build MQTTnet.Server Linux-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64 --framework net5.0 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish" $source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\linux-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination} If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem" Add-Type -assembly "system.io.compression.filesystem"
@@ -119,7 +124,7 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Linux-ARM # Build MQTTnet.Server Linux-ARM
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm --framework net5.0 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish" $source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\linux-ARM\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination} If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem" Add-Type -assembly "system.io.compression.filesystem"
@@ -130,7 +135,7 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Windows-x64 # Build MQTTnet.Server Windows-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64 --framework net5.0 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish" $source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\win-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination} If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem" Add-Type -assembly "system.io.compression.filesystem"
-25
View File
@@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
EndProject EndProject
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", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
@@ -91,28 +89,6 @@ Global
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64
{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.ActiveCfg = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
@@ -327,7 +303,6 @@ Global
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {A56E3128-1639-4F31-873A-325E14BB6295}
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB} {3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
+24 -5
View File
@@ -64,15 +64,32 @@ _MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) bas
## Supported frameworks ## Supported frameworks
* .NET 5.0+
* .NET Standard 1.3+ * .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+
* Mono 5.2+ * Mono 5.2+
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core) * Universal Windows Platform (UWP) 10.0.10240+ (Windows 10 IoT Core)
* Xamarin.Android 7.5+ * Xamarin.Android 7.5+
* Xamarin.iOS 10.14+ * Xamarin.iOS 10.14+
* Blazor WebAssembly 3.2.0 * Blazor WebAssembly 3.2.0+
## Supported platforms
* x86
* x64
* AnyCPU
* ARM
## Supported OS
* Windows
* Windows 10 IoT Core
* Linux (Ubuntu, Raspbian etc.)
* macOS
* Android
* iOS
## Supported MQTT versions ## Supported MQTT versions
@@ -97,12 +114,14 @@ Also additions and updates in the Wiki are welcome.
This library is used in the following projects: This library is used in the following projects:
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/) * Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>) * HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>) * MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>) * MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8)) * MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnet.App>)
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>) * Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents. Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
@@ -112,7 +131,7 @@ If you use this library and want to see your project here please create a pull r
MIT License MIT License
MQTTnet Copyright (c) 2016-2020 Christian Kratky MQTTnet Copyright (c) 2016-2021 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -49,7 +49,7 @@ namespace MQTTnet.AspNetCore
var formatter = new MqttPacketFormatterAdapter(writer); var formatter = new MqttPacketFormatterAdapter(writer);
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate); var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _rootLogger)) using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _rootLogger))
{ {
await clientHandler(channelAdapter).ConfigureAwait(false); await clientHandler(channelAdapter).ConfigureAwait(false);
} }
@@ -53,11 +53,11 @@ namespace MQTTnet.Extensions.ManagedClient
public bool IsConnected => InternalClient.IsConnected; public bool IsConnected => InternalClient.IsConnected;
public bool IsStarted => _connectionCancellationToken != null; public bool IsStarted => _connectionCancellationToken != null;
public IMqttClient InternalClient { get; } public IMqttClient InternalClient { get; }
public int PendingApplicationMessagesCount => _messageQueue.Count; public int PendingApplicationMessagesCount => _messageQueue.Count;
public IManagedMqttClientOptions Options { get; private set; } public IManagedMqttClientOptions Options { get; private set; }
public IMqttClientConnectedHandler ConnectedHandler public IMqttClientConnectedHandler ConnectedHandler
@@ -92,7 +92,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options)); if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started."); if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
Options = options; Options = options;
@@ -292,7 +292,14 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
try try
{ {
await InternalClient.DisconnectAsync().ConfigureAwait(false); using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.CommunicationTimeout))
{
await InternalClient.DisconnectAsync(disconnectTimeout.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
_logger.Warning("Timeout while sending DISCONNECT packet.");
} }
catch (Exception exception) catch (Exception exception)
{ {
+13 -9
View File
@@ -8,6 +8,7 @@ using System.Collections.Concurrent;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Implementations;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc
{ {
@@ -88,8 +89,8 @@ namespace MQTTnet.Extensions.Rpc
try try
{ {
var tcs = new TaskCompletionSource<byte[]>(); var promise = new TaskCompletionSource<byte[]>();
if (!_waitingCalls.TryAdd(responseTopic, tcs)) if (!_waitingCalls.TryAdd(responseTopic, promise))
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@@ -102,15 +103,15 @@ namespace MQTTnet.Extensions.Rpc
{ {
linkedCts.Token.Register(() => linkedCts.Token.Register(() =>
{ {
if (!tcs.Task.IsCompleted && !tcs.Task.IsFaulted && !tcs.Task.IsCanceled) if (!promise.Task.IsCompleted && !promise.Task.IsFaulted && !promise.Task.IsCanceled)
{ {
tcs.TrySetCanceled(); promise.TrySetCanceled();
} }
}); });
try try
{ {
var result = await tcs.Task.ConfigureAwait(false); var result = await promise.Task.ConfigureAwait(false);
timeoutCts.Cancel(false); timeoutCts.Cancel(false);
return result; return result;
} }
@@ -136,14 +137,17 @@ namespace MQTTnet.Extensions.Rpc
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{ {
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs)) if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var promise))
{ {
return Task.FromResult(0); return PlatformAbstractionLayer.CompletedTask;
} }
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload); promise.TrySetResult(eventArgs.ApplicationMessage.Payload);
return Task.FromResult(0); // Set this message to handled to that other code can avoid execution etc.
eventArgs.IsHandled = true;
return PlatformAbstractionLayer.CompletedTask;
} }
public void Dispose() public void Dispose()
@@ -7,6 +7,14 @@ namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
{ {
public string MethodName { get; set; } public string MethodName { get; set; }
/// <summary>
/// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public IMqttClient MqttClient { get; set; } public IMqttClient MqttClient { get; set; }
@@ -18,14 +18,15 @@ namespace MQTTnet.Extensions.Rpc
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; } public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{ {
// First try to check if there is a pending RPC call!
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
if (OriginalHandler != null) if (OriginalHandler != null)
{ {
return OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs); await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
} }
return _handleReceivedApplicationMessageAsync(eventArgs);
} }
} }
} }
@@ -7,7 +7,7 @@ using System;
namespace MQTTnet.Extensions.WebSocket4Net namespace MQTTnet.Extensions.WebSocket4Net
{ {
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{ {
readonly IMqttNetLogger _logger; readonly IMqttNetLogger _logger;
@@ -23,14 +23,22 @@ namespace MQTTnet.Extensions.WebSocket4Net
switch (options.ChannelOptions) switch (options.ChannelOptions)
{ {
case MqttClientTcpOptions _: case MqttClientTcpOptions _:
{ {
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger); return new MqttChannelAdapter(
} new MqttTcpChannel(options),
new MqttPacketFormatterAdapter(options.ProtocolVersion),
options.PacketInspector,
_logger);
}
case MqttClientWebSocketOptions webSocketOptions: case MqttClientWebSocketOptions webSocketOptions:
{ {
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger); return new MqttChannelAdapter(
} new WebSocket4NetMqttChannel(options, webSocketOptions),
new MqttPacketFormatterAdapter(options.ProtocolVersion),
options.PacketInspector,
_logger);
}
default: default:
{ {
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks> <TargetFrameworks>net5.0</TargetFrameworks>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName> <AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace> <RootNamespace>MQTTnet.Server</RootNamespace>
+123 -11
View File
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter namespace MQTTnet.Adapter
{ {
@@ -19,26 +20,26 @@ namespace MQTTnet.Adapter
const uint ErrorOperationAborted = 0x800703E3; const uint ErrorOperationAborted = 0x800703E3;
const int ReadBufferSize = 4096; const int ReadBufferSize = 4096;
readonly byte[] _singleByteBuffer = new byte[1];
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly MqttPacketInspectorHandler _packetInspectorHandler;
readonly IMqttNetScopedLogger _logger; readonly IMqttNetScopedLogger _logger;
readonly IMqttChannel _channel; readonly IMqttChannel _channel;
readonly MqttPacketReader _packetReader;
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly AsyncLock _syncRoot = new AsyncLock(); readonly AsyncLock _syncRoot = new AsyncLock();
long _bytesReceived; long _bytesReceived;
long _bytesSent; long _bytesSent;
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger) public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttPacketInspector packetInspector, IMqttNetLogger logger)
{ {
if (logger == null) throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel)); _channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_packetReader = new MqttPacketReader(_channel); _packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger);
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter)); _logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter));
} }
@@ -124,6 +125,7 @@ namespace MQTTnet.Adapter
try try
{ {
var packetData = PacketFormatterAdapter.Encode(packet); var packetData = PacketFormatterAdapter.Encode(packet);
_packetInspectorHandler.BeginSendPacket(packetData);
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false); await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
@@ -154,12 +156,16 @@ namespace MQTTnet.Adapter
try try
{ {
_packetInspectorHandler.BeginReceivePacket();
var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false); var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
if (receivedPacket == null || cancellationToken.IsCancellationRequested) if (receivedPacket == null || cancellationToken.IsCancellationRequested)
{ {
return null; return null;
} }
_packetInspectorHandler.EndReceivePacket();
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength); Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown) if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
@@ -215,7 +221,12 @@ namespace MQTTnet.Adapter
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken) async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{ {
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -234,7 +245,7 @@ namespace MQTTnet.Adapter
var fixedHeader = readFixedHeaderResult.FixedHeader; var fixedHeader = readFixedHeaderResult.FixedHeader;
if (fixedHeader.RemainingLength == 0) if (fixedHeader.RemainingLength == 0)
{ {
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2); return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(new byte[0], 0, 0), 2);
} }
var bodyLength = fixedHeader.RemainingLength; var bodyLength = fixedHeader.RemainingLength;
@@ -266,6 +277,8 @@ namespace MQTTnet.Adapter
bodyOffset += readBytes; bodyOffset += readBytes;
} while (bodyOffset < bodyLength); } while (bodyOffset < bodyLength);
_packetInspectorHandler.FillReceiveBuffer(body);
var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength); var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength);
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength); return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
} }
@@ -275,11 +288,110 @@ namespace MQTTnet.Adapter
} }
} }
async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
var buffer = _fixedHeaderBuffer;
var totalBytesRead = 0;
while (totalBytesRead < buffer.Length)
{
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (bytesRead == 0)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bytesRead;
}
_packetInspectorHandler.FillReceiveBuffer(buffer);
var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
};
}
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
if (!bodyLength.HasValue)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bodyLength.Value;
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
};
}
async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
{
offset++;
if (offset > 3)
{
throw new MqttProtocolViolationException("Remaining length is invalid.");
}
if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readCount == 0)
{
return null;
}
_packetInspectorHandler.FillReceiveBuffer(_singleByteBuffer);
encodedByte = _singleByteBuffer[0];
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
static bool IsWrappedException(Exception exception) static bool IsWrappedException(Exception exception)
{ {
return exception is OperationCanceledException || return exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException || exception is MqttCommunicationTimedOutException ||
exception is MqttCommunicationException; exception is MqttCommunicationException ||
exception is MqttProtocolViolationException;
} }
static void WrapAndThrowException(Exception exception) static void WrapAndThrowException(Exception exception)
@@ -295,7 +407,7 @@ namespace MQTTnet.Adapter
{ {
throw new OperationCanceledException(); throw new OperationCanceledException();
} }
if (socketException.SocketErrorCode == SocketError.ConnectionAborted) if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{ {
throw new MqttCommunicationException(socketException); throw new MqttCommunicationException(socketException);
@@ -1,17 +1,19 @@
using MQTTnet.Client.Connecting; using System;
using MQTTnet.Client.Connecting;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
namespace MQTTnet.Adapter namespace MQTTnet.Adapter
{ {
public class MqttConnectingFailedException : MqttCommunicationException public sealed class MqttConnectingFailedException : MqttCommunicationException
{ {
public MqttConnectingFailedException(MqttClientAuthenticateResult result) public MqttConnectingFailedException(string message, Exception innerException, MqttClientAuthenticateResult authenticateResult)
: base($"Connecting with MQTT server failed ({result.ResultCode.ToString()}).") : base(message, innerException)
{ {
Result = result; Result = authenticateResult;
} }
public MqttClientAuthenticateResult Result { get; } public MqttClientAuthenticateResult Result { get; }
public MqttClientConnectResultCode ResultCode => Result.ResultCode;
public MqttClientConnectResultCode ResultCode => Result?.ResultCode ?? MqttClientConnectResultCode.UnspecifiedError;
} }
} }
@@ -0,0 +1,84 @@
using System;
using System.IO;
using System.Linq;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter
{
public sealed class MqttPacketInspectorHandler
{
readonly MemoryStream _receivedPacketBuffer;
readonly IMqttPacketInspector _packetInspector;
readonly IMqttNetScopedLogger _logger;
public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger)
{
_packetInspector = packetInspector;
if (packetInspector != null)
{
_receivedPacketBuffer = new MemoryStream();
}
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttPacketInspectorHandler));
}
public void BeginReceivePacket()
{
_receivedPacketBuffer?.SetLength(0);
}
public void EndReceivePacket()
{
if (_packetInspector == null)
{
return;
}
var buffer = _receivedPacketBuffer.ToArray();
_receivedPacketBuffer.SetLength(0);
InspectPacket(buffer, MqttPacketFlowDirection.Inbound);
}
public void BeginSendPacket(ArraySegment<byte> buffer)
{
if (_packetInspector == null)
{
return;
}
// Create a copy of the actual packet so that the inspector gets no access
// to the internal buffers. This is waste of memory but this feature is only
// intended for debugging etc. so that this is OK.
var bufferCopy = buffer.ToArray();
InspectPacket(bufferCopy, MqttPacketFlowDirection.Outbound);
}
public void FillReceiveBuffer(byte[] buffer)
{
_receivedPacketBuffer?.Write(buffer, 0, buffer.Length);
}
void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction)
{
try
{
var context = new ProcessMqttPacketContext
{
Buffer = buffer,
Direction = direction
};
_packetInspector.ProcessMqttPacket(context);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while inspecting packet.");
}
}
}
}
+5 -4
View File
@@ -1,19 +1,20 @@
using MQTTnet.Formatter; using System;
using MQTTnet.Formatter;
namespace MQTTnet.Adapter namespace MQTTnet.Adapter
{ {
public sealed class ReceivedMqttPacket public sealed class ReceivedMqttPacket
{ {
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader body, int totalLength) public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader bodyReader, int totalLength)
{ {
FixedHeader = fixedHeader; FixedHeader = fixedHeader;
Body = body; BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader));
TotalLength = totalLength; TotalLength = totalLength;
} }
public byte FixedHeader { get; } public byte FixedHeader { get; }
public IMqttPacketBodyReader Body { get; } public IMqttPacketBodyReader BodyReader { get; }
public int TotalLength { get; } public int TotalLength { get; }
} }
@@ -7,6 +7,10 @@ namespace MQTTnet.Client.Connecting
// TODO: Consider renaming this to _MqttClientConnectResult_ // TODO: Consider renaming this to _MqttClientConnectResult_
public class MqttClientAuthenticateResult public class MqttClientAuthenticateResult
{ {
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; set; } public MqttClientConnectResultCode ResultCode { get; set; }
public bool IsSessionPresent { get; set; } public bool IsSessionPresent { get; set; }
@@ -17,18 +21,34 @@ namespace MQTTnet.Client.Connecting
public string AssignedClientIdentifier { get; set; } public string AssignedClientIdentifier { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; } public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { get; set; } public uint? MaximumPacketSize { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; } public string ReasonString { get; set; }
public ushort? ReceiveMaximum { get; set; } public ushort? ReceiveMaximum { get; set; }
public MqttQualityOfServiceLevel MaximumQoS { get; set; } public MqttQualityOfServiceLevel MaximumQoS { get; set; }
/// <summary>
/// Gets or sets the response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ResponseInformation { get; set; } public string ResponseInformation { get; set; }
public ushort? TopicAliasMaximum { get; set; } public ushort? TopicAliasMaximum { get; set; }
@@ -39,10 +59,25 @@ namespace MQTTnet.Client.Connecting
public uint? SessionExpiryInterval { get; set; } public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the subscription identifiers are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SubscriptionIdentifiersAvailable { get; set; } public bool? SubscriptionIdentifiersAvailable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the shared subscriptions are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SharedSubscriptionAvailable { get; set; } public bool? SharedSubscriptionAvailable { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -9,6 +9,10 @@ namespace MQTTnet.Client.Connecting
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult)); AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
} }
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; } public MqttClientAuthenticateResult AuthenticateResult { get; }
} }
} }
@@ -1,9 +1,17 @@
namespace MQTTnet.Client.Disconnecting namespace MQTTnet.Client.Disconnecting
{ {
public class MqttClientDisconnectOptions public sealed class MqttClientDisconnectOptions
{ {
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection; public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; } public string ReasonString { get; set; }
} }
} }
@@ -12,7 +12,7 @@
ServerBusy = 137, ServerBusy = 137,
ServerShuttingDown = 139, ServerShuttingDown = 139,
BadAuthenticationMethod = 140, BadAuthenticationMethod = 140,
KeepaliveTimeout = 141, KeepAliveTimeout = 141,
SessionTakenOver = 142, SessionTakenOver = 142,
TopicFilterInvalid = 143, TopicFilterInvalid = 143,
TopicNameInvalid = 144, TopicNameInvalid = 144,
@@ -19,8 +19,16 @@ namespace MQTTnet.Client.Disconnecting
public Exception Exception { get; } public Exception Exception { get; }
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; } public MqttClientAuthenticateResult AuthenticateResult { get; }
/// <summary>
/// Gets or sets the reason.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientDisconnectReason Reason { get; set; } public MqttClientDisconnectReason Reason { get; set; }
[Obsolete("Please use 'Reason' instead. This property will be removed in the future!")] [Obsolete("Please use 'Reason' instead. This property will be removed in the future!")]
@@ -20,14 +20,37 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
Client = client ?? throw new ArgumentNullException(nameof(client)); Client = client ?? throw new ArgumentNullException(nameof(client));
} }
/// <summary>
/// Gets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; } public MqttAuthenticateReasonCode ReasonCode { get; }
/// <summary>
/// Gets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; } public string ReasonString { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; } public string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; } public byte[] AuthenticationData { get; }
/// <summary>
/// Gets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; } public List<MqttUserProperty> UserProperties { get; }
public IMqttClient Client { get; } public IMqttClient Client { get; }
@@ -6,12 +6,33 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
{ {
public class MqttExtendedAuthenticationExchangeData public class MqttExtendedAuthenticationExchangeData
{ {
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; set; } public MqttAuthenticateReasonCode ReasonCode { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; } public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Authentication data is binary information used to transmit multiple iterations of cryptographic secrets of protocol steps.
/// The content of the authentication data is highly dependent on the specific implementation of the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; } public byte[] AuthenticationData { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; } public List<MqttUserProperty> UserProperties { get; }
} }
} }
+20
View File
@@ -13,18 +13,38 @@ namespace MQTTnet.Client
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{ {
bool IsConnected { get; } bool IsConnected { get; }
IMqttClientOptions Options { get; } IMqttClientOptions Options { get; }
/// <summary>
/// Gets or sets the connected handler that is fired after the client has connected to the server successfully.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttClientConnectedHandler ConnectedHandler { get; set; } IMqttClientConnectedHandler ConnectedHandler { get; set; }
/// <summary>
/// Gets or sets the disconnected handler that is fired after the client has disconnected from the server.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken); Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken); Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
Task PingAsync(CancellationToken cancellationToken); Task PingAsync(CancellationToken cancellationToken);
/// <summary>
/// Sends extended authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="data">The extended data.</param>
/// <param name="cancellationToken">A cancellation token to stop the task.</param>
/// <returns>A <see cref="Task"/> representing any asynchronous operation.</returns>
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken); Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken);
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken); Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken);
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken); Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken);
} }
} }
+1 -2
View File
@@ -1,5 +1,4 @@
using System; using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.LowLevelClient; using MQTTnet.LowLevelClient;
+42 -66
View File
@@ -167,7 +167,7 @@ namespace MQTTnet.Client
public Task PingAsync(CancellationToken cancellationToken) public Task PingAsync(CancellationToken cancellationToken)
{ {
return SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken); return SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken);
} }
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken) public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken)
@@ -280,16 +280,25 @@ namespace MQTTnet.Client
async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
{ {
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket( MqttClientAuthenticateResult result;
willApplicationMessage,
Options);
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false); try
var result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket); {
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
Options);
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
}
catch (Exception exception)
{
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
}
if (result.ResultCode != MqttClientConnectResultCode.Success) if (result.ResultCode != MqttClientConnectResultCode.Success)
{ {
throw new MqttConnectingFailedException(result); throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
} }
_logger.Verbose("Authenticated MQTT connection with server established."); _logger.Verbose("Authenticated MQTT connection with server established.");
@@ -404,10 +413,10 @@ namespace MQTTnet.Client
_lastSentAtTicks = Environment.TickCount; _lastSentAtTicks = Environment.TickCount;
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false); await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) catch (Exception exception)
{ {
_logger.Warning(e, "Error when sending request packet ({0}).", requestPacket.GetType().Name); _logger.Warning(exception, "Error when sending request packet ({0}).", requestPacket.GetType().Name);
packetAwaiter.Cancel(); packetAwaiter.Fail(exception);
} }
try try
@@ -630,7 +639,7 @@ namespace MQTTnet.Client
} }
var publishPacket = publishPacketDequeueResult.Item; var publishPacket = publishPacketDequeueResult.Item;
var publishResult = await HandleReceivedApplicationMessageAsync(publishPacket); var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket);
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
@@ -638,28 +647,18 @@ namespace MQTTnet.Client
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{ {
if (publishResult.ProcessingSucceeded) if (!eventArgs.ProcessingFailed)
{ {
var pubAckPacket = new MqttPubAckPacket var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(publishPacket, eventArgs.ReasonCode);
{ await SendResponseForReceivedPublishPacket(eventArgs, pubAckPacket, cancellationToken).ConfigureAwait(false);
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubAckReasonCode.Success
};
await SendResponseForReceivedPublishPacket(publishResult, pubAckPacket, cancellationToken).ConfigureAwait(false);
} }
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{ {
if (publishResult.ProcessingSucceeded) if (!eventArgs.ProcessingFailed)
{ {
var pubRecPacket = new MqttPubRecPacket var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(publishPacket, eventArgs.ReasonCode);
{ await SendResponseForReceivedPublishPacket(eventArgs, pubRecPacket, cancellationToken).ConfigureAwait(false);
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success
};
await SendResponseForReceivedPublishPacket(publishResult, pubRecPacket, cancellationToken).ConfigureAwait(false);
} }
} }
else else
@@ -677,15 +676,15 @@ namespace MQTTnet.Client
} }
} }
async Task SendResponseForReceivedPublishPacket(ReceivedApplicationMessageResult publishResult, MqttBasePacket resultPacket, CancellationToken cancellationToken) async Task SendResponseForReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs, MqttBasePacket resultPacket, CancellationToken cancellationToken)
{ {
if (publishResult.PendingTask == null) if (eventArgs.PendingTask == null)
{ {
await SendAsync(resultPacket, cancellationToken).ConfigureAwait(false); await SendAsync(resultPacket, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
_ = publishResult.PendingTask.ContinueWith(async x => _ = eventArgs.PendingTask.ContinueWith(async x =>
{ {
try try
{ {
@@ -706,13 +705,10 @@ namespace MQTTnet.Client
{ {
if (!_packetDispatcher.TryDispatch(pubRecPacket)) if (!_packetDispatcher.TryDispatch(pubRecPacket))
{ {
// The packet is unknown. Probably due to a restart of the client. // The packet is unknown. Probably due to a restart of the client.
// So wen send this to the server to trigger a full resend of the message. // So wen send this to the server to trigger a full resend of the message.
return SendAsync(new MqttPubRelPacket var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
{ return SendAsync(pubRelPacket, cancellationToken);
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound
}, cancellationToken);
} }
return PlatformAbstractionLayer.CompletedTask; return PlatformAbstractionLayer.CompletedTask;
@@ -720,11 +716,8 @@ namespace MQTTnet.Client
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{ {
return SendAsync(new MqttPubCompPacket var pubCompPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
{ return SendAsync(pubCompPacket, cancellationToken);
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken);
} }
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket) Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
@@ -757,14 +750,14 @@ namespace MQTTnet.Client
{ {
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(null); return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(null);
} }
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{ {
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false); var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(response); return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(response);
} }
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
@@ -773,42 +766,25 @@ namespace MQTTnet.Client
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false); var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
};
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false); var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket); return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
} }
struct ReceivedApplicationMessageResult async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{
public readonly bool ProcessingSucceeded;
public readonly Task<bool> PendingTask;
public ReceivedApplicationMessageResult(bool processingSucceeded, Task<bool> pendingTask)
{
this.ProcessingSucceeded = processingSucceeded;
this.PendingTask = pendingTask;
}
}
async Task<ReceivedApplicationMessageResult> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{ {
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket); var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
var handler = ApplicationMessageReceivedHandler; var handler = ApplicationMessageReceivedHandler;
if (handler != null) if (handler != null)
{ {
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false); await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
return new ReceivedApplicationMessageResult(!eventArgs.ProcessingFailed, eventArgs.PendingTask);
} }
return new ReceivedApplicationMessageResult(true, null); return eventArgs;
} }
async Task WaitForTaskAsync(Task task, Task sender) async Task WaitForTaskAsync(Task task, Task sender)
+15 -8
View File
@@ -128,7 +128,21 @@ namespace MQTTnet.Client
{ {
if (client == null) throw new ArgumentNullException(nameof(client)); if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(new MqttClientDisconnectOptions()); return client.DisconnectAsync(CancellationToken.None);
}
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(options, CancellationToken.None);
}
public static Task DisconnectAsync(this IMqttClient client, CancellationToken cancellationToken)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(new MqttClientDisconnectOptions(), cancellationToken);
} }
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters) public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters)
@@ -176,13 +190,6 @@ namespace MQTTnet.Client
return client.ConnectAsync(options, CancellationToken.None); return client.ConnectAsync(options, CancellationToken.None);
} }
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(options, CancellationToken.None);
}
public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data) public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data)
{ {
if (client == null) throw new ArgumentNullException(nameof(client)); if (client == null) throw new ArgumentNullException(nameof(client));
@@ -3,31 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
public interface IMqttClientOptions public interface IMqttClientOptions
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; } string ClientId { get; }
/// <summary>
/// Gets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
bool CleanSession { get; } bool CleanSession { get; }
IMqttClientCredentials Credentials { get; } IMqttClientCredentials Credentials { get; }
IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; } IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; }
MqttProtocolVersion ProtocolVersion { get; } MqttProtocolVersion ProtocolVersion { get; }
IMqttClientChannelOptions ChannelOptions { get; } IMqttClientChannelOptions ChannelOptions { get; }
TimeSpan CommunicationTimeout { get; } TimeSpan CommunicationTimeout { get; }
/// <summary>
/// Gets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
TimeSpan KeepAlivePeriod { get; } TimeSpan KeepAlivePeriod { get; }
/// <summary>
/// Gets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
MqttApplicationMessage WillMessage { get; } MqttApplicationMessage WillMessage { get; }
/// <summary>
/// Gets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
uint? WillDelayInterval { get; } uint? WillDelayInterval { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
string AuthenticationMethod { get; } string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
byte[] AuthenticationData { get; } byte[] AuthenticationData { get; }
uint? MaximumPacketSize { get; } uint? MaximumPacketSize { get; }
/// <summary>
/// Gets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
ushort? ReceiveMaximum { get; } ushort? ReceiveMaximum { get; }
/// <summary>
/// Gets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestProblemInformation { get; } bool? RequestProblemInformation { get; }
/// <summary>
/// Gets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestResponseInformation { get; } bool? RequestResponseInformation { get; }
/// <summary>
/// Gets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// </summary>
uint? SessionExpiryInterval { get; } uint? SessionExpiryInterval { get; }
/// <summary>
/// Gets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
ushort? TopicAliasMaximum { get; } ushort? TopicAliasMaximum { get; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
List<MqttUserProperty> UserProperties { get; set; } List<MqttUserProperty> UserProperties { get; set; }
IMqttPacketInspector PacketInspector { get; set; }
} }
} }
@@ -3,33 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
public class MqttClientOptions : IMqttClientOptions public class MqttClientOptions : IMqttClientOptions
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; set; } = Guid.NewGuid().ToString("N"); public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
/// <summary>
/// Gets or sets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
public bool CleanSession { get; set; } = true; public bool CleanSession { get; set; } = true;
public IMqttClientCredentials Credentials { get; set; } public IMqttClientCredentials Credentials { get; set; }
public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; } public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; }
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public IMqttClientChannelOptions ChannelOptions { get; set; } public IMqttClientChannelOptions ChannelOptions { get; set; }
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
/// <summary>
/// Gets or sets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15); public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
/// <summary>
/// Gets or sets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
public MqttApplicationMessage WillMessage { get; set; } public MqttApplicationMessage WillMessage { get; set; }
/// <summary>
/// Gets or sets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
public uint? WillDelayInterval { get; set; } public uint? WillDelayInterval { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; } public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { get; set; } public uint? MaximumPacketSize { get; set; }
/// <summary>
/// Gets or sets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
public ushort? ReceiveMaximum { get; set; } public ushort? ReceiveMaximum { get; set; }
/// <summary>
/// Gets or sets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestProblemInformation { get; set; } public bool? RequestProblemInformation { get; set; }
/// <summary>
/// Gets or sets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestResponseInformation { get; set; } public bool? RequestResponseInformation { get; set; }
/// <summary>
/// Gets or sets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// </summary>
public uint? SessionExpiryInterval { get; set; } public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
public ushort? TopicAliasMaximum { get; set; } public ushort? TopicAliasMaximum { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
public IMqttPacketInspector PacketInspector { get; set; }
} }
} }
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
@@ -256,6 +257,12 @@ namespace MQTTnet.Client.Options
return this; return this;
} }
public MqttClientOptionsBuilder WithPacketInspector(IMqttPacketInspector packetInspector)
{
_options.PacketInspector = packetInspector;
return this;
}
public IMqttClientOptions Build() public IMqttClientOptions Build()
{ {
if (_tcpOptions == null && _webSocketOptions == null) if (_tcpOptions == null && _webSocketOptions == null)
@@ -8,10 +8,25 @@ namespace MQTTnet.Client.Publishing
{ {
public ushort? PacketIdentifier { get; set; } public ushort? PacketIdentifier { get; set; }
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success; public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; } public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -5,10 +5,28 @@ namespace MQTTnet.Client.Subscribing
{ {
public class MqttClientSubscribeOptions public class MqttClientSubscribeOptions
{ {
/// <summary>
/// Gets or sets a list of topic filters the client wants to subscribe to.
/// Topic filters can include regular topics or wild cards.
/// </summary>
public List<MqttTopicFilter> TopicFilters { get; set; } = new List<MqttTopicFilter>(); public List<MqttTopicFilter> TopicFilters { get; set; } = new List<MqttTopicFilter>();
/// <summary>
/// Gets or sets the subscription identifier.
/// The client can specify a subscription identifier when subscribing.
/// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription.
/// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client.
/// Hint: MQTT 5 feature only.
/// </summary>
public uint? SubscriptionIdentifier { get; set; } public uint? SubscriptionIdentifier { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -9,6 +9,13 @@ namespace MQTTnet.Client.Subscribing
{ {
private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions(); private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions();
/// <summary>
/// Adds the user property to the subscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="value">The property value.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttClientSubscribeOptionsBuilder WithUserProperty(string name, string value) public MqttClientSubscribeOptionsBuilder WithUserProperty(string name, string value)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
@@ -27,7 +34,6 @@ namespace MQTTnet.Client.Subscribing
public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier) public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier)
{ {
_subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier; _subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier;
return this; return this;
} }
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Subscribing
ResultCode = resultCode; ResultCode = resultCode;
} }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public MqttTopicFilter TopicFilter { get; } public MqttTopicFilter TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientSubscribeResultCode ResultCode { get; } public MqttClientSubscribeResultCode ResultCode { get; }
} }
} }
@@ -5,8 +5,19 @@ namespace MQTTnet.Client.Unsubscribing
{ {
public class MqttClientUnsubscribeOptions public class MqttClientUnsubscribeOptions
{ {
/// <summary>
/// Gets or sets a list of topic filters the client wants to unsubscribe from.
/// Topic filters can include regular topics or wild cards.
/// </summary>
public List<string> TopicFilters { get; set; } = new List<string>(); public List<string> TopicFilters { get; set; } = new List<string>();
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>(); public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
} }
} }
@@ -8,6 +8,13 @@ namespace MQTTnet.Client.Unsubscribing
{ {
private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions(); private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions();
/// <summary>
/// Adds the user property to the unsubscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="value">The property value.</param>
/// <returns>A new instance of the <see cref="MqttClientUnsubscribeOptionsBuilder"/> class.</returns>
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value) public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value)
{ {
if (name is null) throw new ArgumentNullException(nameof(name)); if (name is null) throw new ArgumentNullException(nameof(name));
@@ -16,6 +23,12 @@ namespace MQTTnet.Client.Unsubscribing
return WithUserProperty(new MqttUserProperty(name, value)); return WithUserProperty(new MqttUserProperty(name, value));
} }
/// <summary>
/// Adds the user property to the unsubscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="userProperty">The user property.</param>
/// <returns>A new instance of the <see cref="MqttClientUnsubscribeOptionsBuilder"/> class.</returns>
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty) public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty)
{ {
if (userProperty is null) throw new ArgumentNullException(nameof(userProperty)); if (userProperty is null) throw new ArgumentNullException(nameof(userProperty));
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Unsubscribing
ReasonCode = reasonCode; ReasonCode = reasonCode;
} }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public string TopicFilter { get; } public string TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientUnsubscribeResultCode ReasonCode { get; } public MqttClientUnsubscribeResultCode ReasonCode { get; }
} }
} }
@@ -18,6 +18,11 @@ namespace MQTTnet.Diagnostics
{ {
logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception); logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception);
} }
public static void Warning(this IMqttNetScopedLogger logger, string message, params object[] parameters)
{
logger.Publish(MqttNetLogLevel.Warning, message, parameters, null);
}
public static void Error(this IMqttNetScopedLogger logger, Exception exception, string message, params object[] parameters) public static void Error(this IMqttNetScopedLogger logger, Exception exception, string message, params object[] parameters)
{ {
@@ -0,0 +1,7 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public interface IMqttPacketInspector
{
void ProcessMqttPacket(ProcessMqttPacketContext context);
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public enum MqttPacketFlowDirection
{
Inbound,
Outbound
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public sealed class ProcessMqttPacketContext
{
public MqttPacketFlowDirection Direction { get; set; }
public byte[] Buffer { get; set; }
}
}
+17 -13
View File
@@ -14,24 +14,32 @@ namespace MQTTnet.Formatter
{ {
public interface IMqttDataConverter public interface IMqttDataConverter
{ {
MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage);
MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket);
MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket);
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket); MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket); MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket);
MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket);
MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket);
MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket);
MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options); MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options);
MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext); MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext);
MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage);
MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket); MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket); MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options); MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult); MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult);
@@ -41,9 +49,5 @@ namespace MQTTnet.Formatter
MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes); MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes);
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options); MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket);
MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket);
} }
} }
@@ -11,7 +11,7 @@ namespace MQTTnet.Formatter
public sealed class MqttPacketFormatterAdapter public sealed class MqttPacketFormatterAdapter
{ {
IMqttPacketFormatter _formatter; IMqttPacketFormatter _formatter;
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion) public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
: this(protocolVersion, new MqttPacketWriter()) : this(protocolVersion, new MqttPacketWriter())
{ {
@@ -26,7 +26,7 @@ namespace MQTTnet.Formatter
public MqttPacketFormatterAdapter(IMqttPacketWriter writer) public MqttPacketFormatterAdapter(IMqttPacketWriter writer)
{ {
Writer = writer; Writer = writer;
} }
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown; public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
@@ -39,7 +39,7 @@ namespace MQTTnet.Formatter
return _formatter.DataConverter; return _formatter.DataConverter;
} }
} }
public IMqttPacketWriter Writer { get; } public IMqttPacketWriter Writer { get; }
public ArraySegment<byte> Encode(MqttBasePacket packet) public ArraySegment<byte> Encode(MqttBasePacket packet)
@@ -69,10 +69,10 @@ namespace MQTTnet.Formatter
{ {
var protocolVersion = ParseProtocolVersion(receivedMqttPacket); var protocolVersion = ParseProtocolVersion(receivedMqttPacket);
// Reset the position of the stream because the protocol version is part of // Reset the position of the stream because the protocol version is part of
// the regular CONNECT packet. So it will not properly deserialized if this // the regular CONNECT packet. So it will not properly deserialized if this
// data is missing. // data is missing.
receivedMqttPacket.Body.Seek(0); receivedMqttPacket.BodyReader.Seek(0);
UseProtocolVersion(protocolVersion); UseProtocolVersion(protocolVersion);
} }
@@ -83,7 +83,7 @@ namespace MQTTnet.Formatter
{ {
throw new InvalidOperationException("MQTT protocol version is invalid."); throw new InvalidOperationException("MQTT protocol version is invalid.");
} }
switch (protocolVersion) switch (protocolVersion)
{ {
case MqttProtocolVersion.V500: case MqttProtocolVersion.V500:
@@ -120,7 +120,7 @@ namespace MQTTnet.Formatter
{ {
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket)); if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
if (receivedMqttPacket.Body.Length < 7) if (receivedMqttPacket.BodyReader.Length < 7)
{ {
// 2 byte protocol name length // 2 byte protocol name length
// at least 4 byte protocol name // at least 4 byte protocol name
@@ -128,8 +128,8 @@ namespace MQTTnet.Formatter
throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes."); throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes.");
} }
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix(); var protocolName = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix();
var protocolLevel = receivedMqttPacket.Body.ReadByte(); var protocolLevel = receivedMqttPacket.BodyReader.ReadByte();
if (protocolName == "MQTT") if (protocolName == "MQTT")
{ {
@@ -1,114 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Exceptions;
namespace MQTTnet.Formatter
{
public sealed class MqttPacketReader
{
readonly byte[] _singleByteBuffer = new byte[1];
readonly IMqttChannel _channel;
public MqttPacketReader(IMqttChannel channel)
{
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
}
public async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
var buffer = fixedHeaderBuffer;
var totalBytesRead = 0;
while (totalBytesRead < buffer.Length)
{
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (bytesRead == 0)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bytesRead;
}
var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
};
}
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
if (!bodyLength.HasValue)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bodyLength.Value;
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
};
}
async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
{
offset++;
if (offset > 3)
{
throw new MqttProtocolViolationException("Remaining length is invalid.");
}
if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readCount == 0)
{
return null;
}
encodedByte = _singleByteBuffer[0];
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
}
}
+7 -6
View File
@@ -14,7 +14,8 @@ namespace MQTTnet.Formatter
public sealed class MqttPacketWriter : IMqttPacketWriter public sealed class MqttPacketWriter : IMqttPacketWriter
{ {
public static int InitialBufferSize = 4096; public static int InitialBufferSize = 4096;
public static int MaxBufferSize = 4096 + 4;
public static int MaxBufferSize = 4096 * 4;
byte[] _buffer = new byte[InitialBufferSize]; byte[] _buffer = new byte[InitialBufferSize];
@@ -97,7 +98,7 @@ namespace MQTTnet.Formatter
_buffer[_offset] = (byte)(bufferSize >> 8); _buffer[_offset] = (byte)(bufferSize >> 8);
_buffer[_offset + 1] = (byte)bufferSize; _buffer[_offset + 1] = (byte)bufferSize;
Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _offset + 2); Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _offset + 2);
IncreasePosition(bufferSize + 2); IncreasePosition(bufferSize + 2);
@@ -121,7 +122,7 @@ namespace MQTTnet.Formatter
_buffer[_offset] = (byte)(value.Length >> 8); _buffer[_offset] = (byte)(value.Length >> 8);
_buffer[_offset + 1] = (byte)value.Length; _buffer[_offset + 1] = (byte)value.Length;
Array.Copy(value, 0, _buffer, _offset + 2, value.Length); Array.Copy(value, 0, _buffer, _offset + 2, value.Length);
IncreasePosition(value.Length + 2); IncreasePosition(value.Length + 2);
} }
@@ -201,8 +202,8 @@ namespace MQTTnet.Formatter
{ {
// This method frees the used memory by shrinking the buffer. This is required because the buffer // This method frees the used memory by shrinking the buffer. This is required because the buffer
// is used across several messages. In general this is not a big issue because subsequent Ping packages // is used across several messages. In general this is not a big issue because subsequent Ping packages
// have the same size but a very big publish package with 100 MB of payload will increase the buffer // have the same size but a very big publish package with 100 MB of payload will increase the buffer
// a lot and the size will never reduced. So this method tries to find a size which can be held in // a lot and the size will never reduced. So this method tries to find a size which can be held in
// memory for a long time without causing troubles. // memory for a long time without causing troubles.
if (_buffer.Length < MaxBufferSize) if (_buffer.Length < MaxBufferSize)
@@ -212,7 +213,7 @@ namespace MQTTnet.Formatter
Array.Resize(ref _buffer, MaxBufferSize); Array.Resize(ref _buffer, MaxBufferSize);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureAdditionalCapacity(int additionalCapacity) void EnsureAdditionalCapacity(int additionalCapacity)
{ {
@@ -31,21 +31,43 @@ namespace MQTTnet.Formatter.V3
}; };
} }
public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket) public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttPubAckPacket return new MqttPubAckPacket
{ {
PacketIdentifier = publishPacket.PacketIdentifier, PacketIdentifier = publishPacket.PacketIdentifier
ReasonCode = MqttPubAckReasonCode.Success
}; };
} }
public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket) public MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttPubRecPacket return new MqttPubRecPacket
{ {
PacketIdentifier = publishPacket.PacketIdentifier, PacketIdentifier = publishPacket.PacketIdentifier
ReasonCode = MqttPubRecReasonCode.Success };
}
public MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{
if (pubRelPacket == null) throw new ArgumentNullException(nameof(pubRelPacket));
return new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier
};
}
public MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{
if (pubRecPacket == null) throw new ArgumentNullException(nameof(pubRecPacket));
return new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier
}; };
} }
@@ -58,7 +80,8 @@ namespace MQTTnet.Formatter.V3
Topic = publishPacket.Topic, Topic = publishPacket.Topic,
Payload = publishPacket.Payload, Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain Retain = publishPacket.Retain,
Dup = publishPacket.Dup
}; };
} }
@@ -184,6 +207,9 @@ namespace MQTTnet.Formatter.V3
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult) public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
{ {
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
if (subscribeResult == null) throw new ArgumentNullException(nameof(subscribeResult));
var subackPacket = new MqttSubAckPacket var subackPacket = new MqttSubAckPacket
{ {
PacketIdentifier = subscribePacket.PacketIdentifier PacketIdentifier = subscribePacket.PacketIdentifier
@@ -206,6 +232,9 @@ namespace MQTTnet.Formatter.V3
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes) public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{ {
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
if (reasonCodes == null) throw new ArgumentNullException(nameof(reasonCodes));
return new MqttUnsubAckPacket return new MqttUnsubAckPacket
{ {
PacketIdentifier = unsubscribePacket.PacketIdentifier, PacketIdentifier = unsubscribePacket.PacketIdentifier,
@@ -223,7 +252,7 @@ namespace MQTTnet.Formatter.V3
return new MqttDisconnectPacket(); return new MqttDisconnectPacket();
} }
public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket) public MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket)
{ {
return new MqttClientPublishResult return new MqttClientPublishResult
{ {
@@ -232,7 +261,7 @@ namespace MQTTnet.Formatter.V3
}; };
} }
public MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) public MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket)
{ {
if (pubRecPacket == null || pubCompPacket == null) if (pubRecPacket == null || pubCompPacket == null)
{ {
@@ -63,20 +63,20 @@ namespace MQTTnet.Formatter.V3
switch ((MqttControlPacketType)controlPacketType) switch ((MqttControlPacketType)controlPacketType)
{ {
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DisconnectPacket; case MqttControlPacketType.Disconnect: return DisconnectPacket;
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket); case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return PingReqPacket; case MqttControlPacketType.PingReq: return PingReqPacket;
case MqttControlPacketType.PingResp: return PingRespPacket; case MqttControlPacketType.PingResp: return PingRespPacket;
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
} }
@@ -202,18 +202,18 @@ namespace MQTTnet.Formatter.V3
static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket) static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket)
{ {
ThrowIfBodyIsEmpty(receivedMqttPacket.Body); ThrowIfBodyIsEmpty(receivedMqttPacket.BodyReader);
var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0; var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0;
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3); var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3);
var dup = (receivedMqttPacket.FixedHeader & 0x8) > 0; var dup = (receivedMqttPacket.FixedHeader & 0x8) > 0;
var topic = receivedMqttPacket.Body.ReadStringWithLengthPrefix(); var topic = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix();
ushort packetIdentifier = 0; ushort packetIdentifier = 0;
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger(); packetIdentifier = receivedMqttPacket.BodyReader.ReadTwoByteInteger();
} }
var packet = new MqttPublishPacket var packet = new MqttPublishPacket
@@ -225,9 +225,9 @@ namespace MQTTnet.Formatter.V3
Dup = dup Dup = dup
}; };
if (!receivedMqttPacket.Body.EndOfStream) if (!receivedMqttPacket.BodyReader.EndOfStream)
{ {
packet.Payload = receivedMqttPacket.Body.ReadRemainingData(); packet.Payload = receivedMqttPacket.BodyReader.ReadRemainingData();
} }
return packet; return packet;
@@ -15,7 +15,7 @@ using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribe
namespace MQTTnet.Formatter.V5 namespace MQTTnet.Formatter.V5
{ {
public class MqttV500DataConverter : IMqttDataConverter public sealed class MqttV500DataConverter : IMqttDataConverter
{ {
public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage) public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage)
{ {
@@ -27,7 +27,7 @@ namespace MQTTnet.Formatter.V5
Payload = applicationMessage.Payload, Payload = applicationMessage.Payload,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain, Retain = applicationMessage.Retain,
Dup = false, Dup = applicationMessage.Dup,
Properties = new MqttPublishPacketProperties Properties = new MqttPublishPacketProperties
{ {
ContentType = applicationMessage.ContentType, ContentType = applicationMessage.ContentType,
@@ -49,32 +49,61 @@ namespace MQTTnet.Formatter.V5
return packet; return packet;
} }
public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket) public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttPubAckPacket return new MqttPubAckPacket
{ {
PacketIdentifier = publishPacket.PacketIdentifier, PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubAckReasonCode.Success ReasonCode = (MqttPubAckReasonCode)(int)reasonCode
}; };
} }
public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket) public MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttPubRecPacket return new MqttPubRecPacket
{ {
PacketIdentifier = publishPacket.PacketIdentifier, PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success ReasonCode = (MqttPubRecReasonCode)(int)reasonCode
};
}
public MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{
if (pubRelPacket == null) throw new ArgumentNullException(nameof(pubRelPacket));
return new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = (MqttPubCompReasonCode)(int)reasonCode
};
}
public MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{
if (pubRecPacket == null) throw new ArgumentNullException(nameof(pubRecPacket));
return new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = (MqttPubRelReasonCode)(int)reasonCode
}; };
} }
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket) public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttApplicationMessage return new MqttApplicationMessage
{ {
Topic = publishPacket.Topic, Topic = publishPacket.Topic,
Payload = publishPacket.Payload, Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain, Retain = publishPacket.Retain,
Dup = publishPacket.Dup,
ResponseTopic = publishPacket.Properties?.ResponseTopic, ResponseTopic = publishPacket.Properties?.ResponseTopic,
ContentType = publishPacket.Properties?.ContentType, ContentType = publishPacket.Properties?.ContentType,
CorrelationData = publishPacket.Properties?.CorrelationData, CorrelationData = publishPacket.Properties?.CorrelationData,
@@ -93,7 +122,7 @@ namespace MQTTnet.Formatter.V5
return new MqttClientAuthenticateResult return new MqttClientAuthenticateResult
{ {
IsSessionPresent = connAckPacket.IsSessionPresent, IsSessionPresent = connAckPacket.IsSessionPresent,
ResultCode = (MqttClientConnectResultCode)connAckPacket.ReasonCode.Value, ResultCode = (MqttClientConnectResultCode)(int)(connAckPacket.ReasonCode ?? 0),
WildcardSubscriptionAvailable = connAckPacket.Properties?.WildcardSubscriptionAvailable, WildcardSubscriptionAvailable = connAckPacket.Properties?.WildcardSubscriptionAvailable,
RetainAvailable = connAckPacket.Properties?.RetainAvailable, RetainAvailable = connAckPacket.Properties?.RetainAvailable,
AssignedClientIdentifier = connAckPacket.Properties?.AssignedClientIdentifier, AssignedClientIdentifier = connAckPacket.Properties?.AssignedClientIdentifier,
@@ -144,6 +173,8 @@ namespace MQTTnet.Formatter.V5
public MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext) public MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext)
{ {
if (connectionValidatorContext == null) throw new ArgumentNullException(nameof(connectionValidatorContext));
return new MqttConnAckPacket return new MqttConnAckPacket
{ {
ReasonCode = connectionValidatorContext.ReasonCode, ReasonCode = connectionValidatorContext.ReasonCode,
@@ -213,6 +244,9 @@ namespace MQTTnet.Formatter.V5
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult) public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
{ {
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
if (subscribeResult == null) throw new ArgumentNullException(nameof(subscribeResult));
var subackPacket = new MqttSubAckPacket var subackPacket = new MqttSubAckPacket
{ {
PacketIdentifier = subscribePacket.PacketIdentifier PacketIdentifier = subscribePacket.PacketIdentifier
@@ -240,6 +274,9 @@ namespace MQTTnet.Formatter.V5
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes) public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{ {
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
if (reasonCodes == null) throw new ArgumentNullException(nameof(reasonCodes));
return new MqttUnsubAckPacket return new MqttUnsubAckPacket
{ {
PacketIdentifier = unsubscribePacket.PacketIdentifier, PacketIdentifier = unsubscribePacket.PacketIdentifier,
@@ -263,7 +300,7 @@ namespace MQTTnet.Formatter.V5
return packet; return packet;
} }
public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket) public MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket)
{ {
var result = new MqttClientPublishResult var result = new MqttClientPublishResult
{ {
@@ -276,7 +313,7 @@ namespace MQTTnet.Formatter.V5
{ {
// QoS 0 has no response. So we treat it as a success always. // QoS 0 has no response. So we treat it as a success always.
// Both enums have the same values. So it can be easily converted. // Both enums have the same values. So it can be easily converted.
result.ReasonCode = (MqttClientPublishReasonCode)pubAckPacket.ReasonCode; result.ReasonCode = (MqttClientPublishReasonCode)(int)(pubAckPacket.ReasonCode ?? 0);
result.PacketIdentifier = pubAckPacket.PacketIdentifier; result.PacketIdentifier = pubAckPacket.PacketIdentifier;
} }
@@ -284,7 +321,7 @@ namespace MQTTnet.Formatter.V5
return result; return result;
} }
public MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) public MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket)
{ {
if (pubRecPacket == null || pubCompPacket == null) if (pubRecPacket == null || pubCompPacket == null)
{ {
@@ -317,10 +354,10 @@ namespace MQTTnet.Formatter.V5
if (pubRecPacket.ReasonCode.HasValue) if (pubRecPacket.ReasonCode.HasValue)
{ {
// Both enums share the same values. // Both enums share the same values.
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode.Value; result.ReasonCode = (MqttClientPublishReasonCode)(pubRecPacket.ReasonCode ?? 0);
} }
return result; return result;
} }
} }
} }
@@ -25,21 +25,21 @@ namespace MQTTnet.Formatter.V5
switch ((MqttControlPacketType)controlPacketType) switch ((MqttControlPacketType)controlPacketType)
{ {
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body); case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return DecodePingReqPacket(); case MqttControlPacketType.PingReq: return DecodePingReqPacket();
case MqttControlPacketType.PingResp: return DecodePingRespPacket(); case MqttControlPacketType.PingResp: return DecodePingRespPacket();
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body); case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
} }
@@ -4,6 +4,10 @@ namespace MQTTnet
{ {
public interface IApplicationMessageReceiver public interface IApplicationMessageReceiver
{ {
/// <summary>
/// Gets or sets the application message received handler that is fired every time a new message is received on the client's subscriptions.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; } IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
} }
} }
@@ -3,6 +3,7 @@ using MQTTnet.Client.Options;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using System; using System;
using MQTTnet.Channel;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
@@ -19,16 +20,19 @@ namespace MQTTnet.Implementations
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
IMqttChannel channel;
switch (options.ChannelOptions) switch (options.ChannelOptions)
{ {
case MqttClientTcpOptions _: case MqttClientTcpOptions _:
{ {
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); channel = new MqttTcpChannel(options);
break;
} }
case MqttClientWebSocketOptions webSocketOptions: case MqttClientWebSocketOptions webSocketOptions:
{ {
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); channel = new MqttWebSocketChannel(webSocketOptions);
break;
} }
default: default:
@@ -36,6 +40,9 @@ namespace MQTTnet.Implementations
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter());
return new MqttChannelAdapter(channel, packetFormatterAdapter, options.PacketInspector, _logger);
} }
} }
} }
@@ -77,7 +77,7 @@ namespace MQTTnet.Implementations
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var networkStream = socket.GetStream(); var networkStream = socket.GetStream();
if (_tcpOptions.TlsOptions?.UseTls == true) if (_tcpOptions.TlsOptions?.UseTls == true)
{ {
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
@@ -94,7 +94,7 @@ namespace MQTTnet.Implementations
}; };
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false); await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
#else #else
await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
#endif #endif
} }
@@ -89,7 +89,7 @@ namespace MQTTnet.Implementations
} }
} }
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger)) using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, _rootLogger))
{ {
await clientHandler(clientAdapter).ConfigureAwait(false); await clientHandler(clientAdapter).ConfigureAwait(false);
} }
@@ -178,7 +178,11 @@ namespace MQTTnet.Implementations
var clientHandler = ClientHandler; var clientHandler = ClientHandler;
if (clientHandler != null) if (clientHandler != null)
{ {
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger)) using (var clientAdapter = new MqttChannelAdapter(
new MqttTcpChannel(stream, remoteEndPoint, clientCertificate),
new MqttPacketFormatterAdapter(new MqttPacketWriter()),
null,
_rootLogger))
{ {
await clientHandler(clientAdapter).ConfigureAwait(false); await clientHandler(clientAdapter).ConfigureAwait(false);
} }
+6 -1
View File
@@ -6,7 +6,7 @@ using MQTTnet.Channel;
namespace MQTTnet.Internal namespace MQTTnet.Internal
{ {
public class TestMqttChannel : IMqttChannel public sealed class TestMqttChannel : IMqttChannel
{ {
readonly MemoryStream _stream; readonly MemoryStream _stream;
@@ -15,6 +15,11 @@ namespace MQTTnet.Internal
_stream = stream; _stream = stream;
} }
public TestMqttChannel(byte[] buffer)
{
_stream = new MemoryStream(buffer);
}
public string Endpoint { get; } = "<Test channel>"; public string Endpoint { get; } = "<Test channel>";
public bool IsSecureConnection { get; } = false; public bool IsSecureConnection { get; } = false;
-20
View File
@@ -35,14 +35,6 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'"> <PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
@@ -54,20 +46,8 @@
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" /> <PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'"> <ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.10" /> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.10" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
</ItemGroup>
</Project> </Project>
+79
View File
@@ -6,28 +6,107 @@ namespace MQTTnet
{ {
public class MqttApplicationMessage public class MqttApplicationMessage
{ {
/// <summary>
/// Gets or sets the MQTT topic.
/// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client.
/// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary>
public string Topic { get; set; } public string Topic { get; set; }
/// <summary>
/// Gets or sets the payload.
/// The payload is the data bytes sent via the MQTT protocol.
/// </summary>
public byte[] Payload { get; set; } public byte[] Payload { get; set; }
/// <summary>
/// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the message should be retained or not.
/// A retained message is a normal MQTT message with the retained flag set to true.
/// The broker stores the last retained message and the corresponding QoS for that topic.
/// </summary>
public bool Retain { get; set; } public bool Retain { get; set; }
/// <summary>
/// If the DUP flag is set to 0, it indicates that this is the first occasion that the Client or Server has attempted to send this MQTT PUBLISH Packet.
/// If the DUP flag is set to 1, it indicates that this might be re-delivery of an earlier attempt to send the Packet.
/// The DUP flag MUST be set to 1 by the Client or Server when it attempts to re-deliver a PUBLISH Packet [MQTT-3.3.1.-1].
/// The DUP flag MUST be set to 0 for all QoS 0 messages [MQTT-3.3.1-2].
/// </summary>
public bool Dup { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
/// <summary>
/// Gets or sets the content type.
/// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload.
/// </summary>
public string ContentType { get; set; } public string ContentType { get; set; }
/// <summary>
/// Gets or sets the response topic.
/// In MQTT 5 the ability to publish a response topic was added in the publish message which allows you to implement the request/response pattern between clients that is common in web applications.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ResponseTopic { get; set; } public string ResponseTopic { get; set; }
/// <summary>
/// Gets or sets the payload format indicator.
/// The payload format indicator is part of any MQTT packet that can contain a payload. The indicator is an optional byte value.
/// A value of 0 indicates an “unspecified byte stream”.
/// A value of 1 indicates a "UTF-8 encoded payload".
/// If no payload format indicator is provided, the default value is 0.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; } public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; }
/// <summary>
/// Gets or sets the message expiry interval.
/// A client can set the message expiry interval in seconds for each PUBLISH message individually.
/// This interval defines the period of time that the broker stores the PUBLISH message for any matching subscribers that are not currently connected.
/// When no message expiry interval is set, the broker must store the message for matching subscribers indefinitely.
/// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic.
/// Hint: MQTT 5 feature only.
/// </summary>
public uint? MessageExpiryInterval { get; set; } public uint? MessageExpiryInterval { get; set; }
/// <summary>
/// Gets or sets the topic alias.
/// Topic aliases were introduced are a mechanism for reducing the size of published packets by reducing the size of the topic field.
/// Hint: MQTT 5 feature only.
/// </summary>
public ushort? TopicAlias { get; set; } public ushort? TopicAlias { get; set; }
/// <summary>
/// Gets or sets the correlation data.
/// In order for the sender to know what sent message the response refers to it can also send correlation data with the published message.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] CorrelationData { get; set; } public byte[] CorrelationData { get; set; }
/// <summary>
/// Gets or sets the subscription identifiers.
/// The client can specify a subscription identifier when subscribing.
/// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription.
/// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<uint> SubscriptionIdentifiers { get; set; } public List<uint> SubscriptionIdentifiers { get; set; }
} }
} }
+141 -26
View File
@@ -11,18 +11,102 @@ namespace MQTTnet
{ {
public class MqttApplicationMessageBuilder public class MqttApplicationMessageBuilder
{ {
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; /// <summary>
private string _topic; /// The quality of service level.
private byte[] _payload; /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
private bool _retain; /// There are 3 QoS levels in MQTT:
private string _contentType; /// - At most once (0): Message gets delivered no time, once or multiple times.
private string _responseTopic; /// - At least once (1): Message gets delivered at least once (one time or more often).
private byte[] _correlationData; /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
private ushort? _topicAlias; /// </summary>
private List<uint> _subscriptionIdentifiers; MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
private uint? _messageExpiryInterval;
private MqttPayloadFormatIndicator? _payloadFormatIndicator; /// <summary>
private List<MqttUserProperty> _userProperties; /// The MQTT topic.
/// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client.
/// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary>
string _topic;
/// <summary>
/// The payload.
/// The payload is the data bytes sent via the MQTT protocol.
/// </summary>
byte[] _payload;
/// <summary>
/// A value indicating whether the message should be retained or not.
/// A retained message is a normal MQTT message with the retained flag set to true.
/// The broker stores the last retained message and the corresponding QoS for that topic.
/// </summary>
bool _retain;
bool _dup;
/// <summary>
/// The content type.
/// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload.
/// </summary>
string _contentType;
/// <summary>
/// The response topic.
/// In MQTT 5 the ability to publish a response topic was added in the publish message which allows you to implement the request/response pattern between clients that is common in web applications.
/// Hint: MQTT 5 feature only.
/// </summary>
string _responseTopic;
/// <summary>
/// The correlation data.
/// In order for the sender to know what sent message the response refers to it can also send correlation data with the published message.
/// Hint: MQTT 5 feature only.
/// </summary>
byte[] _correlationData;
/// <summary>
/// The topic alias.
/// Topic aliases were introduced are a mechanism for reducing the size of published packets by reducing the size of the topic field.
/// Hint: MQTT 5 feature only.
/// </summary>
ushort? _topicAlias;
/// <summary>
/// The subscription identifiers.
/// The client can specify a subscription identifier when subscribing.
/// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription.
/// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client.
/// Hint: MQTT 5 feature only.
/// </summary>
List<uint> _subscriptionIdentifiers;
/// <summary>
/// The message expiry interval.
/// A client can set the message expiry interval in seconds for each PUBLISH message individually.
/// This interval defines the period of time that the broker stores the PUBLISH message for any matching subscribers that are not currently connected.
/// When no message expiry interval is set, the broker must store the message for matching subscribers indefinitely.
/// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic.
/// Hint: MQTT 5 feature only.
/// </summary>
uint? _messageExpiryInterval;
/// <summary>
/// The payload format indicator.
/// The payload format indicator is part of any MQTT packet that can contain a payload. The indicator is an optional byte value.
/// A value of 0 indicates an “unspecified byte stream”.
/// A value of 1 indicates a "UTF-8 encoded payload".
/// If no payload format indicator is provided, the default value is 0.
/// Hint: MQTT 5 feature only.
/// </summary>
MqttPayloadFormatIndicator? _payloadFormatIndicator;
/// <summary>
/// The user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// </summary>
/// Hint: MQTT 5 feature only.
List<MqttUserProperty> _userProperties;
public MqttApplicationMessageBuilder WithTopic(string topic) public MqttApplicationMessageBuilder WithTopic(string topic)
{ {
@@ -44,12 +128,7 @@ namespace MQTTnet
return this; return this;
} }
_payload = payload as byte[]; _payload = payload as byte[] ?? payload.ToArray();
if (_payload == null)
{
_payload = payload.ToArray();
}
return this; return this;
} }
@@ -119,6 +198,12 @@ namespace MQTTnet
_retain = value; _retain = value;
return this; return this;
} }
public MqttApplicationMessageBuilder WithDupFlag(bool value = true)
{
_dup = value;
return this;
}
public MqttApplicationMessageBuilder WithAtLeastOnceQoS() public MqttApplicationMessageBuilder WithAtLeastOnceQoS()
{ {
@@ -139,8 +224,12 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the user property to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="name">The property name.</param>
/// <param name="value">The property value.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithUserProperty(string name, string value) public MqttApplicationMessageBuilder WithUserProperty(string name, string value)
{ {
if (_userProperties == null) if (_userProperties == null)
@@ -153,8 +242,10 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the content type to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithContentType(string contentType) public MqttApplicationMessageBuilder WithContentType(string contentType)
{ {
_contentType = contentType; _contentType = contentType;
@@ -162,8 +253,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the response topic to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="responseTopic">The response topic.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic) public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic)
{ {
_responseTopic = responseTopic; _responseTopic = responseTopic;
@@ -171,8 +265,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the correlation data to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="correlationData">The correlation data.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData) public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData)
{ {
_correlationData = correlationData; _correlationData = correlationData;
@@ -180,8 +277,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the topic alias to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="topicAlias">The topic alias.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias) public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias)
{ {
_topicAlias = topicAlias; _topicAlias = topicAlias;
@@ -189,8 +289,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the subscription identifier to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="subscriptionIdentifier">The subscription identifier.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier) public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier)
{ {
if (_subscriptionIdentifiers == null) if (_subscriptionIdentifiers == null)
@@ -203,8 +306,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the message expiry interval to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="messageExpiryInterval">The message expiry interval.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval) public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval)
{ {
_messageExpiryInterval = messageExpiryInterval; _messageExpiryInterval = messageExpiryInterval;
@@ -212,8 +318,11 @@ namespace MQTTnet
} }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Adds the payload format indicator to the message.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
/// <param name="payloadFormatIndicator">The payload format indicator.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator) public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator)
{ {
_payloadFormatIndicator = payloadFormatIndicator; _payloadFormatIndicator = payloadFormatIndicator;
@@ -232,12 +341,18 @@ namespace MQTTnet
throw new MqttProtocolViolationException("A Topic Alias of 0 is not permitted. A sender MUST NOT send a PUBLISH packet containing a Topic Alias which has the value 0 [MQTT-3.3.2-8]."); throw new MqttProtocolViolationException("A Topic Alias of 0 is not permitted. A sender MUST NOT send a PUBLISH packet containing a Topic Alias which has the value 0 [MQTT-3.3.2-8].");
} }
if (_qualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce && _dup)
{
throw new MqttProtocolViolationException("The DUP flag MUST be set to 0 for all QoS 0 messages [MQTT-3.3.1-2].");
}
var applicationMessage = new MqttApplicationMessage var applicationMessage = new MqttApplicationMessage
{ {
Topic = _topic, Topic = _topic,
Payload = _payload, Payload = _payload,
QualityOfServiceLevel = _qualityOfServiceLevel, QualityOfServiceLevel = _qualityOfServiceLevel,
Retain = _retain, Retain = _retain,
Dup = _dup,
ContentType = _contentType, ContentType = _contentType,
ResponseTopic = _responseTopic, ResponseTopic = _responseTopic,
CorrelationData = _correlationData, CorrelationData = _correlationData,
@@ -11,12 +11,26 @@ namespace MQTTnet
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
public MqttApplicationMessage ApplicationMessage { get; } public MqttApplicationMessage ApplicationMessage { get; }
public bool ProcessingFailed { get; set; } public bool ProcessingFailed { get; set; }
public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success;
/// <summary>
/// Gets or sets whether this message was handled.
/// This value can be used in user code for custom control flow.
/// </summary>
public bool IsHandled { get; set; }
public object Tag { get; set; }
public Task<bool> PendingTask { get; set; } public Task<bool> PendingTask { get; set; }
} }
} }
@@ -0,0 +1,16 @@
namespace MQTTnet
{
public enum MqttApplicationMessageReceivedReasonCode
{
Success = 0,
NoMatchingSubscribers = 16,
UnspecifiedError = 128,
ImplementationSpecificError = 131,
NotAuthorized = 135,
TopicNameInvalid = 144,
PacketIdentifierInUse = 145,
PacketIdentifierNotFound = 146,
QuotaExceeded = 151,
PayloadFormatInvalid = 153
}
}
+19 -3
View File
@@ -10,22 +10,38 @@ namespace MQTTnet
public class MqttTopicFilter public class MqttTopicFilter
{ {
/// <summary>
/// Gets or sets the MQTT topic.
/// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client.
/// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary>
public string Topic { get; set; } public string Topic { get; set; }
/// <summary>
/// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Gets or sets a value indicating whether to not send messages originating on this client (noLocal) or not.
/// </summary> /// </summary>
/// Hint: MQTT 5 feature only.
public bool? NoLocal { get; set; } public bool? NoLocal { get; set; }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Gets or sets a value indicating whether messages are retained as published or not.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
public bool? RetainAsPublished { get; set; } public bool? RetainAsPublished { get; set; }
/// <summary> /// <summary>
/// This is only supported when using MQTTv5. /// Gets or sets the retain handling.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
public MqttRetainHandling? RetainHandling { get; set; } public MqttRetainHandling? RetainHandling { get; set; }
+43 -1
View File
@@ -11,8 +11,25 @@ namespace MQTTnet
public class MqttTopicFilterBuilder public class MqttTopicFilterBuilder
{ {
/// <summary>
/// The quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
/// <summary>
/// The MQTT topic.
/// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client.
/// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary>
string _topic; string _topic;
bool? _noLocal;
bool? _retainAsPublished;
MqttRetainHandling? _retainHandling;
public MqttTopicFilterBuilder WithTopic(string topic) public MqttTopicFilterBuilder WithTopic(string topic)
{ {
@@ -44,6 +61,24 @@ namespace MQTTnet
return this; return this;
} }
public MqttTopicFilterBuilder WithNoLocal(bool? value = true)
{
_noLocal = value;
return this;
}
public MqttTopicFilterBuilder WithRetainAsPublished(bool? value = true)
{
_retainAsPublished = value;
return this;
}
public MqttTopicFilterBuilder WithRetainHandling(MqttRetainHandling? value)
{
_retainHandling = value;
return this;
}
public MqttTopicFilter Build() public MqttTopicFilter Build()
{ {
if (string.IsNullOrEmpty(_topic)) if (string.IsNullOrEmpty(_topic))
@@ -51,7 +86,14 @@ namespace MQTTnet
throw new MqttProtocolViolationException("Topic is not set."); throw new MqttProtocolViolationException("Topic is not set.");
} }
return new MqttTopicFilter { Topic = _topic, QualityOfServiceLevel = _qualityOfServiceLevel }; return new MqttTopicFilter
{
Topic = _topic,
QualityOfServiceLevel = _qualityOfServiceLevel,
NoLocal = _noLocal,
RetainAsPublished = _retainAsPublished,
RetainHandling = _retainHandling
};
} }
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
public sealed class MqttPingReqPacket : MqttBasePacket public sealed class MqttPingReqPacket : MqttBasePacket
{ {
// This is a minor performance improvement. // This is a minor performance improvement.
public static MqttPingReqPacket Instance = new MqttPingReqPacket(); public static readonly MqttPingReqPacket Instance = new MqttPingReqPacket();
public override string ToString() public override string ToString()
{ {
+1 -1
View File
@@ -3,7 +3,7 @@
public sealed class MqttPingRespPacket : MqttBasePacket public sealed class MqttPingRespPacket : MqttBasePacket
{ {
// This is a minor performance improvement. // This is a minor performance improvement.
public static MqttPingRespPacket Instance = new MqttPingRespPacket(); public static readonly MqttPingRespPacket Instance = new MqttPingRespPacket();
public override string ToString() public override string ToString()
{ {
@@ -8,6 +8,14 @@ namespace MQTTnet.Server
public bool IsSubscribed { get; set; } public bool IsSubscribed { get; set; }
/// <summary>
/// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
} }
} }
@@ -4,6 +4,10 @@ namespace MQTTnet.Server
{ {
public interface IMqttClientSession public interface IMqttClientSession
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; } string ClientId { get; }
Task StopAsync(); Task StopAsync();
@@ -4,6 +4,10 @@ namespace MQTTnet.Server
{ {
public interface IMqttServerOptions public interface IMqttServerOptions
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; set; } string ClientId { get; set; }
bool EnablePersistentSessions { get; } bool EnablePersistentSessions { get; }
@@ -5,14 +5,26 @@ namespace MQTTnet.Server
{ {
public interface IMqttServerPersistedSession public interface IMqttServerPersistedSession
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; } string ClientId { get; }
IDictionary<object, object> Items { get; } IDictionary<object, object> Items { get; }
IList<MqttTopicFilter> Subscriptions { get; } IList<MqttTopicFilter> Subscriptions { get; }
/// <summary>
/// Gets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
MqttApplicationMessage WillMessage { get; } MqttApplicationMessage WillMessage { get; }
/// <summary>
/// Gets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
uint? WillDelayInterval { get; } uint? WillDelayInterval { get; }
DateTime? SessionExpiryTimestamp { get; } DateTime? SessionExpiryTimestamp { get; }
@@ -18,6 +18,10 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public IMqttNetScopedLogger Logger { get; } public IMqttNetScopedLogger Logger { get; }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
public MqttApplicationMessage ApplicationMessage { get; set; } public MqttApplicationMessage ApplicationMessage { get; set; }
+22 -21
View File
@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client.Disconnecting;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
@@ -42,7 +43,7 @@ namespace MQTTnet.Server
long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere. long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere.
long _receivedApplicationMessagesCount; long _receivedApplicationMessagesCount;
long _sentApplicationMessagesCount; long _sentApplicationMessagesCount;
MqttDisconnectReasonCode _disconnectReason; MqttClientDisconnectReason _disconnectReason;
public MqttClientConnection(MqttConnectPacket connectPacket, public MqttClientConnection(MqttConnectPacket connectPacket,
IMqttChannelAdapter channelAdapter, IMqttChannelAdapter channelAdapter,
@@ -84,12 +85,12 @@ namespace MQTTnet.Server
public MqttClientSession Session { get; } public MqttClientSession Session { get; }
public async Task StopAsync(MqttDisconnectReasonCode reason) public async Task StopAsync(MqttClientDisconnectReason reason)
{ {
Status = MqttClientConnectionStatus.Finalizing; Status = MqttClientConnectionStatus.Finalizing;
_disconnectReason = reason; _disconnectReason = reason;
if (reason == MqttDisconnectReasonCode.SessionTakenOver || reason == MqttDisconnectReasonCode.KeepAliveTimeout) if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout)
{ {
// Is is very important to send the DISCONNECT packet here BEFORE cancelling the // Is is very important to send the DISCONNECT packet here BEFORE cancelling the
// token because the entire connection is closed (disposed) as soon as the cancellation // token because the entire connection is closed (disposed) as soon as the cancellation
@@ -97,10 +98,18 @@ namespace MQTTnet.Server
// at the client! // at the client!
try try
{ {
await _channelAdapter.SendPacketAsync(new MqttDisconnectPacket var disconnectOptions = new MqttClientDisconnectOptions
{ {
ReasonCode = reason ReasonCode = reason,
}, CancellationToken.None).ConfigureAwait(false); ReasonString = reason.ToString()
};
var disconnectPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(disconnectOptions);
using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
{
await _channelAdapter.SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -235,7 +244,7 @@ namespace MQTTnet.Server
} }
finally finally
{ {
if (_disconnectReason == MqttDisconnectReasonCode.SessionTakenOver) if (_disconnectReason == MqttClientDisconnectReason.SessionTakenOver)
{ {
disconnectType = MqttClientDisconnectType.Takeover; disconnectType = MqttClientDisconnectType.Takeover;
} }
@@ -277,12 +286,7 @@ namespace MQTTnet.Server
Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{ {
var pubCompPacket = new MqttPubCompPacket var pubCompPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
};
return SendAsync(pubCompPacket, cancellationToken); return SendAsync(pubCompPacket, cancellationToken);
} }
@@ -327,12 +331,12 @@ namespace MQTTnet.Server
} }
case MqttQualityOfServiceLevel.AtLeastOnce: case MqttQualityOfServiceLevel.AtLeastOnce:
{ {
var pubAckPacket = _dataConverter.CreatePubAckPacket(publishPacket); var pubAckPacket = _dataConverter.CreatePubAckPacket(publishPacket, MqttApplicationMessageReceivedReasonCode.Success);
return SendAsync(pubAckPacket, cancellationToken); return SendAsync(pubAckPacket, cancellationToken);
} }
case MqttQualityOfServiceLevel.ExactlyOnce: case MqttQualityOfServiceLevel.ExactlyOnce:
{ {
var pubRecPacket = _dataConverter.CreatePubRecPacket(publishPacket); var pubRecPacket = _dataConverter.CreatePubRecPacket(publishPacket, MqttApplicationMessageReceivedReasonCode.Success);
return SendAsync(pubRecPacket, cancellationToken); return SendAsync(pubRecPacket, cancellationToken);
} }
default: default:
@@ -441,13 +445,10 @@ namespace MQTTnet.Server
using (var awaiter2 = _packetDispatcher.AddAwaiter<MqttPubCompPacket>(publishPacket.PacketIdentifier)) using (var awaiter2 = _packetDispatcher.AddAwaiter<MqttPubCompPacket>(publishPacket.PacketIdentifier))
{ {
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); var pubRecPacket = await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false);
await SendAsync(new MqttPubRelPacket var pubRelPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
{ await SendAsync(pubRelPacket, cancellationToken).ConfigureAwait(false);
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
}, cancellationToken).ConfigureAwait(false);
await awaiter2.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); await awaiter2.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false);
} }
@@ -20,6 +20,14 @@ namespace MQTTnet.Server
public bool AcceptEnqueue { get; set; } = true; public bool AcceptEnqueue { get; set; } = true;
/// <summary>
/// Gets or sets the supscription quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; }
} }
} }
@@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client.Disconnecting;
using OperationCanceledException = System.OperationCanceledException; using OperationCanceledException = System.OperationCanceledException;
namespace MQTTnet.Server namespace MQTTnet.Server
@@ -121,7 +122,7 @@ namespace MQTTnet.Server
foreach (var connection in connections) foreach (var connection in connections)
{ {
await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
} }
} }
@@ -211,7 +212,7 @@ namespace MQTTnet.Server
if (connection != null) if (connection != null)
{ {
await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
} }
_logger.Verbose("Session for client '{0}' deleted.", clientId); _logger.Verbose("Session for client '{0}' deleted.", clientId);
@@ -301,7 +302,7 @@ namespace MQTTnet.Server
{ {
if (clientConnection != null) if (clientConnection != null)
{ {
await clientConnection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
} }
} }
@@ -424,7 +425,7 @@ namespace MQTTnet.Server
_connections[connectPacket.ClientId] = connection; _connections[connectPacket.ClientId] = connection;
} }
existingConnection?.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).GetAwaiter().GetResult(); existingConnection?.StopAsync(MqttClientDisconnectReason.SessionTakenOver).GetAwaiter().GetResult();
return connection; return connection;
} }
@@ -21,6 +21,10 @@ namespace MQTTnet.Server
SessionItems = sessionItems; SessionItems = sessionItems;
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId => _connectPacket.ClientId; public string ClientId => _connectPacket.ClientId;
public string Endpoint => _clientAdapter.Endpoint; public string Endpoint => _clientAdapter.Endpoint;
@@ -37,30 +41,88 @@ namespace MQTTnet.Server
public string Password => Encoding.UTF8.GetString(RawPassword ?? new byte[0]); public string Password => Encoding.UTF8.GetString(RawPassword ?? new byte[0]);
/// <summary>
/// Gets or sets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
public MqttApplicationMessage WillMessage => _connectPacket?.WillMessage; public MqttApplicationMessage WillMessage => _connectPacket?.WillMessage;
/// <summary>
/// Gets or sets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
public bool? CleanSession => _connectPacket?.CleanSession; public bool? CleanSession => _connectPacket?.CleanSession;
/// <summary>
/// Gets or sets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
public ushort? KeepAlivePeriod => _connectPacket?.KeepAlivePeriod; public ushort? KeepAlivePeriod => _connectPacket?.KeepAlivePeriod;
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties => _connectPacket?.Properties?.UserProperties; public List<MqttUserProperty> UserProperties => _connectPacket?.Properties?.UserProperties;
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData => _connectPacket?.Properties?.AuthenticationData; public byte[] AuthenticationData => _connectPacket?.Properties?.AuthenticationData;
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod => _connectPacket?.Properties?.AuthenticationMethod; public string AuthenticationMethod => _connectPacket?.Properties?.AuthenticationMethod;
public uint? MaximumPacketSize => _connectPacket?.Properties?.MaximumPacketSize; public uint? MaximumPacketSize => _connectPacket?.Properties?.MaximumPacketSize;
/// <summary>
/// Gets or sets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
public ushort? ReceiveMaximum => _connectPacket?.Properties?.ReceiveMaximum; public ushort? ReceiveMaximum => _connectPacket?.Properties?.ReceiveMaximum;
/// <summary>
/// Gets or sets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
public ushort? TopicAliasMaximum => _connectPacket?.Properties?.TopicAliasMaximum; public ushort? TopicAliasMaximum => _connectPacket?.Properties?.TopicAliasMaximum;
/// <summary>
/// Gets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestProblemInformation => _connectPacket?.Properties?.RequestProblemInformation; public bool? RequestProblemInformation => _connectPacket?.Properties?.RequestProblemInformation;
/// <summary>
/// Gets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestResponseInformation => _connectPacket?.Properties?.RequestResponseInformation; public bool? RequestResponseInformation => _connectPacket?.Properties?.RequestResponseInformation;
/// <summary>
/// Gets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// </summary>
public uint? SessionExpiryInterval => _connectPacket?.Properties?.SessionExpiryInterval; public uint? SessionExpiryInterval => _connectPacket?.Properties?.SessionExpiryInterval;
/// <summary>
/// Gets or sets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
public uint? WillDelayInterval => _connectPacket?.Properties?.WillDelayInterval; public uint? WillDelayInterval => _connectPacket?.Properties?.WillDelayInterval;
/// <summary> /// <summary>
@@ -79,14 +141,26 @@ namespace MQTTnet.Server
} }
/// <summary> /// <summary>
/// This is used for MQTTv5 only. When a MQTTv3 client connects the enum value must be one which is /// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be /// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
/// converted properly. /// converted properly.
/// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success; public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
/// <summary>
/// Gets or sets the response user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> ResponseUserProperties { get; set; } public List<MqttUserProperty> ResponseUserProperties { get; set; }
/// <summary>
/// Gets or sets the response authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] ResponseAuthenticationData { get; set; } public byte[] ResponseAuthenticationData { get; set; }
public string AssignedClientIdentifier { get; set; } public string AssignedClientIdentifier { get; set; }
@@ -11,6 +11,14 @@ namespace MQTTnet.Server
public bool IsRetainedMessage { get; set; } public bool IsRetainedMessage { get; set; }
/// <summary>
/// Gets or sets the subscription quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; }
[Obsolete("Use 'SubscriptionQualityOfServiceLevel' instead.")] [Obsolete("Use 'SubscriptionQualityOfServiceLevel' instead.")]
@@ -9,6 +9,10 @@ namespace MQTTnet.Server
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
} }
} }
@@ -9,7 +9,11 @@ namespace MQTTnet.Server
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
DisconnectType = disconnectType; DisconnectType = disconnectType;
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
public MqttClientDisconnectType DisconnectType { get; } public MqttClientDisconnectType DisconnectType { get; }
@@ -10,8 +10,16 @@ namespace MQTTnet.Server
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public MqttTopicFilter TopicFilter { get; } public MqttTopicFilter TopicFilter { get; }
} }
} }
@@ -10,8 +10,16 @@ namespace MQTTnet.Server
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public string TopicFilter { get; } public string TopicFilter { get; }
} }
} }
@@ -3,8 +3,8 @@ using MQTTnet.Internal;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Implementations; using MQTTnet.Implementations;
using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
@@ -108,7 +108,7 @@ namespace MQTTnet.Server
// We do not need to wait for the task so no await is needed. // We do not need to wait for the task so no await is needed.
// Also the internal state of the connection must be swapped to "Finalizing" because the // Also the internal state of the connection must be swapped to "Finalizing" because the
// next iteration of the keep alive timer happens. // next iteration of the keep alive timer happens.
var _ = connection.StopAsync(MqttDisconnectReasonCode.KeepAliveTimeout); var _ = connection.StopAsync(MqttClientDisconnectReason.KeepAliveTimeout);
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -8,6 +8,10 @@ namespace MQTTnet.Server
public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsTcpEndpointOptions(); public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsTcpEndpointOptions();
/// <summary>
/// Gets or sets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; set; } public string ClientId { get; set; }
public bool EnablePersistentSessions { get; set; } public bool EnablePersistentSessions { get; set; }
@@ -11,8 +11,16 @@ namespace MQTTnet.Server
SessionItems = sessionItems; SessionItems = sessionItems;
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public MqttTopicFilter TopicFilter { get; set; } public MqttTopicFilter TopicFilter { get; set; }
/// <summary> /// <summary>
@@ -31,6 +31,13 @@ namespace MQTTnet.Server
{ {
return true; return true;
} }
// Check for e.g. foo/ matching foo/#
if (sPos == sLen - 2
&& filter[sPos] == LevelSeparator
&& filter[sPos + 1] == MultiLevelWildcard)
{
return true;
}
} }
sPos++; sPos++;
@@ -11,8 +11,17 @@ namespace MQTTnet.Server
SessionItems = sessionItems; SessionItems = sessionItems;
} }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets or sets the MQTT topic.
/// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client.
/// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary>
public string Topic { get; set; } public string Topic { get; set; }
/// <summary> /// <summary>
@@ -6,6 +6,10 @@ namespace MQTTnet.Server.Status
{ {
public interface IMqttClientStatus public interface IMqttClientStatus
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; } string ClientId { get; }
string Endpoint { get; } string Endpoint { get; }
@@ -5,6 +5,10 @@ namespace MQTTnet.Server.Status
{ {
public interface IMqttSessionStatus public interface IMqttSessionStatus
{ {
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; } string ClientId { get; }
long PendingApplicationMessagesCount { get; } long PendingApplicationMessagesCount { get; }
@@ -1,7 +1,7 @@
using MQTTnet.Formatter; using MQTTnet.Formatter;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Protocol; using MQTTnet.Client.Disconnecting;
namespace MQTTnet.Server.Status namespace MQTTnet.Server.Status
{ {
@@ -14,6 +14,10 @@ namespace MQTTnet.Server.Status
_connection = connection ?? throw new ArgumentNullException(nameof(connection)); _connection = connection ?? throw new ArgumentNullException(nameof(connection));
} }
/// <summary>
/// Gets or sets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; set; } public string ClientId { get; set; }
public string Endpoint { get; set; } public string Endpoint { get; set; }
@@ -42,7 +46,7 @@ namespace MQTTnet.Server.Status
public Task DisconnectAsync() public Task DisconnectAsync()
{ {
return _connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection); return _connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection);
} }
public void ResetStatistics() public void ResetStatistics()
@@ -15,6 +15,10 @@ namespace MQTTnet.Server.Status
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
} }
/// <summary>
/// Gets or sets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; set; } public string ClientId { get; set; }
public long PendingApplicationMessagesCount { get; set; } public long PendingApplicationMessagesCount { get; set; }
@@ -43,7 +43,7 @@ namespace MQTTnet.Benchmarks
var channel = new TestMqttChannel(_stream); var channel = new TestMqttChannel(_stream);
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetLogger()); _channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetLogger());
} }
[Benchmark] [Benchmark]
@@ -9,6 +9,7 @@ using MQTTnet.Channel;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Formatter.V3; using MQTTnet.Formatter.V3;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks
{ {
@@ -48,19 +49,13 @@ namespace MQTTnet.Benchmarks
{ {
var channel = new BenchmarkMqttChannel(_serializedPacket); var channel = new BenchmarkMqttChannel(_serializedPacket);
var fixedHeader = new byte[2]; var fixedHeader = new byte[2];
var reader = new MqttPacketReader(channel); var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, new MqttNetLogger());
for (var i = 0; i < 10000; i++) for (var i = 0; i < 10000; i++)
{ {
channel.Reset(); channel.Reset();
var header = reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; var header = reader.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
var receivedPacket = new ReceivedMqttPacket(
header.Flags,
new MqttPacketBodyReader(_serializedPacket.Array, _serializedPacket.Count - header.RemainingLength, _serializedPacket.Array.Length), 0);
_serializer.Decode(receivedPacket);
} }
} }
@@ -1,23 +1,24 @@
using System.IO; //using System.IO;
using System.Threading; //using System.Threading;
using System.Threading.Tasks; //using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; //using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter; //using MQTTnet.Formatter;
using MQTTnet.Internal; //using MQTTnet.Internal;
namespace MQTTnet.Tests //namespace MQTTnet.Tests
{ //{
[TestClass] // [TestClass]
public class MqttPacketReader_Tests // public class MqttPacketReader_Tests
{ // {
[TestMethod] // [TestMethod]
public async Task MqttPacketReader_EmptyStream() // public async Task MqttPacketReader_EmptyStream()
{ // {
var fixedHeader = new byte[2]; // var fixedHeader = new byte[2];
var reader = new MqttPacketReader(new TestMqttChannel(new MemoryStream())); // var reader = new MqttPacketReader(new TestMqttChannel(new MemoryStream()));
var readResult = await reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None); // var readResult = await reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None);
Assert.IsTrue(readResult.ConnectionClosed); // Assert.IsTrue(readResult.ConnectionClosed);
} // }
} // }
} //}
// TODO: Fix
@@ -6,6 +6,7 @@ using System.Text;
using System.Threading; using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Formatter.V3; using MQTTnet.Formatter.V3;
@@ -22,7 +23,7 @@ namespace MQTTnet.Tests
[TestMethod] [TestMethod]
public void DetectVersionFromMqttConnectPacket() public void DetectVersionFromMqttConnectPacket()
{ {
var p = new MqttConnectPacket var packet = new MqttConnectPacket
{ {
ClientId = "XYZ", ClientId = "XYZ",
Password = Encoding.UTF8.GetBytes("PASS"), Password = Encoding.UTF8.GetBytes("PASS"),
@@ -30,17 +31,26 @@ namespace MQTTnet.Tests
KeepAlivePeriod = 123, KeepAlivePeriod = 123,
CleanSession = true CleanSession = true
}; };
var adapter = new MqttPacketFormatterAdapter(WriterFactory());
Assert.AreEqual(
Assert.AreEqual(MqttProtocolVersion.V310, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V310))); MqttProtocolVersion.V310,
Assert.AreEqual(MqttProtocolVersion.V311, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V311))); DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V310)));
Assert.AreEqual(MqttProtocolVersion.V500, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V500)));
Assert.AreEqual(
MqttProtocolVersion.V311,
DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V311)));
Assert.AreEqual(
MqttProtocolVersion.V500,
DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V500)));
var adapter = new MqttPacketFormatterAdapter(new MqttPacketWriter());
var ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[0]))); var ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[0])));
Assert.AreEqual("CONNECT packet must have at least 7 bytes.", ex.Message); Assert.AreEqual("CONNECT packet must have at least 7 bytes.", ex.Message);
ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[7]))); ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[7])));
Assert.AreEqual("Protocol '' not supported.", ex.Message); Assert.AreEqual("Protocol '' not supported.", ex.Message);
ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[] { 255, 255, 0,0,0,0,0 }))); ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[] { 255, 255, 0, 0, 0, 0, 0 })));
Assert.AreEqual("Expected at least 65537 bytes but there are only 7 bytes", ex.Message); Assert.AreEqual("Expected at least 65537 bytes but there are only 7 bytes", ex.Message);
} }
@@ -205,24 +215,28 @@ namespace MQTTnet.Tests
Payload = payload Payload = payload
}; };
var buffer = serializer.Encode(publishPacket);
var testChannel = new TestMqttChannel(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count));
var header = new MqttPacketReader(testChannel).ReadFixedHeaderAsync( var publishPacketCopy = Roundtrip(publishPacket);
new byte[2],
CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
var eof = buffer.Offset + buffer.Count; //var buffer = serializer.Encode(publishPacket);
//var testChannel = new TestMqttChannel(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count));
var receivedPacket = new ReceivedMqttPacket(
header.Flags,
new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset),
0);
var packet = (MqttPublishPacket)serializer.Decode(receivedPacket); //var header = new MqttPacketReader(testChannel).ReadFixedHeaderAsync(
// new byte[2],
// CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
Assert.AreEqual(publishPacket.Topic, packet.Topic); //var eof = buffer.Offset + buffer.Count;
Assert.IsTrue(publishPacket.Payload.SequenceEqual(packet.Payload));
//var receivedPacket = new ReceivedMqttPacket(
// header.Flags,
// new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset),
// 0);
//var packet = (MqttPublishPacket)serializer.Decode(receivedPacket);
Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic);
Assert.IsTrue(publishPacket.Payload.SequenceEqual(publishPacketCopy.Payload));
} }
[TestMethod] [TestMethod]
@@ -262,7 +276,7 @@ namespace MQTTnet.Tests
[TestMethod] [TestMethod]
public void SerializeV500_MqttPublishPacket() public void SerializeV500_MqttPublishPacket()
{ {
var prop = new MqttPublishPacketProperties {UserProperties = new List<MqttUserProperty>()}; var prop = new MqttPublishPacketProperties { UserProperties = new List<MqttUserProperty>() };
prop.ResponseTopic = "/Response"; prop.ResponseTopic = "/Response";
@@ -581,15 +595,14 @@ namespace MQTTnet.Tests
DeserializeAndCompare(p, "sAIAew=="); DeserializeAndCompare(p, "sAIAew==");
} }
private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
{ {
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Serialize(packet, protocolVersion))); Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Serialize(packet, protocolVersion)));
} }
private byte[] Serialize(MqttBasePacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) byte[] Serialize(MqttBasePacket packet, MqttProtocolVersion protocolVersion)
{ {
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, WriterFactory()); return MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, WriterFactory()).Encode(packet).ToArray();
return Join(serializer.Encode(packet));
} }
protected virtual IMqttPacketWriter WriterFactory() protected virtual IMqttPacketWriter WriterFactory()
@@ -602,83 +615,92 @@ namespace MQTTnet.Tests
return new MqttPacketBodyReader(data, 0, data.Length); return new MqttPacketBodyReader(data, 0, data.Length);
} }
private void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
{ {
var writer = WriterFactory(); var writer = WriterFactory();
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer); var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer);
var buffer1 = serializer.Encode(packet); var buffer1 = serializer.Encode(packet);
using (var headerStream = new MemoryStream(Join(buffer1))) using (var headerStream = new MemoryStream(buffer1.ToArray()))
{ {
var channel = new TestMqttChannel(headerStream); var channel = new TestMqttChannel(headerStream);
var fixedHeader = new byte[2]; var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, writer), null, new MqttNetLogger());
var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength)) var buffer2 = serializer.Encode(receivedPacket);
{
var reader = ReaderFactory(bodyStream.ToArray());
var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0));
var buffer2 = serializer.Encode(deserializedPacket);
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2))); Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(buffer2.ToArray()));
}
//adapter.ReceivePacketAsync(CancellationToken.None);
//var fixedHeader = new byte[2];
//var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
//using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength))
//{
// var reader = ReaderFactory(bodyStream.ToArray());
// var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0));
// var buffer2 = serializer.Encode(deserializedPacket);
// Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2)));
//}
} }
} }
private T Roundtrip<T>(T packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
where T : MqttBasePacket where TPacket : MqttBasePacket
{ {
var writer = WriterFactory(); var writer = WriterFactory();
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer); var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer);
var buffer = serializer.Encode(packet);
var buffer1 = serializer.Encode(packet); var channel = new TestMqttChannel(buffer.ToArray());
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, writer), null, new MqttNetLogger());
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
using (var headerStream = new MemoryStream(Join(buffer1))) //using (var headerStream = new MemoryStream(buffer1.ToArray()))
{ //{
var channel = new TestMqttChannel(headerStream);
var fixedHeader = new byte[2];
var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength))
{
var reader = ReaderFactory(bodyStream.ToArray()); // //var fixedHeader = new byte[2];
return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0));
} // //var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
}
// //using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength))
// //{
// // var reader = ReaderFactory(bodyStream.ToArray());
// // return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0));
// //}
//}
} }
private MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter adapter, byte[] buffer) MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
{ {
using (var headerStream = new MemoryStream(buffer)) var channel = new TestMqttChannel(buffer);
{ var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetLogger());
var channel = new TestMqttChannel(headerStream);
var fixedHeader = new byte[2];
var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
return packetFormatterAdapter.ProtocolVersion;
using (var bodyStream = new MemoryStream(buffer, (int)headerStream.Position, (int)header.RemainingLength)) //using (var headerStream = new MemoryStream(buffer))
{ //{
var reader = ReaderFactory(bodyStream.ToArray());
var packet = new ReceivedMqttPacket(header.Flags, reader, 0);
adapter.DetectProtocolVersion(packet);
return adapter.ProtocolVersion;
}
}
}
private static byte[] Join(params ArraySegment<byte>[] chunks)
{
var buffer = new MemoryStream();
foreach (var chunk in chunks)
{
buffer.Write(chunk.Array, chunk.Offset, chunk.Count);
}
return buffer.ToArray();
// //var fixedHeader = new byte[2];
// //var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
// //using (var bodyStream = new MemoryStream(buffer, (int)headerStream.Position, (int)header.RemainingLength))
// //{
// // var reader = ReaderFactory(bodyStream.ToArray());
// // var packet = new ReceivedMqttPacket(header.Flags, reader, 0);
// // packetFormatterAdapter.DetectProtocolVersion(packet);
// // return adapter.ProtocolVersion;
// //}
//}
} }
} }
} }
+53 -3
View File
@@ -1,4 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Client.Connecting; using MQTTnet.Client.Connecting;
@@ -6,6 +6,7 @@ using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options; using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving; using MQTTnet.Client.Receiving;
using MQTTnet.Client.Subscribing; using MQTTnet.Client.Subscribing;
using MQTTnet.Exceptions;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
@@ -25,6 +26,55 @@ namespace MQTTnet.Tests
{ {
public TestContext TestContext { get; set; } public TestContext TestContext { get; set; }
[TestMethod]
[DataRow("", null)]
[DataRow("", "")]
[DataRow(null, null)]
public async Task Use_Admissible_Credentials(string username, string password)
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials(username, password)
.Build();
var connectResult = await client.ConnectAsync(clientOptions);
Assert.IsFalse(connectResult.IsSessionPresent);
Assert.IsTrue(client.IsConnected);
}
}
[TestMethod]
public async Task Use_Username_Null_Password_Empty()
{
string username = null;
string password = string.Empty;
using (var testEnvironment = new TestEnvironment())
{
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServerAsync();
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials(username, password)
.Build();
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
}
}
[TestMethod] [TestMethod]
public async Task Use_Empty_Client_ID() public async Task Use_Empty_Client_ID()
{ {
@@ -416,7 +466,7 @@ namespace MQTTnet.Tests
public async Task Publish_Multiple_Clients() public async Task Publish_Multiple_Clients()
{ {
var receivedMessagesCount = 0; var receivedMessagesCount = 0;
using (var testEnvironment = new TestEnvironment(TestContext)) using (var testEnvironment = new TestEnvironment(TestContext))
{ {
await testEnvironment.StartServerAsync(); await testEnvironment.StartServerAsync();
@@ -446,7 +496,7 @@ namespace MQTTnet.Tests
} }
SpinWait.SpinUntil(() => receivedMessagesCount == 1000, TimeSpan.FromSeconds(20)); SpinWait.SpinUntil(() => receivedMessagesCount == 1000, TimeSpan.FromSeconds(20));
Assert.AreEqual(1000, receivedMessagesCount); Assert.AreEqual(1000, receivedMessagesCount);
} }
} }
@@ -6,6 +6,20 @@ namespace MQTTnet.Tests
[TestClass] [TestClass]
public class TopicFilterComparer_Tests public class TopicFilterComparer_Tests
{ {
[TestMethod]
public void TopicFilterComparer_Plus_Match_With_Separator_Only()
{
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
CompareAndAssert("A", "+", true);
}
[TestMethod]
public void TopicFilterComparer_Hash_Match_With_Separator_Only()
{
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
CompareAndAssert("/", "#", true);
}
[TestMethod] [TestMethod]
public void TopicFilterComparer_DirectMatch() public void TopicFilterComparer_DirectMatch()
{ {
@@ -54,6 +68,12 @@ namespace MQTTnet.Tests
CompareAndAssert("A/B/C/D", "A/C/#", false); CompareAndAssert("A/B/C/D", "A/C/#", false);
} }
[TestMethod]
public void TopicFilterComparer_EndMultipleLevelsWildcardMatchEmptyLevel()
{
CompareAndAssert("A/", "A/#", true);
}
[TestMethod] [TestMethod]
public void TopicFilterComparer_AllLevelsWildcardMatch() public void TopicFilterComparer_AllLevelsWildcardMatch()
{ {
@@ -1,6 +0,0 @@
<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"
RequestedTheme="Light">
</Application>
@@ -1,51 +0,0 @@
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace MQTTnet.TestApp.UniversalWindows
{
sealed partial class App
{
public App()
{
InitializeComponent();
Suspending += OnSuspending;
}
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
if (!(Window.Current.Content is Frame rootFrame))
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
Window.Current.Activate();
}
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
deferral.Complete();
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More