Compare commits

...
80 Commits
Author SHA1 Message Date
Christian ed5e6ee897 Update project files. 2021-06-28 20:39:10 +02:00
Christian 2e019f372c Update docs. 2021-06-27 17:02:04 +02:00
Christian cc9bc2d3a0 Merge remote-tracking branch 'origin/master' 2021-06-27 16:53:54 +02:00
Christian 306bce5a58 Refactor code. 2021-06-27 16:53:44 +02:00
Ramon Smits 6fe7ce7a0c FIxed MqttNetLogLevel.Verbose incorrectly mapped to LogLevel.Debug (#1170)
Based on https://github.com/chkr1011/MQTTnet/issues/1138#issuecomment-846649937
2021-05-30 22:01:38 +02:00
Christian 8f1d4e3c22 Remove client disconnected handler from wrong implementation location. 2021-04-11 20:34:35 +02:00
Christian 77338527b4 Refactor code. 2021-04-05 12:50:42 +02:00
Christian 962622a9f7 Merge pull request #1134 from yyjdelete/issue996
Disallow to call MqttClient.ConnectAsync while Disconnect is pending.
2021-04-05 11:19:20 +02:00
SilverFox 899ed7b1da Rename MqttClient.ConnectState to MqttClientConnectionStatus 2021-04-03 13:19:50 +08:00
Christian 10f7d809f2 Merge pull request #1132 from qzwzqty/master
Passes the Endpoint of the client before it disconnected
2021-04-02 16:18:15 +02:00
Christian 324fb7e48c Merge pull request #1136 from yyjdelete/framework
Fix UWP support broken in #1135
2021-04-02 16:17:13 +02:00
SilverFox d913d4402e Convert MqttClient._connectState to enum 2021-04-02 21:01:49 +08:00
SilverFox df262b5588 Fix UWP support broken in #1135 2021-04-02 19:58:03 +08:00
Christian 27daf37ba4 Update MQTTnet.nuspec 2021-04-02 12:49:31 +02:00
Christian e5e10eee6d Merge pull request #1135 from yyjdelete/framework
Add more checks for NET5_0
2021-04-02 12:48:43 +02:00
SilverFox e174546b5a Add more checks for NET5_0 2021-04-02 18:03:44 +08:00
SilverFox f99daee4d1 Fix Session_Tests.Manage_Session_MaxParallel 2021-04-02 17:54:41 +08:00
SilverFox 9b10eadb24 Fix broken tests 2021-04-02 17:54:41 +08:00
SilverFox c39da42ef2 Disallow to call MqttClient.ConnectAsync while Disconnect is pending.
Fix #996
Fix #1010
2021-04-01 22:00:01 +08:00
Wang Chunlong 85b339e6f0 rename 2021-03-31 11:56:58 +08:00
Christian fbb5cd45a0 Refactor deferred ACK of publish packets. 2021-03-30 21:06:44 +02:00
Christian d430ca0250 Merge pull request #1075 from tkurucsai/feature/pending-appmsg-acknowledge
Feature/pending appmsg acknowledge
2021-03-30 14:34:43 +02:00
Wang Chunlong cf8ea872d3 Disconnected Endpoint 2021-03-29 16:00:37 +08:00
Christian d4440f3be1 Merge remote-tracking branch 'origin/master' 2021-03-21 20:42:25 +01:00
Christian b97d01b78b Fix wrong data validation (#342). 2021-03-21 20:42:09 +01:00
Christian 375ee5c4ac Merge pull request #1121 from asymetrixs/master
Added workflow to  build debian/ubuntu packages
2021-03-21 20:29:02 +01:00
tamas.kurucsai 90b37a1e2d reimplemented delayed acknowledgement using a property to switch off automatic acknowledgement and a new method Acknowledge() in the event handler 2021-03-20 10:36:44 +01:00
Damian 0978f6cc75 Merge pull request #7 from asymetrixs/deb-packaging
Deb packaging
2021-03-12 18:04:25 +01:00
Damian Wolgast e7c51e8dbc Extraction of Version from project file. 2021-03-12 18:03:32 +01:00
Damian Wolgast aab4448980 Debugging 2021-03-12 18:01:31 +01:00
Damian Wolgast 52139c6a99 Debugging 2021-03-12 17:56:34 +01:00
Damian Wolgast f3d121120b Debugging 2021-03-12 17:51:18 +01:00
Damian Wolgast 413c68de44 Debugging 2021-03-12 17:47:53 +01:00
Damian Wolgast f0919487f2 Debugging 2021-03-12 17:44:26 +01:00
Damian ffa7c51899 Merge pull request #6 from asymetrixs/deb-packaging
Deb packaging
2021-03-12 17:38:07 +01:00
Damian Wolgast 493cad087f Fixed syntax 2021-03-12 17:36:41 +01:00
Damian Wolgast 4d94e1a318 Debugging 2021-03-12 17:32:03 +01:00
Damian 28ff08ceb9 Merge pull request #5 from asymetrixs/deb-packaging
Read version from MQTTnet.Server project file
2021-03-12 17:28:03 +01:00
Damian Wolgast 0f3d461c37 Read version from MQTTnet.Server project file 2021-03-12 17:27:18 +01:00
Damian 4285169108 Merge pull request #4 from asymetrixs/deb-packaging
Added dependency to libssl
2021-03-12 15:44:58 +01:00
Damian Wolgast 1b1f2db1e6 Added dependency to libssl 2021-03-12 15:44:22 +01:00
Damian d7248c0f64 Merge pull request #3 from asymetrixs/deb-packaging
Added check for availability of setcap
2021-03-12 15:20:08 +01:00
Damian Wolgast d2e1b12186 Added check for availability of setcap 2021-03-12 15:18:13 +01:00
Damian dafcfb47d2 Merge pull request #2 from asymetrixs/deb-packaging
Docker support
2021-03-12 14:41:38 +01:00
Damian Wolgast 897f0f36d1 Fixed spelling 2021-03-12 14:40:21 +01:00
Damian Wolgast 9cb12ac2ce Docker, check if systemctl exists before using it 2021-03-12 14:39:51 +01:00
Damian 312fb2bb3c Merge pull request #1 from asymetrixs/deb-packaging
Fixed user creation to not copy skel-files
2021-03-12 14:19:24 +01:00
Damian Wolgast 3ee785c781 Fixed user creation to not copy skel-files 2021-03-12 14:17:38 +01:00
Damian Wolgast 0ea4ae7501 Fix, forgot to rename file. 2021-03-12 12:40:02 +01:00
Damian Wolgast 7e764c7f0f Added revision (assuming 1) and architecture 2021-03-12 12:36:14 +01:00
Damian Wolgast 5c2879029b Added command to stop service before uninstalling 2021-03-12 12:26:04 +01:00
Damian Wolgast 6cbb8d42e3 Added prerm to revoke service permissions 2021-03-12 12:15:49 +01:00
Damian Wolgast d8d50f6dcb Added workflow to build debian/ubuntu packages 2021-03-11 18:19:25 +01:00
Christian a01dc9fef8 Add new overload for server options builder. 2021-03-05 13:01:14 +01:00
tamas.kurucsai 3427d82efc Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/pending-appmsg-acknowledge
# Conflicts:
#	Source/MQTTnet/Client/MqttClient.cs
2021-03-05 12:10:01 +01:00
Christian 5915285522 Fix TLS parameter options builder (#1104). 2021-03-02 10:00:34 +01:00
Christian 12f346115f Improve RPC client handling. 2021-02-26 10:55:26 +01:00
Christian 05827341d4 Update codeql-analysis.yml 2021-02-23 12:05:24 +01:00
Christian 1e88d6a05b Merge remote-tracking branch 'origin/master' 2021-02-23 11:46:38 +01:00
Christian 8261ba9dc1 Update docs. 2021-02-23 11:46:16 +01:00
Christian b766c1bebb Refactor keep alive message handling in order to fix issues with sudden disconnects. 2021-02-23 11:45:22 +01:00
Christian 6c188899fb Create codeql-analysis.yml 2021-02-21 11:33:51 +01:00
tamas.kurucsai 23c5270c3f Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/pending-appmsg-acknowledge
# Conflicts:
#	Source/MQTTnet/Client/MqttClient.cs
#	Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs
2021-02-20 12:55:28 +01:00
Christian 2d4ea8ee9b Improve nuget packages. 2021-02-19 21:52:42 +01:00
Christian 15d3761843 Merge pull request #1097 from yyjdelete/nuspec
Update nuspecs
2021-02-19 21:50:49 +01:00
Christian ed520455d0 Use the data converter only when creating packets. 2021-02-19 21:45:23 +01:00
Christian cfc4ebe6a3 Expose Dup flag in application messages (#1096). 2021-02-19 21:39:23 +01:00
Christian 120b7fb7d1 Fix RPC issue #1093 by calling both message handlers. 2021-02-18 18:56:15 +01:00
SilverFox 8b164a4846 Update nuspecs
Manually sync with the file generated by `msbuild /t:pack` or `dotnet pack`, and remove some unnessary dependencies
2021-02-18 11:26:54 +08:00
Christian fda4272611 Update docs. 2021-02-11 12:31:13 +01:00
Christian 88cc79d52f Fix issues in nuspecs. 2021-02-11 12:25:58 +01:00
Christian 8ba960343c Fix exception handling issues (#1082). 2021-02-08 13:29:15 +01:00
Christian 0976fa682a Merge pull request #1083 from milbrandt/bugfix/noUsername
Test for Regression: No exception on ConnectAsync(null, password) [MQTT-3.1.2-22]
2021-02-06 13:38:04 +01:00
Christian 18cc454842 Fix build script for .net5.0. 2021-02-06 13:36:23 +01:00
Volker Milbrandt e342a51d50 Merge branch 'master' 2021-02-04 21:23:02 +01:00
Volker Milbrandt 57652c76cf tests with MqttClient.ConnectAsync with credentials
for all four combinations of null/non-null for username and password
2021-02-04 21:09:36 +01:00
tamas.kurucsai fbf7d9bb5c corrected thread safety of ping timers after adding delayed acknowledgement 2021-01-29 07:03:56 +01:00
tamas.kurucsai 822f582e41 added test for delayed message acknowledgement 2021-01-29 07:03:19 +01:00
tamas.kurucsai fcbe2e32f6 corrected name of response sender method 2021-01-18 17:58:49 +01:00
tamas.kurucsai f955b4a3e6 implemented delayed message acknowledgement 2021-01-18 10:47:21 +01:00
63 changed files with 1058 additions and 541 deletions
+67
View File
@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '23 9 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+62
View File
@@ -0,0 +1,62 @@
# This is a basic workflow to help you get started with Actions
name: Deb Packaging
# Controls when the action will run.
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Parse version
run: echo VERSION=$(grep '<Version>' Source/MQTTnet.Server/MQTTnet.Server.csproj | grep [0-9\.]* -o) >> $GITHUB_ENV
- name: Build MQTTnetServer
run: dotnet publish Source/MQTTnet.Server/MQTTnet.Server.csproj --configuration Release --self-contained --runtime linux-x64 --framework net5.0
- name: Set up installation directory
run: mkdir -p packaging/opt/MQTTnetServer
- name: Copy meta data
run: |
cp -R Build/deb-meta/* packaging/
chmod 755 packaging/DEBIAN/postinst
chmod 755 packaging/DEBIAN/postrm
chmod 755 packaging/DEBIAN/prerm
- name: Move artifacts to packaging directory
run: cp -R Source/MQTTnet.Server/bin/Release/net5.0/linux-x64/publish/* packaging/opt/MQTTnetServer
- name: Adjust files
run: |
rm packaging/opt/MQTTnetServer/appsettings.Development.json
mv packaging/opt/MQTTnetServer/appsettings.json packaging/opt/MQTTnetServer/appsettings.template.json
- name: Adjust permissions
run: |
cd packaging/opt/MQTTnetServer
find . -type f | xargs chmod -R 644
chmod 755 MQTTnet.Server
- name: Generate MD5s
run: |
cd packaging/
md5sum $(find * -type f -not -path 'DEBIAN/*') > DEBIAN/md5sums
- name: Patch meta
run: sed -i 's/\VERSIONPLACEHOLDER/${{ env.VERSION }}/' packaging/DEBIAN/control
- name: Package everything
run: dpkg-deb -v --build packaging/ mqttnet-server_${{ env.VERSION }}-1_amd64.deb
- name: Save artifact
uses: actions/upload-artifact@v2
with:
name: mqttnet-server
path: mqttnet-server_${{ env.VERSION }}-1_amd64.deb
+2
View File
@@ -294,3 +294,5 @@ __pycache__/
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json /Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
Build/NuGet/ Build/NuGet/
Build/nuget.exe
+1 -1
View File
@@ -38,7 +38,7 @@
<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.AspNetCore\bin\Release\net5.0\MQTTnet.*" target="lib\net5.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\" />
@@ -27,7 +27,7 @@
<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.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.*" target="lib\net5.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\" />
@@ -35,6 +35,9 @@
<!-- .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\" />
@@ -42,7 +45,7 @@
<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>
+4 -1
View File
@@ -27,7 +27,7 @@
<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.Extensions.Rpc\bin\Release\net5.0\MQTTnet.*" target="lib\net5.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\" />
@@ -35,6 +35,9 @@
<!-- .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\" />
+6 -17
View File
@@ -15,22 +15,8 @@
<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>
@@ -42,7 +28,7 @@
<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.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.*" target="lib\net5.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\" />
@@ -50,6 +36,9 @@
<!-- .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\" />
+15 -37
View File
@@ -12,54 +12,32 @@
<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 _DISCONNECT_ packet reading for protocol version 3.1.0.DisconnectPacket
* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal). * [Client] Added support for deferred message approval (#1075, thanks to @tkurucsai).
* [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato). * [Client] Exposed missing APIs for .NET 5.0 build (thanks to @yyjdelete).
* [Core] Aligned the format of some log messages. * [Client] Improved disconnect handling (#1134, thanks to @yyjdelete).
* [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick). * [Client] Fix TLS parameter options builder (#1104).
* [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect. * [Server] Add new overload for options builder.
* [Client] Added new API for raw packet inspection. * [Server] Exposed endpoint in _MqttServerClientDisconnectedEventArgs_.
* [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!). * [Server] Remove client disconnected handler from wrong implementation location (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.
* [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed).
* [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil).
* [Core] Added code documentation.
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>
+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"
+10
View File
@@ -0,0 +1,10 @@
Package: mqttnet-server
Version: VERSIONPLACEHOLDER
Section: base
Priority: optional
Architecture: amd64
Depends: libssl1.1
Suggests:
Maintainer: Damian Wolgast <packages@asymetrixs.net>
Description:
MQTTnet Server is a standalone cross platform MQTT server (like mosquitto) basing on this library.
+26
View File
@@ -0,0 +1,26 @@
The source of MQTTnet is at:
https://github.com/chkr1011/MQTTnet
Project is maintained by Christian Kratky
MIT License
Copyright (c) 2021 Damian Wolgast
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
# Check if user exits
USER=mqttnetsrv
USEREXISTS=$(id -u $USER >/dev/null 2>&1; echo $?)
EC=0
BASEPATH=/opt
INSTALLPATH=$BASEPATH/MQTTnetServer
# If user does not exist, create it
if (( $USEREXISTS == 1 )); then
useradd -d $INSTALLPATH -r $USER
EC=$(echo $?)
if (( $EC == 1 )); then
exit 1;
fi
fi
# Set permissions on files
chown -R root:root $INSTALLPATH
# Check if config does not exist and create it
if [ ! -f "$INSTALLPATH/appsettings.json" ]; then
cp $INSTALLPATH/appsettings.template.json $INSTALLPATH/appsettings.json
fi
# Check if setcap exists, does not exist in docker containers
SCAP=$(which setcap >/dev/null; echo $?)
if [ "$SCAP" -eq "0" ]; then
# Enable MQTTServer.NET to bind to IP interface
setcap CAP_NET_BIND_SERVICE=+eip $INSTALLPATH/MQTTnet.Server
fi
chmod 644 /etc/systemd/system/mqttnet-server.service
# Check if systemctl exists, does not exist in docker containers
SCTL=$(which systemctl >/dev/null; echo $?)
if [ "$SCTL" -eq "0" ]; then
# Reload systemd because of new service file
systemctl daemon-reload
fi
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
# Check if systemctl exists, does not exist in docker containers
SCTL=$(which systemctl >/dev/null; echo $?)
if [ "$SCTL" -eq "0" ]; then
# Reload systemd because of new service file
systemctl daemon-reload
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
BASEPATH=/opt
INSTALLPATH=$BASEPATH/MQTTnetServer
# Check if systemctl exists, does not exist in docker containers
SCTL=$(which systemctl >/dev/null; echo $?)
if [ "$SCTL" -eq "0" ]; then
# Stop service before uninstalling
systemctl stop mqttnet-server
fi
# Check if setcap exists, does not exist in docker containers
SCAP=$(which setcap >/dev/null; echo $?)
if [ "$SCAP" -eq "0" ]; then
# Remove permission to open ports
setcap CAP_NET_BIND_SERVICE=-eip $INSTALLPATH/MQTTnet.Server
fi
@@ -0,0 +1,14 @@
[Unit]
Description=MQTTnetServer
After=network.target
[Service]
ExecStart=/opt/MQTTnetServer/MQTTnet.Server
WorkingDirectory=/opt/MQTTnetServer
StandardOutput=inherit
StandardError=inherit
Restart=always
User=mqttnetsrv
[Install]
WantedBy=multi-user.target
+1
View File
@@ -12,4 +12,5 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company /> <Company />
<Authors /> <Authors />
<PackageId /> <PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -7,12 +7,8 @@ namespace MQTTnet.Extensions.Rpc
{ {
public interface IMqttRpcClient : IDisposable public interface IMqttRpcClient : IDisposable
{ {
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel); Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken); Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
} }
} }
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company /> <Company />
<Authors /> <Authors />
<PackageId /> <PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+48 -50
View File
@@ -5,9 +5,10 @@ using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client.Subscribing;
using MQTTnet.Implementations;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc
{ {
@@ -35,22 +36,27 @@ namespace MQTTnet.Extensions.Rpc
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler; _mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
} }
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel) public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{ {
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, CancellationToken.None); using (var timeoutToken = new CancellationTokenSource(timeout))
{
try
{
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, timeoutToken.Token).ConfigureAwait(false);
}
catch (OperationCanceledException exception)
{
if (timeoutToken.IsCancellationRequested)
{
throw new MqttCommunicationTimedOutException(exception);
}
throw;
}
}
} }
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken) public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
{
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, cancellationToken);
}
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
return ExecuteAsync(timeout, methodName, payload, qualityOfServiceLevel, CancellationToken.None);
}
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
{ {
if (methodName == null) throw new ArgumentNullException(nameof(methodName)); if (methodName == null) throw new ArgumentNullException(nameof(methodName));
@@ -88,62 +94,54 @@ namespace MQTTnet.Extensions.Rpc
try try
{ {
var tcs = new TaskCompletionSource<byte[]>(); #if NET452
if (!_waitingCalls.TryAdd(responseTopic, tcs)) var promise = new TaskCompletionSource<byte[]>();
#else
var promise = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
#endif
if (!_waitingCalls.TryAdd(responseTopic, promise))
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false); var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false); .WithTopicFilter(responseTopic, qualityOfServiceLevel)
.Build();
using (var timeoutCts = new CancellationTokenSource(timeout)) await _mqttClient.SubscribeAsync(subscribeOptions, cancellationToken).ConfigureAwait(false);
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)) await _mqttClient.PublishAsync(requestMessage, cancellationToken).ConfigureAwait(false);
using (cancellationToken.Register(() => { promise.TrySetCanceled(); }))
{ {
linkedCts.Token.Register(() => return await promise.Task.ConfigureAwait(false);
{
if (!tcs.Task.IsCompleted && !tcs.Task.IsFaulted && !tcs.Task.IsCanceled)
{
tcs.TrySetCanceled();
}
});
try
{
var result = await tcs.Task.ConfigureAwait(false);
timeoutCts.Cancel(false);
return result;
}
catch (OperationCanceledException exception)
{
if (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
{
throw new MqttCommunicationTimedOutException(exception);
}
else
{
throw;
}
}
} }
} }
finally finally
{ {
_waitingCalls.TryRemove(responseTopic, out _); _waitingCalls.TryRemove(responseTopic, out _);
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false); await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
} }
} }
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); #if NET452
Task.Run(() => promise.TrySetResult(eventArgs.ApplicationMessage.Payload));
#else
promise.TrySetResult(eventArgs.ApplicationMessage.Payload);
#endif
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()
@@ -158,4 +156,4 @@ namespace MQTTnet.Extensions.Rpc
_waitingCalls.Clear(); _waitingCalls.Clear();
} }
} }
} }
@@ -0,0 +1,19 @@
using System;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc
{
public static class MqttRpcClientExtensions
{
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
var buffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel);
}
}
}
@@ -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);
} }
} }
} }
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company /> <Company />
<Authors /> <Authors />
<PackageId /> <PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -55,7 +55,7 @@ namespace MQTTnet.Server.Logging
case MqttNetLogLevel.Error: return LogLevel.Error; case MqttNetLogLevel.Error: return LogLevel.Error;
case MqttNetLogLevel.Warning: return LogLevel.Warning; case MqttNetLogLevel.Warning: return LogLevel.Warning;
case MqttNetLogLevel.Info: return LogLevel.Information; case MqttNetLogLevel.Info: return LogLevel.Information;
case MqttNetLogLevel.Verbose: return LogLevel.Debug; case MqttNetLogLevel.Verbose: return LogLevel.Trace;
} }
return LogLevel.Debug; return LogLevel.Debug;
@@ -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;
} }
} }
@@ -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,
+169 -147
View File
@@ -14,7 +14,6 @@ using MQTTnet.PacketDispatcher;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Implementations; using MQTTnet.Implementations;
@@ -25,8 +24,6 @@ namespace MQTTnet.Client
{ {
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider(); readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher(); readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
readonly Stopwatch _sendTracker = new Stopwatch();
readonly Stopwatch _receiveTracker = new Stopwatch();
readonly object _disconnectLock = new object(); readonly object _disconnectLock = new object();
readonly IMqttClientAdapterFactory _adapterFactory; readonly IMqttClientAdapterFactory _adapterFactory;
@@ -41,10 +38,12 @@ namespace MQTTnet.Client
IMqttChannelAdapter _adapter; IMqttChannelAdapter _adapter;
bool _cleanDisconnectInitiated; bool _cleanDisconnectInitiated;
long _isDisconnectPending; volatile int _connectionStatus;
bool _isConnected;
MqttClientDisconnectReason _disconnectReason; MqttClientDisconnectReason _disconnectReason;
DateTime _lastPacketSentTimestamp;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger) public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{ {
if (logger == null) throw new ArgumentNullException(nameof(logger)); if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -59,7 +58,7 @@ namespace MQTTnet.Client
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; } public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
public bool IsConnected => _isConnected && Interlocked.Read(ref _isDisconnectPending) == 0; public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
public IMqttClientOptions Options { get; private set; } public IMqttClientOptions Options { get; private set; }
@@ -72,6 +71,11 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
if (CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connecting, MqttClientConnectionStatus.Disconnected) != MqttClientConnectionStatus.Disconnected)
{
throw new InvalidOperationException("Not allowed to connect while connect/disconnect is pending.");
}
MqttClientAuthenticateResult authenticateResult = null; MqttClientAuthenticateResult authenticateResult = null;
try try
@@ -79,12 +83,11 @@ namespace MQTTnet.Client
Options = options; Options = options;
_packetIdentifierProvider.Reset(); _packetIdentifierProvider.Reset();
_packetDispatcher.Cancel(); _packetDispatcher.CancelAll();
_backgroundCancellationTokenSource = new CancellationTokenSource(); _backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token; var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
_isDisconnectPending = 0;
var adapter = _adapterFactory.CreateClientAdapter(options); var adapter = _adapterFactory.CreateClientAdapter(options);
_adapter = adapter; _adapter = adapter;
@@ -102,15 +105,14 @@ namespace MQTTnet.Client
authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false); authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
} }
_sendTracker.Restart(); _lastPacketSentTimestamp = DateTime.UtcNow;
_receiveTracker.Restart();
if (Options.KeepAlivePeriod != TimeSpan.Zero) if (Options.KeepAlivePeriod != TimeSpan.Zero)
{ {
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken); _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
} }
_isConnected = true; CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
_logger.Info("Connected."); _logger.Info("Connected.");
@@ -128,10 +130,7 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while connecting with server."); _logger.Error(exception, "Error while connecting with server.");
if (!DisconnectIsPending()) await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false);
{
await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false);
}
throw; throw;
} }
@@ -143,7 +142,8 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
if (DisconnectIsPending()) var clientWasConnected = IsConnected;
if (DisconnectIsPendingOrFinished())
{ {
return; return;
} }
@@ -153,7 +153,7 @@ namespace MQTTnet.Client
_disconnectReason = MqttClientDisconnectReason.NormalDisconnection; _disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
_cleanDisconnectInitiated = true; _cleanDisconnectInitiated = true;
if (_isConnected) if (clientWasConnected)
{ {
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options); var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false); await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
@@ -161,13 +161,13 @@ namespace MQTTnet.Client
} }
finally finally
{ {
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false); await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
} }
} }
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)
@@ -232,21 +232,21 @@ namespace MQTTnet.Client
switch (applicationMessage.QualityOfServiceLevel) switch (applicationMessage.QualityOfServiceLevel)
{ {
case MqttQualityOfServiceLevel.AtMostOnce: case MqttQualityOfServiceLevel.AtMostOnce:
{ {
return PublishAtMostOnce(publishPacket, cancellationToken); return PublishAtMostOnce(publishPacket, cancellationToken);
} }
case MqttQualityOfServiceLevel.AtLeastOnce: case MqttQualityOfServiceLevel.AtLeastOnce:
{ {
return PublishAtLeastOnceAsync(publishPacket, cancellationToken); return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
} }
case MqttQualityOfServiceLevel.ExactlyOnce: case MqttQualityOfServiceLevel.ExactlyOnce:
{ {
return PublishExactlyOnceAsync(publishPacket, cancellationToken); return PublishExactlyOnceAsync(publishPacket, cancellationToken);
} }
default: default:
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
} }
@@ -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.");
@@ -299,7 +308,7 @@ namespace MQTTnet.Client
void ThrowIfNotConnected() void ThrowIfNotConnected()
{ {
if (!IsConnected || Interlocked.Read(ref _isDisconnectPending) == 1) if (!IsConnected)
{ {
throw new MqttCommunicationException("The client is not connected."); throw new MqttCommunicationException("The client is not connected.");
} }
@@ -310,12 +319,21 @@ namespace MQTTnet.Client
if (IsConnected) throw new MqttProtocolViolationException(message); if (IsConnected) throw new MqttProtocolViolationException(message);
} }
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult) Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
{ {
var clientWasConnected = _isConnected; var clientWasConnected = IsConnected;
if (!DisconnectIsPendingOrFinished())
{
return DisconnectCoreAsync(sender, exception, authenticateResult, clientWasConnected);
}
return PlatformAbstractionLayer.CompletedTask;
}
async Task DisconnectCoreAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult, bool clientWasConnected)
{
TryInitiateDisconnect(); TryInitiateDisconnect();
_isConnected = false;
try try
{ {
@@ -339,8 +357,6 @@ namespace MQTTnet.Client
var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender); var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender);
await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false); await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false);
_publishPacketReceiverQueue?.Dispose();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -350,6 +366,7 @@ namespace MQTTnet.Client
{ {
Cleanup(); Cleanup();
_cleanDisconnectInitiated = false; _cleanDisconnectInitiated = false;
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Disconnected, MqttClientConnectionStatus.Disconnecting);
_logger.Info("Disconnected."); _logger.Info("Disconnected.");
@@ -382,7 +399,7 @@ namespace MQTTnet.Client
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
_sendTracker.Restart(); _lastPacketSentTimestamp = DateTime.UtcNow;
return _adapter.SendPacketAsync(packet, cancellationToken); return _adapter.SendPacketAsync(packet, cancellationToken);
} }
@@ -391,23 +408,22 @@ namespace MQTTnet.Client
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
ushort identifier = 0; ushort packetIdentifier = 0;
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0) if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier)
{ {
identifier = packetWithIdentifier.PacketIdentifier; packetIdentifier = packetWithIdentifier.PacketIdentifier;
} }
using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier)) using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(packetIdentifier))
{ {
try try
{ {
_sendTracker.Restart(); await SendAsync(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
@@ -437,15 +453,15 @@ namespace MQTTnet.Client
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
// Values described here: [MQTT-3.1.2-24]. // Values described here: [MQTT-3.1.2-24].
var waitTime = keepAlivePeriod - _sendTracker.Elapsed; var timeWithoutPacketSent = DateTime.UtcNow - _lastPacketSentTimestamp;
if (waitTime <= TimeSpan.Zero) if (timeWithoutPacketSent > keepAlivePeriod)
{ {
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false); await PingAsync(cancellationToken).ConfigureAwait(false);
} }
// Wait a fixed time in all cases. Calculation of the remaining time is complicated // Wait a fixed time in all cases. Calculation of the remaining time is complicated
// due to some edge cases and was buggy in the past. Now we wait half a second because the // due to some edge cases and was buggy in the past. Now we wait several ms because the
// min keep alive value is one second so that the server will wait 1.5 seconds for a PING // min keep alive value is one second so that the server will wait 1.5 seconds for a PING
// packet. // packet.
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false); await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false);
@@ -471,10 +487,7 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error exception while sending/receiving keep alive packets."); _logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
} }
if (!DisconnectIsPending()) await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
{
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -499,10 +512,7 @@ namespace MQTTnet.Client
if (packet == null) if (packet == null)
{ {
if (!DisconnectIsPending()) await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
{
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
}
return; return;
} }
@@ -529,12 +539,9 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while receiving packets."); _logger.Error(exception, "Error while receiving packets.");
} }
_packetDispatcher.Dispatch(exception); _packetDispatcher.FailAll(exception);
if (!DisconnectIsPending()) await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -546,8 +553,6 @@ namespace MQTTnet.Client
{ {
try try
{ {
_receiveTracker.Restart();
if (packet is MqttPublishPacket publishPacket) if (packet is MqttPublishPacket publishPacket)
{ {
EnqueueReceivedPublishPacket(publishPacket); EnqueueReceivedPublishPacket(publishPacket);
@@ -560,10 +565,6 @@ namespace MQTTnet.Client
{ {
await ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false); await ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttPingReqPacket)
{
await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttDisconnectPacket disconnectPacket) else if (packet is MqttDisconnectPacket disconnectPacket)
{ {
await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false); await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false);
@@ -572,9 +573,20 @@ namespace MQTTnet.Client
{ {
await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false); await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false);
} }
else if (packet is MqttPingRespPacket)
{
_packetDispatcher.TryDispatch(packet);
}
else if (packet is MqttPingReqPacket)
{
throw new MqttProtocolViolationException("The PINGREQ Packet is sent from a Client to the Server only.");
}
else else
{ {
_packetDispatcher.Dispatch(packet); if (!_packetDispatcher.TryDispatch(packet))
{
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
}
} }
} }
catch (Exception exception) catch (Exception exception)
@@ -596,12 +608,9 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while receiving packets."); _logger.Error(exception, "Error while receiving packets.");
} }
_packetDispatcher.Dispatch(exception); _packetDispatcher.FailAll(exception);
if (!DisconnectIsPending()) await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
} }
} }
@@ -630,40 +639,11 @@ namespace MQTTnet.Client
} }
var publishPacket = publishPacketDequeueResult.Item; var publishPacket = publishPacketDequeueResult.Item;
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (eventArgs.AutoAcknowledge)
{ {
await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); await eventArgs.AcknowledgeAsync(cancellationToken).ConfigureAwait(false);
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
await SendAsync(new MqttPubAckPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
await SendAsync(new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -676,17 +656,44 @@ namespace MQTTnet.Client
} }
} }
internal Task AcknowledgeReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs, CancellationToken cancellationToken)
{
if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
// no response required
}
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
if (!eventArgs.ProcessingFailed)
{
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
return SendAsync(pubAckPacket, cancellationToken);
}
}
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
if (!eventArgs.ProcessingFailed)
{
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
return SendAsync(pubRecPacket, cancellationToken);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
}
return PlatformAbstractionLayer.CompletedTask;
}
Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken) Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken)
{ {
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;
@@ -694,26 +701,18 @@ 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)
{ {
_disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection); _disconnectReason = (MqttClientDisconnectReason) (disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
// Also dispatch disconnect to waiting threads to generate a proper exception. // Also dispatch disconnect to waiting threads to generate a proper exception.
_packetDispatcher.Dispatch(disconnectPacket); _packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
if (!DisconnectIsPending()) return DisconnectInternalAsync(_packetReceiverTask, null, null);
{
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
return PlatformAbstractionLayer.CompletedTask;
} }
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket) Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
@@ -731,14 +730,16 @@ 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);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(response); var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubAckPacket);
} }
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
@@ -747,21 +748,17 @@ 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);
} }
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) async Task<MqttApplicationMessageReceivedEventArgs> 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 eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
var handler = ApplicationMessageReceivedHandler; var handler = ApplicationMessageReceivedHandler;
if (handler != null) if (handler != null)
@@ -803,11 +800,36 @@ namespace MQTTnet.Client
} }
} }
bool DisconnectIsPending() bool DisconnectIsPendingOrFinished()
{ {
// This will read the _isDisconnectPending and set it to "1" afterwards regardless of the value. var connectionStatus = (MqttClientConnectionStatus)_connectionStatus;
// So the first caller will get a "false" and all subsequent ones will get "true".
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0; do
{
switch (connectionStatus)
{
case MqttClientConnectionStatus.Disconnected:
case MqttClientConnectionStatus.Disconnecting:
return true;
case MqttClientConnectionStatus.Connected:
case MqttClientConnectionStatus.Connecting:
// This will compare the _connectionStatus to old value and set it to "MqttClientConnectionStatus.Disconnecting" afterwards.
// So the first caller will get a "false" and all subsequent ones will get "true".
var curStatus = CompareExchangeConnectionStatus(MqttClientConnectionStatus.Disconnecting, connectionStatus);
if (curStatus == connectionStatus)
{
return false;
}
connectionStatus = curStatus;
break;
}
} while (true);
}
MqttClientConnectionStatus CompareExchangeConnectionStatus(MqttClientConnectionStatus value, MqttClientConnectionStatus comparand)
{
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
} }
} }
} }
@@ -0,0 +1,10 @@
namespace MQTTnet.Client
{
public enum MqttClientConnectionStatus
{
Disconnected,
Disconnecting,
Connected,
Connecting
}
}
@@ -252,7 +252,11 @@ namespace MQTTnet.Client.Options
{ {
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_tlsParameters = new MqttClientOptionsBuilderTlsParameters(); _tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
UseTls = true
};
optionsBuilder(_tlsParameters); optionsBuilder(_tlsParameters);
return this; return this;
} }
@@ -287,7 +291,7 @@ namespace MQTTnet.Client.Options
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback, CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
ApplicationProtocols = _tlsParameters.ApplicationProtocols, ApplicationProtocols = _tlsParameters.ApplicationProtocols,
#endif #endif
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler, CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Security; using System.Net.Security;
using System.Security.Authentication; using System.Security.Authentication;
@@ -31,9 +31,9 @@ namespace MQTTnet.Client.Options
public IEnumerable<X509Certificate> Certificates { get; set; } public IEnumerable<X509Certificate> Certificates { get; set; }
#endif #endif
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get;set; } public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
#endif #endif
public bool AllowUntrustedCertificates { get; set; } public bool AllowUntrustedCertificates { get; set; }
@@ -22,7 +22,7 @@ namespace MQTTnet.Client.Options
public List<X509Certificate> Certificates { get; set; } public List<X509Certificate> Certificates { get; set; }
#endif #endif
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get; set; } public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
#endif #endif
+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);
} }
} }
@@ -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,
@@ -215,15 +244,10 @@ namespace MQTTnet.Formatter.V3
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options) public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{ {
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
{
throw new MqttProtocolViolationException("Reason codes and reason string for disconnect are only supported for MQTTv5.");
}
return new MqttDisconnectPacket(); return new MqttDisconnectPacket();
} }
public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket) public MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket)
{ {
return new MqttClientPublishResult return new MqttClientPublishResult
{ {
@@ -232,7 +256,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)
{ {
@@ -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;
} }
} }
} }
@@ -75,23 +75,11 @@ namespace MQTTnet.Implementations
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult); _socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
} }
#if NETCOREAPP3_1 || NET5_0
var socketProtectionLevel = SocketProtectionLevel.Tls13;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls12)
{
socketProtectionLevel = SocketProtectionLevel.Tls12;
}
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{
socketProtectionLevel = SocketProtectionLevel.Tls11;
}
#else
var socketProtectionLevel = SocketProtectionLevel.Tls12; var socketProtectionLevel = SocketProtectionLevel.Tls12;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11) if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{ {
socketProtectionLevel = SocketProtectionLevel.Tls11; socketProtectionLevel = SocketProtectionLevel.Tls11;
} }
#endif
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls) else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
{ {
socketProtectionLevel = SocketProtectionLevel.Tls10; socketProtectionLevel = SocketProtectionLevel.Tls10;
@@ -83,7 +83,7 @@ namespace MQTTnet.Implementations
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
try try
{ {
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
var sslOptions = new SslClientAuthenticationOptions var sslOptions = new SslClientAuthenticationOptions
{ {
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols, ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
@@ -100,7 +100,7 @@ namespace MQTTnet.Implementations
} }
catch catch
{ {
#if NETSTANDARD2_1 || NETCOREAPP3_1 #if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0
await sslStream.DisposeAsync().ConfigureAwait(false); await sslStream.DisposeAsync().ConfigureAwait(false);
#else #else
sslStream.Dispose(); sslStream.Dispose();
@@ -98,7 +98,7 @@ namespace MQTTnet.Implementations
{ {
_socket?.Dispose(); _socket?.Dispose();
#if NETSTANDARD1_3 || NETSTANDARD2_0 || NET461 || NET472 #if !NET452
_tlsCertificate?.Dispose(); _tlsCertificate?.Dispose();
#endif #endif
} }
@@ -17,7 +17,7 @@ namespace MQTTnet.Implementations
public static void Sleep(TimeSpan timeout) public static void Sleep(TimeSpan timeout)
{ {
#if NET452 || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 #if !NETSTANDARD1_3 && !WINDOWS_UWP
System.Threading.Thread.Sleep(timeout); System.Threading.Thread.Sleep(timeout);
#else #else
Task.Delay(timeout).Wait(); Task.Delay(timeout).Wait();
-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>
+8
View File
@@ -35,6 +35,14 @@ namespace MQTTnet
/// The broker stores the last retained message and the corresponding QoS for that topic. /// The broker stores the last retained message and the corresponding QoS for that topic.
/// </summary> /// </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> /// <summary>
/// Gets or sets the user properties. /// Gets or sets the user properties.
+27 -18
View File
@@ -19,54 +19,56 @@ namespace MQTTnet
/// - At least once (1): Message gets delivered at least once (one time or more often). /// - 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). /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary> /// </summary>
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
/// <summary> /// <summary>
/// The MQTT topic. /// 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. /// 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). /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
/// </summary> /// </summary>
private string _topic; string _topic;
/// <summary> /// <summary>
/// The payload. /// The payload.
/// The payload is the data bytes sent via the MQTT protocol. /// The payload is the data bytes sent via the MQTT protocol.
/// </summary> /// </summary>
private byte[] _payload; byte[] _payload;
/// <summary> /// <summary>
/// A value indicating whether the message should be retained or not. /// 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. /// 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. /// The broker stores the last retained message and the corresponding QoS for that topic.
/// </summary> /// </summary>
private bool _retain; bool _retain;
bool _dup;
/// <summary> /// <summary>
/// The content type. /// 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. /// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload.
/// </summary> /// </summary>
private string _contentType; string _contentType;
/// <summary> /// <summary>
/// The response topic. /// 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. /// 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. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private string _responseTopic; string _responseTopic;
/// <summary> /// <summary>
/// The correlation data. /// 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. /// 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. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private byte[] _correlationData; byte[] _correlationData;
/// <summary> /// <summary>
/// The topic alias. /// 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. /// 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. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private ushort? _topicAlias; ushort? _topicAlias;
/// <summary> /// <summary>
/// The subscription identifiers. /// The subscription identifiers.
@@ -75,7 +77,7 @@ namespace MQTTnet
/// 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. /// 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. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private List<uint> _subscriptionIdentifiers; List<uint> _subscriptionIdentifiers;
/// <summary> /// <summary>
/// The message expiry interval. /// The message expiry interval.
@@ -85,7 +87,7 @@ namespace MQTTnet
/// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic. /// 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. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private uint? _messageExpiryInterval; uint? _messageExpiryInterval;
/// <summary> /// <summary>
/// The payload format indicator. /// The payload format indicator.
@@ -95,7 +97,7 @@ namespace MQTTnet
/// If no payload format indicator is provided, the default value is 0. /// If no payload format indicator is provided, the default value is 0.
/// Hint: MQTT 5 feature only. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
private MqttPayloadFormatIndicator? _payloadFormatIndicator; MqttPayloadFormatIndicator? _payloadFormatIndicator;
/// <summary> /// <summary>
/// The user properties. /// The user properties.
@@ -104,7 +106,7 @@ namespace MQTTnet
/// The feature is very similar to the HTTP header concept. /// The feature is very similar to the HTTP header concept.
/// </summary> /// </summary>
/// Hint: MQTT 5 feature only. /// Hint: MQTT 5 feature only.
private List<MqttUserProperty> _userProperties; List<MqttUserProperty> _userProperties;
public MqttApplicationMessageBuilder WithTopic(string topic) public MqttApplicationMessageBuilder WithTopic(string topic)
{ {
@@ -126,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;
} }
@@ -201,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()
{ {
@@ -338,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,
@@ -1,15 +1,30 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Packets;
namespace MQTTnet namespace MQTTnet
{ {
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
{ {
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage) readonly Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> _acknowledgeHandler;
int _isAcknowledged;
public MqttApplicationMessageReceivedEventArgs(
string clientId,
MqttApplicationMessage applicationMessage,
MqttPublishPacket publishPacket,
Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> acknowledgeHandler)
{ {
ClientId = clientId; ClientId = clientId;
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
PublishPacket = publishPacket;
_acknowledgeHandler = acknowledgeHandler;
} }
internal MqttPublishPacket PublishPacket { get; }
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
@@ -21,5 +36,33 @@ namespace MQTTnet
public bool ProcessingFailed { get; set; } public bool ProcessingFailed { get; set; }
public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success; 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; }
/// <summary>
/// Gets ir sets whether the library should send MQTT ACK packets automatically if required.
/// </summary>
public bool AutoAcknowledge { get; set; } = true;
public object Tag { get; set; }
public Task AcknowledgeAsync(CancellationToken cancellationToken)
{
if (_acknowledgeHandler == null)
{
throw new NotSupportedException("Deferred acknowledgement of application message is not yet supported in MQTTnet server.");
}
if (Interlocked.CompareExchange(ref _isAcknowledged, 1, 0) == 0)
{
return _acknowledgeHandler(this, cancellationToken);
}
throw new InvalidOperationException("The application message is already acknowledged.");
}
} }
} }
@@ -9,6 +9,7 @@
NotAuthorized = 135, NotAuthorized = 135,
TopicNameInvalid = 144, TopicNameInvalid = 144,
PacketIdentifierInUse = 145, PacketIdentifierInUse = 145,
PacketIdentifierNotFound = 146,
QuotaExceeded = 151, QuotaExceeded = 151,
PayloadFormatInvalid = 153 PayloadFormatInvalid = 153
} }
@@ -5,6 +5,8 @@ namespace MQTTnet.PacketDispatcher
{ {
public interface IMqttPacketAwaiter : IDisposable public interface IMqttPacketAwaiter : IDisposable
{ {
MqttPacketAwaiterPacketFilter PacketFilter { get; }
void Complete(MqttBasePacket packet); void Complete(MqttBasePacket packet);
void Fail(Exception exception); void Fail(Exception exception);
@@ -9,12 +9,16 @@ namespace MQTTnet.PacketDispatcher
public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket
{ {
readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource; readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource;
readonly ushort? _packetIdentifier;
readonly MqttPacketDispatcher _owningPacketDispatcher; readonly MqttPacketDispatcher _owningPacketDispatcher;
public MqttPacketAwaiter(ushort? packetIdentifier, MqttPacketDispatcher owningPacketDispatcher) public MqttPacketAwaiter(ushort packetIdentifier, MqttPacketDispatcher owningPacketDispatcher)
{ {
_packetIdentifier = packetIdentifier; PacketFilter = new MqttPacketAwaiterPacketFilter
{
Type = typeof(TPacket),
Identifier = packetIdentifier
};
_owningPacketDispatcher = owningPacketDispatcher ?? throw new ArgumentNullException(nameof(owningPacketDispatcher)); _owningPacketDispatcher = owningPacketDispatcher ?? throw new ArgumentNullException(nameof(owningPacketDispatcher));
#if NET452 #if NET452
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(); _taskCompletionSource = new TaskCompletionSource<MqttBasePacket>();
@@ -22,7 +26,9 @@ namespace MQTTnet.PacketDispatcher
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(TaskCreationOptions.RunContinuationsAsynchronously); _taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(TaskCreationOptions.RunContinuationsAsynchronously);
#endif #endif
} }
public MqttPacketAwaiterPacketFilter PacketFilter { get; }
public async Task<TPacket> WaitOneAsync(TimeSpan timeout) public async Task<TPacket> WaitOneAsync(TimeSpan timeout)
{ {
using (var timeoutToken = new CancellationTokenSource(timeout)) using (var timeoutToken = new CancellationTokenSource(timeout))
@@ -82,7 +88,7 @@ namespace MQTTnet.PacketDispatcher
public void Dispose() public void Dispose()
{ {
_owningPacketDispatcher.RemoveAwaiter<TPacket>(_packetIdentifier); _owningPacketDispatcher.RemoveAwaiter(this);
} }
} }
} }
@@ -0,0 +1,11 @@
using System;
namespace MQTTnet.PacketDispatcher
{
public sealed class MqttPacketAwaiterPacketFilter
{
public Type Type { get; set; }
public ushort Identifier { get; set; }
}
}
@@ -1,103 +1,101 @@
using MQTTnet.Exceptions;
using MQTTnet.Packets; using MQTTnet.Packets;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Generic;
namespace MQTTnet.PacketDispatcher namespace MQTTnet.PacketDispatcher
{ {
public sealed class MqttPacketDispatcher public sealed class MqttPacketDispatcher
{ {
readonly ConcurrentDictionary<Tuple<ushort, Type>, IMqttPacketAwaiter> _awaiters = new ConcurrentDictionary<Tuple<ushort, Type>, IMqttPacketAwaiter>(); readonly List<IMqttPacketAwaiter> _awaiters = new List<IMqttPacketAwaiter>();
public void Dispatch(Exception exception) public void FailAll(Exception exception)
{ {
foreach (var awaiter in _awaiters) if (exception == null) throw new ArgumentNullException(nameof(exception));
{
awaiter.Value.Fail(exception);
}
_awaiters.Clear(); lock (_awaiters)
{
foreach (var awaiter in _awaiters)
{
awaiter.Fail(exception);
}
_awaiters.Clear();
}
} }
public void CancelAll()
{
lock (_awaiters)
{
foreach (var entry in _awaiters)
{
entry.Cancel();
}
_awaiters.Clear();
}
}
public bool TryDispatch(MqttBasePacket packet) public bool TryDispatch(MqttBasePacket packet)
{ {
if (packet == null) throw new ArgumentNullException(nameof(packet)); if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packet is MqttDisconnectPacket disconnectPacket)
{
foreach (var packetAwaiter in _awaiters)
{
packetAwaiter.Value.Fail(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
}
return true;
}
ushort identifier = 0; ushort identifier = 0;
if (packet is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0) if (packet is IMqttPacketWithIdentifier packetWithIdentifier)
{ {
identifier = packetWithIdentifier.PacketIdentifier; identifier = packetWithIdentifier.PacketIdentifier;
} }
var type = packet.GetType(); var packetType = packet.GetType();
var key = new Tuple<ushort, Type>(identifier, type); var matchingAwaiters = new List<IMqttPacketAwaiter>();
if (_awaiters.TryRemove(key, out var awaiter)) lock (_awaiters)
{ {
awaiter.Complete(packet); for (var i = _awaiters.Count - 1; i >= 0; i--)
return true; {
var entry = _awaiters[i];
// Note: The PingRespPacket will also arrive here and has NO identifier but there
// is code which waits for it. So the code must be able to deal with filters which
// are referring to the type only (identifier is 0)!
if (entry.PacketFilter.Type != packetType || entry.PacketFilter.Identifier != identifier)
{
continue;
}
matchingAwaiters.Add(entry);
_awaiters.RemoveAt(i);
}
}
foreach (var matchingEntry in matchingAwaiters)
{
matchingEntry.Complete(packet);
} }
return false; return matchingAwaiters.Count > 0;
} }
public void Dispatch(MqttBasePacket packet) public MqttPacketAwaiter<TResponsePacket> AddAwaiter<TResponsePacket>(ushort packetIdentifier) where TResponsePacket : MqttBasePacket
{ {
if (packet == null) throw new ArgumentNullException(nameof(packet)); var awaiter = new MqttPacketAwaiter<TResponsePacket>(packetIdentifier, this);
if (!TryDispatch(packet)) lock (_awaiters)
{ {
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time."); _awaiters.Add(awaiter);
} }
}
public void Cancel()
{
foreach (var awaiter in _awaiters)
{
awaiter.Value.Cancel();
}
_awaiters.Clear();
}
public MqttPacketAwaiter<TResponsePacket> AddAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
{
if (!identifier.HasValue)
{
identifier = 0;
}
var awaiter = new MqttPacketAwaiter<TResponsePacket>(identifier, this);
var key = new Tuple<ushort, Type>(identifier.Value, typeof(TResponsePacket));
if (!_awaiters.TryAdd(key, awaiter))
{
throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{key.Item2.Name}' with identifier {key.Item1}.");
}
return awaiter; return awaiter;
} }
public void RemoveAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket public void RemoveAwaiter(IMqttPacketAwaiter awaiter)
{ {
if (!identifier.HasValue) if (awaiter == null) throw new ArgumentNullException(nameof(awaiter));
lock (_awaiters)
{ {
identifier = 0; _awaiters.Remove(awaiter);
} }
var key = new Tuple<ushort, Type>(identifier.Value, typeof(TResponsePacket));
_awaiters.TryRemove(key, out _);
} }
} }
} }
+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()
{ {
+3 -6
View File
@@ -8,7 +8,7 @@ namespace MQTTnet.Server
/// Gets the client identifier. /// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary> /// </summary>
string ClientId { get; set; } string ClientId { get; }
bool EnablePersistentSessions { get; } bool EnablePersistentSessions { get; }
@@ -23,16 +23,13 @@ namespace MQTTnet.Server
IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; } IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; }
IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; } IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; }
IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; } IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; }
IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; }
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; } MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
IMqttServerStorage Storage { get; } IMqttServerStorage Storage { get; }
IMqttRetainedMessagesManager RetainedMessagesManager { get; } IMqttRetainedMessagesManager RetainedMessagesManager { get; }
IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
IMqttServerClientDisconnectedHandler ClientDisconnectedInterceptor { get; set; }
} }
} }
+31 -23
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)
{ {
@@ -203,6 +212,10 @@ namespace MQTTnet.Server
{ {
await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false); await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttPingRespPacket)
{
throw new MqttProtocolViolationException("A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet only.");
}
else if (packet is MqttDisconnectPacket) else if (packet is MqttDisconnectPacket)
{ {
Session.WillMessage = null; Session.WillMessage = null;
@@ -213,7 +226,10 @@ namespace MQTTnet.Server
} }
else else
{ {
_packetDispatcher.Dispatch(packet); if (!_packetDispatcher.TryDispatch(packet))
{
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
}
} }
} }
} }
@@ -235,7 +251,7 @@ namespace MQTTnet.Server
} }
finally finally
{ {
if (_disconnectReason == MqttDisconnectReasonCode.SessionTakenOver) if (_disconnectReason == MqttClientDisconnectReason.SessionTakenOver)
{ {
disconnectType = MqttClientDisconnectType.Takeover; disconnectType = MqttClientDisconnectType.Takeover;
} }
@@ -246,7 +262,7 @@ namespace MQTTnet.Server
Session.WillMessage = null; Session.WillMessage = null;
} }
_packetDispatcher.Cancel(); _packetDispatcher.CancelAll();
_logger.Info("Client '{0}': Connection stopped.", ClientId); _logger.Info("Client '{0}': Connection stopped.", ClientId);
@@ -277,12 +293,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 +338,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 +452,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);
} }
@@ -1,4 +1,4 @@
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
@@ -12,7 +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 OperationCanceledException = System.OperationCanceledException; using MQTTnet.Client.Disconnecting;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
@@ -20,7 +20,7 @@ namespace MQTTnet.Server
{ {
readonly BlockingCollection<MqttPendingApplicationMessage> _messageQueue = new BlockingCollection<MqttPendingApplicationMessage>(); readonly BlockingCollection<MqttPendingApplicationMessage> _messageQueue = new BlockingCollection<MqttPendingApplicationMessage>();
readonly object _createConnectionSyncRoot = new object(); readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
readonly Dictionary<string, MqttClientConnection> _connections = new Dictionary<string, MqttClientConnection>(); readonly Dictionary<string, MqttClientConnection> _connections = new Dictionary<string, MqttClientConnection>();
readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>(); readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
@@ -97,7 +97,7 @@ namespace MQTTnet.Server
return; return;
} }
var connection = CreateClientConnection(connectPacket, connectionValidatorContext, channelAdapter); var connection = await CreateClientConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false); await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);
await connection.RunAsync().ConfigureAwait(false); await connection.RunAsync().ConfigureAwait(false);
} }
@@ -121,7 +121,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 +211,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);
@@ -236,11 +236,13 @@ namespace MQTTnet.Server
} }
} }
var endpoint = channelAdapter.Endpoint;
await SafeCleanupChannelAsync(channelAdapter).ConfigureAwait(false); await SafeCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
if (clientId != null) if (clientId != null)
{ {
await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false); await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType, endpoint).ConfigureAwait(false);
} }
} }
@@ -301,7 +303,7 @@ namespace MQTTnet.Server
{ {
if (clientConnection != null) if (clientConnection != null)
{ {
await clientConnection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
} }
} }
@@ -386,9 +388,12 @@ namespace MQTTnet.Server
return context; return context;
} }
MqttClientConnection CreateClientConnection(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter) async Task<MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter)
{ {
lock (_createConnectionSyncRoot) MqttClientConnection existingConnection;
MqttClientConnection connection;
using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{ {
MqttClientSession session; MqttClientSession session;
lock (_sessions) lock (_sessions)
@@ -414,8 +419,6 @@ namespace MQTTnet.Server
_sessions[connectPacket.ClientId] = session; _sessions[connectPacket.ClientId] = session;
} }
MqttClientConnection existingConnection;
MqttClientConnection connection;
lock (_connections) lock (_connections)
{ {
_connections.TryGetValue(connectPacket.ClientId, out existingConnection); _connections.TryGetValue(connectPacket.ClientId, out existingConnection);
@@ -424,10 +427,13 @@ namespace MQTTnet.Server
_connections[connectPacket.ClientId] = connection; _connections[connectPacket.ClientId] = connection;
} }
existingConnection?.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).GetAwaiter().GetResult(); if (existingConnection != null)
{
return connection; await existingConnection.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false);
}
} }
return connection;
} }
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(IMqttServerApplicationMessageInterceptor interceptor, MqttClientConnection clientConnection, MqttApplicationMessage applicationMessage) async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(IMqttServerApplicationMessageInterceptor interceptor, MqttClientConnection clientConnection, MqttApplicationMessage applicationMessage)
@@ -1,13 +1,14 @@
using System; using System;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public class MqttServerClientDisconnectedEventArgs : EventArgs public class MqttServerClientDisconnectedEventArgs : EventArgs
{ {
public MqttServerClientDisconnectedEventArgs(string clientId, MqttClientDisconnectType disconnectType) public MqttServerClientDisconnectedEventArgs(string clientId, MqttClientDisconnectType disconnectType, string endpoint)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
DisconnectType = disconnectType; DisconnectType = disconnectType;
Endpoint = endpoint;
} }
/// <summary> /// <summary>
@@ -17,5 +18,7 @@ namespace MQTTnet.Server
public string ClientId { get; } public string ClientId { get; }
public MqttClientDisconnectType DisconnectType { get; } public MqttClientDisconnectType DisconnectType { get; }
public string Endpoint { get; }
} }
} }
@@ -1,4 +1,4 @@
using MQTTnet.Client.Receiving; using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -44,7 +44,7 @@ namespace MQTTnet.Server
} }
} }
public async Task SafeNotifyClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType) public async Task SafeNotifyClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType, string endpoint)
{ {
try try
{ {
@@ -54,7 +54,7 @@ namespace MQTTnet.Server
return; return;
} }
await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType)).ConfigureAwait(false); await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType, endpoint)).ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -108,7 +108,7 @@ namespace MQTTnet.Server
return; return;
} }
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage)).ConfigureAwait(false); await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage, null, null)).ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -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)
{ {
+2 -4
View File
@@ -34,12 +34,10 @@ namespace MQTTnet.Server
public IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; set; } public IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; set; }
public IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
public IMqttServerStorage Storage { get; set; } public IMqttServerStorage Storage { get; set; }
public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager(); public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager();
public IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
public IMqttServerClientDisconnectedHandler ClientDisconnectedInterceptor { get; set; }
} }
} }
@@ -164,19 +164,13 @@ namespace MQTTnet.Server
_options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(value); _options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(value);
return this; return this;
} }
public MqttServerOptionsBuilder WithDisconnectedInterceptor(IMqttServerClientDisconnectedHandler value) public MqttServerOptionsBuilder WithConnectionValidator(Func<MqttConnectionValidatorContext, Task> value)
{ {
_options.ClientDisconnectedInterceptor = value; _options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(value);
return this; return this;
} }
public MqttServerOptionsBuilder WithDisconnectedInterceptor(Action<MqttServerClientDisconnectedEventArgs> value)
{
_options.ClientDisconnectedInterceptor = new MqttServerClientDisconnectedHandlerDelegate(value);
return this;
}
public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value) public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value)
{ {
_options.ApplicationMessageInterceptor = value; _options.ApplicationMessageInterceptor = value;
@@ -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
{ {
@@ -46,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()
@@ -9,6 +9,8 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Extensions.Rpc;
using MQTTnet.Extensions.Rpc.Options;
using MQTTnet.LowLevelClient; using MQTTnet.LowLevelClient;
namespace MQTTnet.Tests.Mockups namespace MQTTnet.Tests.Mockups
@@ -77,6 +79,11 @@ namespace MQTTnet.Tests.Mockups
}; };
} }
public async Task<IMqttRpcClient> ConnectRpcClientAsync(IMqttRpcClientOptions options)
{
return new MqttRpcClient(await ConnectClientAsync(), options);
}
public IMqttClient CreateClient() public IMqttClient CreateClient()
{ {
lock (_clients) lock (_clients)
@@ -109,15 +109,6 @@ namespace MQTTnet.Tests.Mockups
return Implementation.StartAsync(options); return Implementation.StartAsync(options);
} }
public void ConnectionValidator(MqttConnectionValidatorContext ctx)
{
if (!ctx.ClientId.StartsWith(TestContext.TestName))
{
TestEnvironment.TrackException(new InvalidOperationException($"Invalid client ID used ({ctx.ClientId}). It must start with UnitTest name."));
ctx.ReasonCode = Protocol.MqttConnectReasonCode.ClientIdentifierNotValid;
}
}
public Task StopAsync() public Task StopAsync()
{ {
return Implementation.StopAsync(); return Implementation.StopAsync();
@@ -137,5 +128,14 @@ namespace MQTTnet.Tests.Mockups
{ {
Implementation.Dispose(); Implementation.Dispose();
} }
void ConnectionValidator(MqttConnectionValidatorContext ctx)
{
if (!ctx.ClientId.StartsWith(TestContext.TestName))
{
TestEnvironment.TrackException(new InvalidOperationException($"Invalid client ID used ({ctx.ClientId}). It must start with UnitTest name."));
ctx.ReasonCode = Protocol.MqttConnectReasonCode.ClientIdentifierNotValid;
}
}
} }
} }
@@ -445,6 +445,55 @@ namespace MQTTnet.Tests
} }
} }
[TestMethod]
public async Task Preserve_Message_Order_With_Delayed_Acknowledgement()
{
// The messages are sent in reverse or to ensure that the delay in the handler
// needs longer for the first messages and later messages may be processed earlier (if there
// is an issue).
const int MessagesCount = 50;
using (var testEnvironment = new TestEnvironment(TestContext))
{
await testEnvironment.StartServerAsync();
var client1 = await testEnvironment.ConnectClientAsync();
await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.ExactlyOnce);
var receivedValues = new List<int>();
Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
{
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
eventArgs.AutoAcknowledge = false;
Task.Delay(value).ContinueWith(x => eventArgs.AcknowledgeAsync(CancellationToken.None));
System.Diagnostics.Debug.WriteLine($"received {value}");
lock (receivedValues)
{
receivedValues.Add(value);
}
return Task.CompletedTask;
}
client1.UseApplicationMessageReceivedHandler(Handler1);
var client2 = await testEnvironment.ConnectClientAsync();
for (var i = MessagesCount; i > 0; i--)
{
await client2.PublishAsync("x", i.ToString(), MqttQualityOfServiceLevel.ExactlyOnce);
}
await Task.Delay(5000);
for (var i = MessagesCount; i > 0; i--)
{
Assert.AreEqual(i, receivedValues[MessagesCount - i]);
}
}
}
[TestMethod] [TestMethod]
public async Task Send_Reply_For_Any_Received_Message() public async Task Send_Reply_For_Any_Received_Message()
{ {
+11 -8
View File
@@ -81,18 +81,19 @@ namespace MQTTnet.Tests
var requestSender = await testEnvironment.ConnectClientAsync(); var requestSender = await testEnvironment.ConnectClientAsync();
var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()) .Build()); var rpcClient = await testEnvironment.ConnectRpcClientAsync(new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()) .Build());
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce); await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
} }
} }
private async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion) async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion)
{ {
using (var testEnvironment = new TestEnvironment(TestContext)) using (var testEnvironment = new TestEnvironment(TestContext))
{ {
await testEnvironment.StartServerAsync(); await testEnvironment.StartServerAsync();
var responseSender = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithProtocolVersion(protocolVersion)); var responseSender = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithProtocolVersion(protocolVersion));
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping"); await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", qosLevel);
responseSender.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(async e => responseSender.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(async e =>
{ {
@@ -101,14 +102,16 @@ namespace MQTTnet.Tests
var requestSender = await testEnvironment.ConnectClientAsync(); var requestSender = await testEnvironment.ConnectClientAsync();
var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()); using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel); {
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel);
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
}
} }
} }
private class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy
{ {
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
{ {
+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);
} }
} }