Compare commits

..
61 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
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
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
31 changed files with 467 additions and 168 deletions
+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
Build/NuGet/
Build/nuget.exe
+8 -7
View File
@@ -12,13 +12,14 @@
<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>
<releaseNotes>
* [Core] Fixed some issues in nuget packages.
* [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082).
* [Client] Exposed Dup flag in application messages.
* [Client] Improved keep alive message sending.
* [RpcClient] Fixed an issue when using a custom application message reveived handler (#1093).
* [RpcClient] Fixed a race condition when using timeout and cancellation token at the same time (BREAKING CHANGE!).
* [Server] Improved keep alive message sending.
* [Core] Fixed _DISCONNECT_ packet reading for protocol version 3.1.0.DisconnectPacket
* [Client] Added support for deferred message approval (#1075, thanks to @tkurucsai).
* [Client] Exposed missing APIs for .NET 5.0 build (thanks to @yyjdelete).
* [Client] Improved disconnect handling (#1134, thanks to @yyjdelete).
* [Client] Fix TLS parameter options builder (#1104).
* [Server] Add new overload for options builder.
* [Server] Exposed endpoint in _MqttServerClientDisconnectedEventArgs_.
* [Server] Remove client disconnected handler from wrong implementation location (BREAKING CHANGE).
Git commit: $gitCommit
</releaseNotes>
+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,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -55,7 +55,7 @@ namespace MQTTnet.Server.Logging
case MqttNetLogLevel.Error: return LogLevel.Error;
case MqttNetLogLevel.Warning: return LogLevel.Warning;
case MqttNetLogLevel.Info: return LogLevel.Information;
case MqttNetLogLevel.Verbose: return LogLevel.Debug;
case MqttNetLogLevel.Verbose: return LogLevel.Trace;
}
return LogLevel.Debug;
+117 -90
View File
@@ -38,10 +38,10 @@ namespace MQTTnet.Client
IMqttChannelAdapter _adapter;
bool _cleanDisconnectInitiated;
long _isDisconnectPending;
bool _isConnected;
volatile int _connectionStatus;
MqttClientDisconnectReason _disconnectReason;
DateTime _lastPacketSentTimestamp;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
@@ -58,7 +58,7 @@ namespace MQTTnet.Client
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; }
@@ -71,6 +71,11 @@ namespace MQTTnet.Client
ThrowIfDisposed();
if (CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connecting, MqttClientConnectionStatus.Disconnected) != MqttClientConnectionStatus.Disconnected)
{
throw new InvalidOperationException("Not allowed to connect while connect/disconnect is pending.");
}
MqttClientAuthenticateResult authenticateResult = null;
try
@@ -83,7 +88,6 @@ namespace MQTTnet.Client
_backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
_isDisconnectPending = 0;
var adapter = _adapterFactory.CreateClientAdapter(options);
_adapter = adapter;
@@ -108,7 +112,7 @@ namespace MQTTnet.Client
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
}
_isConnected = true;
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
_logger.Info("Connected.");
@@ -126,10 +130,7 @@ namespace MQTTnet.Client
_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;
}
@@ -141,7 +142,8 @@ namespace MQTTnet.Client
ThrowIfDisposed();
if (DisconnectIsPending())
var clientWasConnected = IsConnected;
if (DisconnectIsPendingOrFinished())
{
return;
}
@@ -151,7 +153,7 @@ namespace MQTTnet.Client
_disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
_cleanDisconnectInitiated = true;
if (_isConnected)
if (clientWasConnected)
{
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
@@ -159,7 +161,7 @@ namespace MQTTnet.Client
}
finally
{
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
}
}
@@ -230,21 +232,21 @@ namespace MQTTnet.Client
switch (applicationMessage.QualityOfServiceLevel)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
return PublishAtMostOnce(publishPacket, cancellationToken);
}
{
return PublishAtMostOnce(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
}
{
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
}
{
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
}
default:
{
throw new NotSupportedException();
}
{
throw new NotSupportedException();
}
}
}
@@ -306,7 +308,7 @@ namespace MQTTnet.Client
void ThrowIfNotConnected()
{
if (!IsConnected || Interlocked.Read(ref _isDisconnectPending) == 1)
if (!IsConnected)
{
throw new MqttCommunicationException("The client is not connected.");
}
@@ -317,12 +319,21 @@ namespace MQTTnet.Client
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();
_isConnected = false;
try
{
@@ -346,8 +357,6 @@ namespace MQTTnet.Client
var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender);
await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false);
_publishPacketReceiverQueue?.Dispose();
}
catch (Exception e)
{
@@ -357,6 +366,7 @@ namespace MQTTnet.Client
{
Cleanup();
_cleanDisconnectInitiated = false;
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Disconnected, MqttClientConnectionStatus.Disconnecting);
_logger.Info("Disconnected.");
@@ -390,7 +400,7 @@ namespace MQTTnet.Client
cancellationToken.ThrowIfCancellationRequested();
_lastPacketSentTimestamp = DateTime.UtcNow;
return _adapter.SendPacketAsync(packet, cancellationToken);
}
@@ -477,10 +487,7 @@ namespace MQTTnet.Client
_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
{
@@ -505,10 +512,7 @@ namespace MQTTnet.Client
if (packet == null)
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
return;
}
@@ -537,10 +541,7 @@ namespace MQTTnet.Client
_packetDispatcher.FailAll(exception);
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
finally
{
@@ -609,10 +610,7 @@ namespace MQTTnet.Client
_packetDispatcher.FailAll(exception);
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
}
@@ -641,34 +639,11 @@ namespace MQTTnet.Client
}
var publishPacket = publishPacketDequeueResult.Item;
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
if (eventArgs.AutoAcknowledge)
{
await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(publishPacket, eventArgs.ReasonCode);
await SendAsync(pubAckPacket, cancellationToken).ConfigureAwait(false);
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(publishPacket, eventArgs.ReasonCode);
await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
await eventArgs.AcknowledgeAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -681,6 +656,36 @@ 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)
{
if (!_packetDispatcher.TryDispatch(pubRecPacket))
@@ -702,17 +707,12 @@ namespace MQTTnet.Client
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.
_packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
if (!DisconnectIsPending())
{
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
return PlatformAbstractionLayer.CompletedTask;
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
@@ -736,8 +736,10 @@ namespace MQTTnet.Client
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(response);
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubAckPacket);
}
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
@@ -747,7 +749,7 @@ namespace MQTTnet.Client
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
@@ -756,7 +758,7 @@ namespace MQTTnet.Client
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket 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;
if (handler != null)
@@ -798,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.
// So the first caller will get a "false" and all subsequent ones will get "true".
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0;
var connectionStatus = (MqttClientConnectionStatus)_connectionStatus;
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));
_tlsParameters = new MqttClientOptionsBuilderTlsParameters();
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
UseTls = true
};
optionsBuilder(_tlsParameters);
return this;
}
@@ -287,7 +291,7 @@ namespace MQTTnet.Client.Options
#pragma warning disable CS0618 // Type or member is obsolete
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
#pragma warning restore CS0618 // Type or member is obsolete
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
#endif
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
@@ -31,9 +31,9 @@ namespace MQTTnet.Client.Options
public IEnumerable<X509Certificate> Certificates { get; set; }
#endif
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
#endif
#endif
public bool AllowUntrustedCertificates { get; set; }
@@ -22,7 +22,7 @@ namespace MQTTnet.Client.Options
public List<X509Certificate> Certificates { get; set; }
#endif
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
#endif
@@ -244,11 +244,6 @@ namespace MQTTnet.Formatter.V3
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();
}
@@ -75,23 +75,11 @@ namespace MQTTnet.Implementations
_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;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{
socketProtectionLevel = SocketProtectionLevel.Tls11;
}
#endif
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
{
socketProtectionLevel = SocketProtectionLevel.Tls10;
@@ -83,7 +83,7 @@ namespace MQTTnet.Implementations
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
try
{
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
var sslOptions = new SslClientAuthenticationOptions
{
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
@@ -100,7 +100,7 @@ namespace MQTTnet.Implementations
}
catch
{
#if NETSTANDARD2_1 || NETCOREAPP3_1
#if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0
await sslStream.DisposeAsync().ConfigureAwait(false);
#else
sslStream.Dispose();
@@ -98,7 +98,7 @@ namespace MQTTnet.Implementations
{
_socket?.Dispose();
#if NETSTANDARD1_3 || NETSTANDARD2_0 || NET461 || NET472
#if !NET452
_tlsCertificate?.Dispose();
#endif
}
@@ -17,7 +17,7 @@ namespace MQTTnet.Implementations
public static void Sleep(TimeSpan timeout)
{
#if NET452 || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1
#if !NETSTANDARD1_3 && !WINDOWS_UWP
System.Threading.Thread.Sleep(timeout);
#else
Task.Delay(timeout).Wait();
@@ -1,15 +1,30 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Packets;
namespace MQTTnet
{
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;
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
PublishPacket = publishPacket;
_acknowledgeHandler = acknowledgeHandler;
}
internal MqttPublishPacket PublishPacket { get; }
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
@@ -27,7 +42,27 @@ namespace MQTTnet
/// 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.");
}
}
}
+3 -6
View File
@@ -8,7 +8,7 @@ namespace MQTTnet.Server
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; set; }
string ClientId { get; }
bool EnablePersistentSessions { get; }
@@ -23,16 +23,13 @@ namespace MQTTnet.Server
IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; }
IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; }
IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; }
IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; }
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
IMqttServerStorage Storage { get; }
IMqttRetainedMessagesManager RetainedMessagesManager { get; }
IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
IMqttServerClientDisconnectedHandler ClientDisconnectedInterceptor { get; set; }
}
}
@@ -1,4 +1,4 @@
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
@@ -13,7 +13,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client.Disconnecting;
using OperationCanceledException = System.OperationCanceledException;
namespace MQTTnet.Server
{
@@ -21,7 +20,7 @@ namespace MQTTnet.Server
{
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, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
@@ -98,7 +97,7 @@ namespace MQTTnet.Server
return;
}
var connection = CreateClientConnection(connectPacket, connectionValidatorContext, channelAdapter);
var connection = await CreateClientConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);
await connection.RunAsync().ConfigureAwait(false);
}
@@ -237,11 +236,13 @@ namespace MQTTnet.Server
}
}
var endpoint = channelAdapter.Endpoint;
await SafeCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
if (clientId != null)
{
await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType, endpoint).ConfigureAwait(false);
}
}
@@ -387,9 +388,12 @@ namespace MQTTnet.Server
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;
lock (_sessions)
@@ -415,8 +419,6 @@ namespace MQTTnet.Server
_sessions[connectPacket.ClientId] = session;
}
MqttClientConnection existingConnection;
MqttClientConnection connection;
lock (_connections)
{
_connections.TryGetValue(connectPacket.ClientId, out existingConnection);
@@ -425,10 +427,13 @@ namespace MQTTnet.Server
_connections[connectPacket.ClientId] = connection;
}
existingConnection?.StopAsync(MqttClientDisconnectReason.SessionTakenOver).GetAwaiter().GetResult();
return connection;
if (existingConnection != null)
{
await existingConnection.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false);
}
}
return connection;
}
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(IMqttServerApplicationMessageInterceptor interceptor, MqttClientConnection clientConnection, MqttApplicationMessage applicationMessage)
@@ -1,13 +1,14 @@
using System;
using System;
namespace MQTTnet.Server
{
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));
DisconnectType = disconnectType;
Endpoint = endpoint;
}
/// <summary>
@@ -17,5 +18,7 @@ namespace MQTTnet.Server
public string ClientId { 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 System;
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
{
@@ -54,7 +54,7 @@ namespace MQTTnet.Server
return;
}
await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType)).ConfigureAwait(false);
await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType, endpoint)).ConfigureAwait(false);
}
catch (Exception exception)
{
@@ -108,7 +108,7 @@ namespace MQTTnet.Server
return;
}
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage)).ConfigureAwait(false);
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage, null, null)).ConfigureAwait(false);
}
catch (Exception exception)
{
+2 -4
View File
@@ -34,12 +34,10 @@ namespace MQTTnet.Server
public IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; set; }
public IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
public IMqttServerStorage Storage { get; set; }
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);
return this;
}
public MqttServerOptionsBuilder WithDisconnectedInterceptor(IMqttServerClientDisconnectedHandler value)
public MqttServerOptionsBuilder WithConnectionValidator(Func<MqttConnectionValidatorContext, Task> value)
{
_options.ClientDisconnectedInterceptor = value;
_options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(value);
return this;
}
public MqttServerOptionsBuilder WithDisconnectedInterceptor(Action<MqttServerClientDisconnectedEventArgs> value)
{
_options.ClientDisconnectedInterceptor = new MqttServerClientDisconnectedHandlerDelegate(value);
return this;
}
public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value)
{
_options.ApplicationMessageInterceptor = value;
@@ -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]
public async Task Send_Reply_For_Any_Received_Message()
{