Compare commits

..
Author SHA1 Message Date
christian 37631730e0 Enable nullable 2025-10-20 11:29:24 +02:00
Christian 5ebc5fcb7e Call enqueue interceptor for retained messages (fix) (#2212)
* Call the event handler also for retained messages

* Update release notes

* Add Unit Test
2025-10-19 21:21:47 +02:00
Christian 51979fa5a4 Call enqueue interceptor for retained messages (#2211)
* Call the event handler also for retained messages

* Update release notes
2025-10-19 19:14:33 +02:00
Christian 7ac7f256ef Update nugets (#2210)
* Upate nugets, fix breaking changes

* Fix CI build
2025-10-17 21:29:46 +02:00
e7ccd897b0 Use License file from code repo (#2197)
* Use License file from code repo

* Configure all relevant projects

* Update release notes

* Restore release notes

---------

Co-authored-by: Jens Nordenbro <jens.nordenbro@beijerelectronics.com>
Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2025-10-17 19:28:45 +02:00
Christian d5d72ac37d Enable support for AOT compilation (#2209)
* Update project files

* Update release notes
2025-10-17 19:06:28 +02:00
Christian a5ef10fddb Restore strong name (#2208)
* Restore strong name certificate

* Migrate strong name certificate loading to linux.

* Fix wrong path

* Restore version, update release notes
2025-10-17 19:05:20 +02:00
zhaowgitandChristian c97bd82334 Issues #2091 Improve the performance of RetainedMessages without subs… (#2093)
* Issues #2091 Improve the performance of RetainedMessages without subscribing to RetainedMessageChangedEvent.

* Fix build errors

* Update release notes

---------

Co-authored-by: Christian <6939810+chkr1011@users.noreply.github.com>
2025-10-17 19:04:11 +02:00
christian 75ebf116cf Merge remote-tracking branch 'origin/master' 2025-09-14 10:47:46 +02:00
christian 2f7f55e3f5 Update version 2025-09-14 10:47:32 +02:00
Christian e43e7f8e36 Use latest language features (#2176)
* Make use of language features line AsyncDispose, init etc.

* Fix build errors

* Improve Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Update samples

* Remove obsolete benchmarks

* Code cleanup

* Improve unit tests

* Improve Unit Tests

* Fix Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Restore unit tests

* Exclude load tests from unit tests
2025-08-30 10:52:59 +02:00
timyhac 247988e633 Remove "MQTTv5 only" in documentation comments for MqttClientConnectResult.IsSessionPresent and MqttClientConnectResult.ResultCode (#2166)
* Remove "MQTTv5" from documentation comments for `IsSessionPresent`

 As far as I can tell, this is available for MQTT v3.1.1

References:
* [OASIS Standard](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718035)
* [MQTTnet source code](https://github.com/dotnet/MQTTnet/blob/93a9d8d1dbe8dde2b7d79a37c873fae13b4f4797/Source/MQTTnet/Connecting/MqttClientConnectResultFactory.cs#L75)

* Remove "MQTTv5 only" for `ResultCode`
2025-03-29 19:11:53 +01:00
Youssef Victor 93a9d8d1db Move to MTP (#2163) 2025-03-02 11:59:33 +01:00
Christian 24c418cf43 Update README.md 2025-01-07 18:57:58 +01:00
Lucas Tandchristian 948c7c2b69 Corrected Cancellation Source for KeepAlive Messages (#2129)
* Corrected Cancellation Source for KeepAlive Messages

* Use half of the keep alive value for the ping timeout

* Update release notes

* Update ReleaseNotes.md

* Update ReleaseNotes.md

* Update ReleaseNotes.md

---------

Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2025-01-02 12:17:05 +01:00
christian 1cdc343e6d Update release notes 2025-01-02 12:11:57 +01:00
27771f0146 Improve session message injection (#2117)
* Improve session message injection methods

* Add more tests for session injection

* Change return type from MqttPublishPacket to MqttPacketWithIdentifier

* Use dedicated type for MattApplicationMessage injection result

* Code review minor fixes

* Apply project code style

* Update release notes

---------

Co-authored-by: Anton Smolkov <anton.smolkov@idp.zyfra.com>
Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2025-01-02 11:51:46 +01:00
christian e7de2b913a Increase version 2025-01-02 11:34:14 +01:00
christian 3dd3279656 Fix missing release notes in nuget package 2025-01-02 11:34:08 +01:00
christian c5934bf18b Reset release notes 2025-01-02 11:22:19 +01:00
375 changed files with 21931 additions and 22832 deletions
+10 -3
View File
@@ -3,7 +3,7 @@ name: CI
on: [push, pull_request] on: [push, pull_request]
env: env:
VERSION: "5.0.0.${{github.run_number}}" VERSION: "5.1.0.${{github.run_number}}"
PACKAGE_SUFFIX: "" PACKAGE_SUFFIX: ""
jobs: jobs:
@@ -21,8 +21,15 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Load strong name certificate
run: |
echo "$SNC_BASE64" | base64 --decode > "${{ github.workspace }}/certificate.snk"
shell: bash
env:
SNC_BASE64: ${{ secrets.SNC_BASE64 }}
- name: Build package - name: Build package
run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }} run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}/certificate.snk
- name: Upload nuget packages - name: Upload nuget packages
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -46,7 +53,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Execute tests - name: Execute tests
run: dotnet test --framework net8.0 Source/MQTTnet.Tests/MQTTnet.Tests.csproj run: dotnet test --framework net8.0 --configuration Release Source/MQTTnet.Tests/MQTTnet.Tests.csproj
sign: sign:
needs: build needs: build
+9
View File
@@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<EnableMSTestRunner>true</EnableMSTestRunner>
</PropertyGroup>
</Project>
+4
View File
@@ -1,4 +1,8 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToSwitchStatement/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UncatchableException/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnthrowableException/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
+2 -2
View File
@@ -3,9 +3,9 @@
<br/> <br/>
</p> </p>
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet) [![NuGet Badge](https://img.shields.io/nuget/dt/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![CI](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml) [![CI](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
[![MyGet](https://img.shields.io/myget/mqttnet/v/mqttnet?color=orange&label=MyGet-Preview)](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet) [![MyGet](https://img.shields.io/myget/mqttnet/v/mqttnet?color=orange&label=preview)](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
![Size](https://img.shields.io/github/repo-size/dotnet/MQTTnet.svg) ![Size](https://img.shields.io/github/repo-size/dotnet/MQTTnet.svg)
[![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://raw.githubusercontent.com/dotnet/MQTTnet/master/LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://raw.githubusercontent.com/dotnet/MQTTnet/master/LICENSE)
+1 -1
View File
@@ -413,7 +413,7 @@ m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
if (e.ClientWasConnected) if (e.ClientWasConnected)
{ {
// Use the current options as the new options. // Use the current options as the new options.
await mqttClient.ConnectAsync(mqttClient.Options); await mqttClient.ConnectAsync(mqttClientOptions);
} }
}; };
+2 -6
View File
@@ -23,8 +23,7 @@ public static class Client_Publish_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder() var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
@@ -42,7 +41,6 @@ public static class Client_Publish_Samples
Console.WriteLine("MQTT application message is published."); Console.WriteLine("MQTT application message is published.");
} }
}
public static async Task Publish_Multiple_Application_Messages() public static async Task Publish_Multiple_Application_Messages()
{ {
@@ -54,8 +52,7 @@ public static class Client_Publish_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder() var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
@@ -87,5 +84,4 @@ public static class Client_Publish_Samples
Console.WriteLine("MQTT application message is published."); Console.WriteLine("MQTT application message is published.");
} }
}
} }
+4 -12
View File
@@ -26,8 +26,7 @@ public static class Client_Subscribe_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
// Setup message handling before connecting so that queued messages // Setup message handling before connecting so that queued messages
@@ -52,7 +51,6 @@ public static class Client_Subscribe_Samples
Console.WriteLine("Press enter to exit."); Console.WriteLine("Press enter to exit.");
Console.ReadLine(); Console.ReadLine();
} }
}
public static async Task Send_Responses() public static async Task Send_Responses()
{ {
@@ -62,8 +60,7 @@ public static class Client_Subscribe_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args) mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
{ {
// Do some work with the message... // Do some work with the message...
@@ -92,7 +89,6 @@ public static class Client_Subscribe_Samples
// The response contains additional data sent by the server after subscribing. // The response contains additional data sent by the server after subscribing.
response.DumpToConsole(); response.DumpToConsole();
} }
}
public static async Task Subscribe_Multiple_Topics() public static async Task Subscribe_Multiple_Topics()
{ {
@@ -102,8 +98,7 @@ public static class Client_Subscribe_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -123,7 +118,6 @@ public static class Client_Subscribe_Samples
// The response contains additional data sent by the server after subscribing. // The response contains additional data sent by the server after subscribing.
response.DumpToConsole(); response.DumpToConsole();
} }
}
public static async Task Subscribe_Topic() public static async Task Subscribe_Topic()
{ {
@@ -133,8 +127,7 @@ public static class Client_Subscribe_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -148,7 +141,6 @@ public static class Client_Subscribe_Samples
// The response contains additional data sent by the server after subscribing. // The response contains additional data sent by the server after subscribing.
response.DumpToConsole(); response.DumpToConsole();
} }
}
static void ConcurrentProcessingDisableAutoAcknowledge(CancellationToken shutdownToken, IMqttClient mqttClient) static void ConcurrentProcessingDisableAutoAcknowledge(CancellationToken shutdownToken, IMqttClient mqttClient)
{ {
+3 -7
View File
@@ -26,8 +26,7 @@ public static class Logger_Samples
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("MQTT client is connected."); Console.WriteLine("MQTT client is connected.");
@@ -37,7 +36,6 @@ public static class Logger_Samples
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None); await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
} }
}
public static async Task Use_Event_Logger() public static async Task Use_Event_Logger()
{ {
@@ -50,7 +48,7 @@ public static class Logger_Samples
// The logger ID is optional but can be set do distinguish different logger instances. // The logger ID is optional but can be set do distinguish different logger instances.
var mqttEventLogger = new MqttNetEventLogger("MyCustomLogger"); var mqttEventLogger = new MqttNetEventLogger("MyCustomLogger");
mqttEventLogger.LogMessagePublished += (sender, args) => mqttEventLogger.LogMessagePublished += (_, args) =>
{ {
var output = new StringBuilder(); var output = new StringBuilder();
output.AppendLine($">> [{args.LogMessage.Timestamp:O}] [{args.LogMessage.ThreadId}] [{args.LogMessage.Source}] [{args.LogMessage.Level}]: {args.LogMessage.Message}"); output.AppendLine($">> [{args.LogMessage.Timestamp:O}] [{args.LogMessage.ThreadId}] [{args.LogMessage.Source}] [{args.LogMessage.Level}]: {args.LogMessage.Message}");
@@ -68,8 +66,7 @@ public static class Logger_Samples
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("MQTT client is connected."); Console.WriteLine("MQTT client is connected.");
@@ -79,7 +76,6 @@ public static class Logger_Samples
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None); await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
} }
}
sealed class MyLogger : IMqttNetLogger sealed class MyLogger : IMqttNetLogger
{ {
@@ -20,8 +20,7 @@ public static class PackageInspection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder() var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
@@ -37,7 +36,6 @@ public static class PackageInspection_Samples
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None); await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
} }
}
static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs) static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs)
{ {
+1
View File
@@ -15,6 +15,7 @@
<NuGetAudit>true</NuGetAudit> <NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel> <NuGetAuditLevel>low</NuGetAuditLevel>
<AnalysisLevel>latest-Recommended</AnalysisLevel> <AnalysisLevel>latest-Recommended</AnalysisLevel>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+1 -3
View File
@@ -26,8 +26,7 @@ public static class RpcClient_Samples
// The RPC client is an addon for the existing client. So we need a regular client // The RPC client is an addon for the existing client. So we need a regular client
// which is wrapped later. // which is wrapped later.
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder() var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com") .WithTcpServer("broker.hivemq.com")
.Build(); .Build();
@@ -43,7 +42,6 @@ public static class RpcClient_Samples
Console.WriteLine("The RPC call was successful."); Console.WriteLine("The RPC call was successful.");
} }
}
/* /*
* The device must respond to the request using the correct topic. The following C code shows how a * The device must respond to the request using the correct topic. The following C code shows how a
+2 -4
View File
@@ -25,8 +25,7 @@ public static class Server_Diagnostics_Samples
var mqttServerFactory = new MqttServerFactory(); var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
// Attach the event handler. // Attach the event handler.
mqttServer.ClientAcknowledgedPublishPacketAsync += e => mqttServer.ClientAcknowledgedPublishPacketAsync += e =>
{ {
@@ -37,7 +36,7 @@ public static class Server_Diagnostics_Samples
Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}"); Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}");
var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket; var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket;
Console.WriteLine($"QoS 2 reason code: {qos1AcknowledgePacket?.ReasonCode}"); Console.WriteLine($"QoS 2 reason code: {qos2AcknowledgePacket?.ReasonCode}");
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
@@ -48,5 +47,4 @@ public static class Server_Diagnostics_Samples
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
} }
@@ -25,8 +25,7 @@ public static class Server_Intercepting_Samples
var mqttServerFactory = new MqttServerFactory(); var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
mqttServer.InterceptingPublishAsync += args => mqttServer.InterceptingPublishAsync += args =>
{ {
// Here we only change the topic of the received application message. // Here we only change the topic of the received application message.
@@ -43,5 +42,4 @@ public static class Server_Intercepting_Samples
Console.ReadLine(); Console.ReadLine();
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
} }
@@ -29,8 +29,7 @@ public static class Server_Retained_Messages_Samples
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default! // Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build(); var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
using (var server = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
// Make sure that the server will load the retained messages. // Make sure that the server will load the retained messages.
server.LoadingRetainedMessageAsync += async eventArgs => server.LoadingRetainedMessageAsync += async eventArgs =>
{ {
@@ -87,7 +86,6 @@ public static class Server_Retained_Messages_Samples
Console.WriteLine("Press Enter to exit."); Console.WriteLine("Press Enter to exit.");
Console.ReadLine(); Console.ReadLine();
} }
}
sealed class MqttRetainedMessageModel sealed class MqttRetainedMessageModel
{ {
+5 -15
View File
@@ -22,8 +22,7 @@ public static class Server_Simple_Samples
* See _Run_Minimal_Server_ for more information. * See _Run_Minimal_Server_ for more information.
*/ */
using (var mqttServer = await StartMqttServer()) using var mqttServer = await StartMqttServer();
{
// Let the client connect. // Let the client connect.
await Task.Delay(TimeSpan.FromSeconds(5)); await Task.Delay(TimeSpan.FromSeconds(5));
@@ -34,7 +33,6 @@ public static class Server_Simple_Samples
await affectedClient.DisconnectAsync(); await affectedClient.DisconnectAsync();
} }
} }
}
public static async Task Publish_Message_From_Broker() public static async Task Publish_Message_From_Broker()
{ {
@@ -44,8 +42,7 @@ public static class Server_Simple_Samples
* See _Run_Minimal_Server_ for more information. * See _Run_Minimal_Server_ for more information.
*/ */
using (var mqttServer = await StartMqttServer()) using var mqttServer = await StartMqttServer();
{
// Create a new message using the builder as usual. // Create a new message using the builder as usual.
var message = new MqttApplicationMessageBuilder().WithTopic("HelloWorld").WithPayload("Test").Build(); var message = new MqttApplicationMessageBuilder().WithTopic("HelloWorld").WithPayload("Test").Build();
@@ -56,7 +53,6 @@ public static class Server_Simple_Samples
SenderClientId = "SenderClientId" SenderClientId = "SenderClientId"
}); });
} }
}
public static async Task Run_Minimal_Server() public static async Task Run_Minimal_Server()
{ {
@@ -77,8 +73,7 @@ public static class Server_Simple_Samples
// .WithDefaultEndpointPort(1234) // .WithDefaultEndpointPort(1234)
// .Build(); // .Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
await mqttServer.StartAsync(); await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit."); Console.WriteLine("Press Enter to exit.");
@@ -87,7 +82,6 @@ public static class Server_Simple_Samples
// Stop and dispose the MQTT server if it is no longer needed! // Stop and dispose the MQTT server if it is no longer needed!
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
public static async Task Run_Server_With_Logging() public static async Task Run_Server_With_Logging()
{ {
@@ -103,8 +97,7 @@ public static class Server_Simple_Samples
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
await mqttServer.StartAsync(); await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit."); Console.WriteLine("Press Enter to exit.");
@@ -113,7 +106,6 @@ public static class Server_Simple_Samples
// Stop and dispose the MQTT server if it is no longer needed! // Stop and dispose the MQTT server if it is no longer needed!
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
public static async Task Validating_Connections() public static async Task Validating_Connections()
{ {
@@ -127,8 +119,7 @@ public static class Server_Simple_Samples
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
// Setup connection validation before starting the server so that there is // Setup connection validation before starting the server so that there is
// no change to connect without valid credentials. // no change to connect without valid credentials.
mqttServer.ValidatingConnectionAsync += e => mqttServer.ValidatingConnectionAsync += e =>
@@ -158,7 +149,6 @@ public static class Server_Simple_Samples
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
static async Task<MqttServer> StartMqttServer() static async Task<MqttServer> StartMqttServer()
{ {
+3 -9
View File
@@ -31,8 +31,7 @@ public static class Server_TLS_Samples
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build(); var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions)) using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
{
await mqttServer.StartAsync(); await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit."); Console.WriteLine("Press Enter to exit.");
@@ -41,7 +40,6 @@ public static class Server_TLS_Samples
// Stop and dispose the MQTT server if it is no longer needed! // Stop and dispose the MQTT server if it is no longer needed!
await mqttServer.StopAsync(); await mqttServer.StopAsync();
} }
}
static X509Certificate2 CreateSelfSignedCertificate(string oid) static X509Certificate2 CreateSelfSignedCertificate(string oid)
{ {
@@ -50,8 +48,7 @@ public static class Server_TLS_Samples
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback); sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost"); sanBuilder.AddDnsName("localhost");
using (var rsa = RSA.Create()) using var rsa = RSA.Create();
{
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add( certRequest.CertificateExtensions.Add(
@@ -61,8 +58,7 @@ public static class Server_TLS_Samples
certRequest.CertificateExtensions.Add(sanBuilder.Build()); certRequest.CertificateExtensions.Add(sanBuilder.Build());
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10))) using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
{
var pfxCertificate = new X509Certificate2( var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx), certificate.Export(X509ContentType.Pfx),
(string)null!, (string)null!,
@@ -70,6 +66,4 @@ public static class Server_TLS_Samples
return pfxCertificate; return pfxCertificate;
} }
}
}
} }
@@ -4,11 +4,11 @@
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MQTTnet.AspTestApp.Pages namespace MQTTnet.AspTestApp.Pages;
public class IndexModel : PageModel
{ {
public class IndexModel : PageModel readonly ILogger<IndexModel> _logger;
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger) public IndexModel(ILogger<IndexModel> logger)
{ {
@@ -17,7 +17,6 @@ namespace MQTTnet.AspTestApp.Pages
public void OnGet() public void OnGet()
{ {
_logger.LogDebug("OnGet");
}
} }
} }
@@ -33,10 +33,8 @@ public static class ApplicationBuilderExtensions
} }
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>(); var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false)) using var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false);
{
await adapter.RunWebSocketConnectionAsync(webSocket, context); await adapter.RunWebSocketConnectionAsync(webSocket, context);
}
}); });
return app; return app;
@@ -4,13 +4,12 @@
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore;
public static class ConnectionBuilderExtensions
{ {
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder) public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
{ {
return builder.UseConnectionHandler<MqttConnectionHandler>(); return builder.UseConnectionHandler<MqttConnectionHandler>();
} }
}
} }
@@ -6,10 +6,10 @@ using System;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore;
public static class EndpointRouterExtensions
{ {
public static class EndpointRouterExtensions
{
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern) public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{ {
ArgumentNullException.ThrowIfNull(endpoints); ArgumentNullException.ThrowIfNull(endpoints);
@@ -19,6 +19,4 @@ namespace MQTTnet.AspNetCore
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol; options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
}); });
} }
}
} }
@@ -26,7 +26,7 @@
<PackageIcon>nuget.png</PackageIcon> <PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes> <PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers> <DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -38,13 +38,14 @@
<NuGetAuditLevel>low</NuGetAuditLevel> <NuGetAuditLevel>low</NuGetAuditLevel>
<NuGetAuditLevel>low</NuGetAuditLevel> <NuGetAuditLevel>low</NuGetAuditLevel>
<AnalysisLevel>latest-Recommended</AnalysisLevel> <AnalysisLevel>latest-Recommended</AnalysisLevel>
<IsAotCompatible>true</IsAotCompatible>
<LangVersion>default</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\Images\nuget.png"> <None Include="..\..\Images\nuget.png" Pack="true" PackagePath="" />
<Pack>True</Pack> <None Include="..\..\LICENSE" Pack="true" PackagePath="" />
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -7,10 +7,10 @@ using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter; using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore;
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{ {
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger) public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
@@ -30,5 +30,4 @@ namespace MQTTnet.AspNetCore
} }
} }
} }
}
} }
@@ -33,15 +33,13 @@ public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapte
} }
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax)); var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
using (var adapter = new MqttConnectionContext(formatter, connection)) using var adapter = new MqttConnectionContext(formatter, connection);
{
var clientHandler = ClientHandler; var clientHandler = ClientHandler;
if (clientHandler != null) if (clientHandler != null)
{ {
await clientHandler(adapter).ConfigureAwait(false); await clientHandler(adapter).ConfigureAwait(false);
} }
} }
}
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger) public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{ {
@@ -43,12 +43,10 @@ public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)); var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var channel = new MqttWebSocketChannel(webSocket, remoteEndPoint, isSecureConnection, clientCertificate); var channel = new MqttWebSocketChannel(webSocket, remoteEndPoint, isSecureConnection, clientCertificate);
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger)) using var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger);
{
await clientHandler(channelAdapter).ConfigureAwait(false); await clientHandler(channelAdapter).ConfigureAwait(false);
} }
} }
}
finally finally
{ {
clientCertificate?.Dispose(); clientCertificate?.Dispose();
+7 -13
View File
@@ -12,34 +12,28 @@ using System.Threading.Tasks;
namespace MQTTnet.AspNetCore; namespace MQTTnet.AspNetCore;
public class SocketAwaitable : ICriticalNotifyCompletion public class SocketAwaitable(PipeScheduler ioScheduler) : ICriticalNotifyCompletion
{ {
static readonly Action _callbackCompleted = () => static readonly Action CallbackCompleted = () =>
{ {
}; };
readonly PipeScheduler _ioScheduler;
int _bytesTransferred; int _bytesTransferred;
Action _callback; Action _callback;
SocketError _error; SocketError _error;
public SocketAwaitable(PipeScheduler ioScheduler) public bool IsCompleted => ReferenceEquals(_callback, CallbackCompleted);
{
_ioScheduler = ioScheduler;
}
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
public void Complete(int bytesTransferred, SocketError socketError) public void Complete(int bytesTransferred, SocketError socketError)
{ {
_error = socketError; _error = socketError;
_bytesTransferred = bytesTransferred; _bytesTransferred = bytesTransferred;
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted); var continuation = Interlocked.Exchange(ref _callback, CallbackCompleted);
if (continuation != null) if (continuation != null)
{ {
_ioScheduler.Schedule(state => ((Action)state)(), continuation); ioScheduler.Schedule(state => ((Action)state)(), continuation);
} }
} }
@@ -50,7 +44,7 @@ public class SocketAwaitable : ICriticalNotifyCompletion
public int GetResult() public int GetResult()
{ {
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted)); Debug.Assert(ReferenceEquals(_callback, CallbackCompleted));
_callback = null; _callback = null;
@@ -64,7 +58,7 @@ public class SocketAwaitable : ICriticalNotifyCompletion
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {
if (ReferenceEquals(_callback, _callbackCompleted) || ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted)) if (ReferenceEquals(_callback, CallbackCompleted) || ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), CallbackCompleted))
{ {
Task.Run(continuation); Task.Run(continuation);
} }
@@ -18,11 +18,11 @@ namespace MQTTnet.AspNetCore;
public sealed class SocketConnection : ConnectionContext public sealed class SocketConnection : ConnectionContext
{ {
readonly EndPoint _endPoint; readonly EndPoint _endPoint;
volatile bool _aborted; volatile bool _aborted;
IDuplexPipe _application; IDuplexPipe _application;
SocketReceiver _receiver; SocketReceiver _receiver;
SocketSender _sender; SocketSender _sender;
Socket _socket; Socket _socket;
public SocketConnection(EndPoint endPoint) public SocketConnection(EndPoint endPoint)
@@ -40,10 +40,13 @@ public sealed class SocketConnection : ConnectionContext
} }
public override string ConnectionId { get; set; } public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; } public override IFeatureCollection Features { get; }
public bool IsConnected { get; private set; } public bool IsConnected { get; private set; }
public override IDictionary<object, object> Items { get; set; } public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; } public override IDuplexPipe Transport { get; set; }
public override ValueTask DisposeAsync() public override ValueTask DisposeAsync()
@@ -78,7 +81,7 @@ public sealed class SocketConnection : ConnectionContext
IsConnected = true; IsConnected = true;
} }
Exception ConnectionAborted() static Exception ConnectionAborted()
{ {
return new MqttCommunicationException("Connection Aborted"); return new MqttCommunicationException("Connection Aborted");
} }
@@ -95,8 +98,7 @@ public sealed class SocketConnection : ConnectionContext
{ {
error = new MqttCommunicationException(ex); error = new MqttCommunicationException(ex);
} }
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted || ex.SocketErrorCode == SocketError.ConnectionAborted || catch (SocketException ex) when (ex.SocketErrorCode is SocketError.OperationAborted or SocketError.ConnectionAborted or SocketError.Interrupted or SocketError.InvalidArgument)
ex.SocketErrorCode == SocketError.Interrupted || ex.SocketErrorCode == SocketError.InvalidArgument)
{ {
if (!_aborted) if (!_aborted)
{ {
@@ -123,10 +125,10 @@ public sealed class SocketConnection : ConnectionContext
{ {
if (_aborted) if (_aborted)
{ {
error = error ?? ConnectionAborted(); error ??= ConnectionAborted();
} }
_application.Output.Complete(error); await _application.Output.CompleteAsync(error);
} }
} }
@@ -0,0 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: DoNotParallelize]
@@ -9,12 +9,12 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Internal; using MQTTnet.Internal;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class AsyncLockBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class AsyncLockBenchmark : BaseBenchmark
{
[Benchmark] [Benchmark]
public async Task Synchronize_100_Tasks() public async Task Synchronize_100_Tasks()
{ {
@@ -52,8 +52,7 @@ namespace MQTTnet.Benchmarks
{ {
var asyncLock = new AsyncLock(); var asyncLock = new AsyncLock();
using (var cancellationToken = new CancellationTokenSource()) using var cancellationToken = new CancellationTokenSource();
{
for (var i = 0; i < 100000; i++) for (var i = 0; i < 100000; i++)
{ {
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false)) using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
@@ -61,6 +60,4 @@ namespace MQTTnet.Benchmarks
} }
} }
} }
}
}
} }
+3 -4
View File
@@ -2,9 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
public abstract class BaseBenchmark
{ {
public abstract class BaseBenchmark
{
}
} }
@@ -12,11 +12,11 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[MemoryDiagnoser]
public sealed class ChannelAdapterBenchmark : BaseBenchmark
{ {
[MemoryDiagnoser]
public sealed class ChannelAdapterBenchmark : BaseBenchmark
{
MqttChannelAdapter _channelAdapter; MqttChannelAdapter _channelAdapter;
int _iterations; int _iterations;
MqttPublishPacket _packet; MqttPublishPacket _packet;
@@ -86,5 +86,4 @@ namespace MQTTnet.Benchmarks
return buffer.ToArray(); return buffer.ToArray();
} }
}
} }
+8 -8
View File
@@ -2,17 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Diagnostics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class LoggerBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class LoggerBenchmark : BaseBenchmark
{
MqttNetNullLogger _nullLogger; MqttNetNullLogger _nullLogger;
MqttNetSourceLogger _sourceNullLogger; MqttNetSourceLogger _sourceNullLogger;
@@ -42,7 +43,7 @@ namespace MQTTnet.Benchmarks
{ {
if (_useHandler) if (_useHandler)
{ {
eventArgs.LogMessage.ToString(); Debug.WriteLine(eventArgs.LogMessage.ToString());
} }
} }
@@ -87,5 +88,4 @@ namespace MQTTnet.Benchmarks
_sourceNullLogger.Verbose("test log message {0}", "parameter"); _sourceNullLogger.Verbose("test log message {0}", "parameter");
} }
} }
}
} }
@@ -18,7 +18,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> <PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -3,16 +3,17 @@ using BenchmarkDotNet.Jobs;
using System; using System;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class MemoryCopyBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)] const int MaxLength = 1024 * 8;
[RPlotExporter, RankColumn]
[MemoryDiagnoser] byte[] _source;
public class MemoryCopyBenchmark byte[] _target;
{
const int max_length = 1024 * 8;
private byte[] source;
private byte[] target;
[Params(64 - 1, 128 - 1, 256 - 1, 512 - 1, 1024 - 1, 2048 - 1, 5096 - 1)] [Params(64 - 1, 128 - 1, 256 - 1, 512 - 1, 1024 - 1, 2048 - 1, 5096 - 1)]
public int Length { get; set; } public int Length { get; set; }
@@ -20,21 +21,20 @@ namespace MQTTnet.Benchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
source = new byte[max_length]; _source = new byte[MaxLength];
target = new byte[max_length]; _target = new byte[MaxLength];
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Array_Copy() public void Array_Copy()
{ {
Array.Copy(source, 0, target, 0, Length); Array.Copy(_source, 0, _target, 0, Length);
} }
[Benchmark] [Benchmark]
public void Memory_Copy() public void Memory_Copy()
{ {
MQTTnet.Internal.MqttMemoryHelper.Copy(source, 0, target, 0, Length); Internal.MqttMemoryHelper.Copy(_source, 0, _target, 0, Length);
} }
}
} }
@@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@@ -23,22 +22,19 @@ public class MessageDeliveryBenchmark : BaseBenchmark
int _messagesExpectedCount; int _messagesExpectedCount;
int _messagesReceivedCount; int _messagesReceivedCount;
Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName; Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName;
MqttServer _mqttServer; MqttServer _mqttServer;
List<IMqttClient> _mqttSubscriberClients; List<IMqttClient> _mqttSubscriberClients;
[Params(1000, 10000)] public int _numPublishers;
[Params(5, 10, 20, 50)] public int _numSubscribedTopicsPerSubscriber;
[Params(10)] public int _numSubscribers;
[Params(1, 5)] public int _numTopicsPerPublisher;
Dictionary<string, string> _publisherByTopic; Dictionary<string, string> _publisherByTopic;
List<MqttApplicationMessage> _topicPublishMessages;
Dictionary<string, List<string>> _topicsByPublisher; Dictionary<string, List<string>> _topicsByPublisher;
[Params(1000, 10000)] public int NumPublishers;
[Params(5, 10, 20, 50)] public int NumSubscribedTopicsPerSubscriber;
[Params(10)] public int NumSubscribers;
[Params(1, 5)] public int NumTopicsPerPublisher;
[GlobalCleanup] [GlobalCleanup]
public void Cleanup() public void Cleanup()
{ {
@@ -64,14 +60,11 @@ public class MessageDeliveryBenchmark : BaseBenchmark
_mqttServer = null; _mqttServer = null;
} }
/// <summary>
/// Publish messages and wait for messages sent to subscribers
/// </summary>
[Benchmark] [Benchmark]
public void DeliverMessages() public void DeliverMessages()
{ {
// There should be one message received per publish for each subscribed topic // There should be one message received per publish for each subscribed topic
_messagesExpectedCount = NumSubscribedTopicsPerSubscriber * NumSubscribers; _messagesExpectedCount = _numSubscribedTopicsPerSubscriber * _numSubscribers;
// Loop for a while and exchange messages // Loop for a while and exchange messages
@@ -82,8 +75,6 @@ public class MessageDeliveryBenchmark : BaseBenchmark
// same payload for all messages // same payload for all messages
var payload = new byte[] { 1, 2, 3, 4 }; var payload = new byte[] { 1, 2, 3, 4 };
var tasks = new List<Task>();
// publish a message for each subscribed topic // publish a message for each subscribed topic
foreach (var topic in _allSubscribedTopics) foreach (var topic in _allSubscribedTopics)
{ {
@@ -101,35 +92,23 @@ public class MessageDeliveryBenchmark : BaseBenchmark
} }
catch catch
{ {
// Ignore all errors.
} }
_cancellationTokenSource.Dispose(); _cancellationTokenSource.Dispose();
if (_messagesReceivedCount < _messagesExpectedCount) if (_messagesReceivedCount < _messagesExpectedCount)
{ {
throw new Exception(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount)); throw new Exception($"Messages Received Count mismatch, expected {_messagesExpectedCount}, received {_messagesReceivedCount}");
} }
} }
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
_lockMsgCount = new object(); _lockMsgCount = new object();
Dictionary<string, List<string>> singleWildcardTopicsByPublisher; TopicGenerator.Generate(_numPublishers, _numTopicsPerPublisher, out _topicsByPublisher, out _, out _);
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
var topics = _topicsByPublisher.First().Value;
_topicPublishMessages = new List<MqttApplicationMessage>();
// Prepare messages, same for each publisher
foreach (var topic in topics)
{
var message = new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }).Build();
_topicPublishMessages.Add(message);
}
// Create server // Create server
var serverFactory = new MqttServerFactory(); var serverFactory = new MqttServerFactory();
@@ -139,7 +118,7 @@ public class MessageDeliveryBenchmark : BaseBenchmark
_mqttServer.StartAsync().GetAwaiter().GetResult(); _mqttServer.StartAsync().GetAwaiter().GetResult();
// Create publisher clients // Create publisher clients
_mqttPublisherClientsByPublisherName = new Dictionary<string, IMqttClient>(); _mqttPublisherClientsByPublisherName = [];
foreach (var pt in _topicsByPublisher) foreach (var pt in _topicsByPublisher)
{ {
var publisherName = pt.Key; var publisherName = pt.Key;
@@ -151,13 +130,13 @@ public class MessageDeliveryBenchmark : BaseBenchmark
// Create subscriber clients // Create subscriber clients
_mqttSubscriberClients = new List<IMqttClient>(); _mqttSubscriberClients = new List<IMqttClient>();
for (var i = 0; i < NumSubscribers; i++) for (var i = 0; i < _numSubscribers; i++)
{ {
var mqttSubscriberClient = clientFactory.CreateMqttClient(); var mqttSubscriberClient = clientFactory.CreateMqttClient();
_mqttSubscriberClients.Add(mqttSubscriberClient); _mqttSubscriberClients.Add(mqttSubscriberClient);
var subscriberOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId("subscriber" + i).Build(); var subscriberOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId("subscriber" + i).Build();
mqttSubscriberClient.ApplicationMessageReceivedAsync += r => mqttSubscriberClient.ApplicationMessageReceivedAsync += _ =>
{ {
// count messages and signal cancellation when expected message count is reached // count messages and signal cancellation when expected message count is reached
lock (_lockMsgCount) lock (_lockMsgCount)
@@ -171,12 +150,13 @@ public class MessageDeliveryBenchmark : BaseBenchmark
return Task.CompletedTask; return Task.CompletedTask;
}; };
mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult(); mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult();
} }
var allTopics = new List<string>(); var allTopics = new List<string>();
_publisherByTopic = new Dictionary<string, string>(); _publisherByTopic = [];
foreach (var t in _topicsByPublisher) foreach (var t in _topicsByPublisher)
{ {
foreach (var topic in t.Value) foreach (var topic in t.Value)
@@ -189,21 +169,18 @@ public class MessageDeliveryBenchmark : BaseBenchmark
// Subscribe to NumSubscribedTopics topics spread across all topics // Subscribe to NumSubscribedTopics topics spread across all topics
_allSubscribedTopics = new List<string>(); _allSubscribedTopics = new List<string>();
var totalNumTopics = NumPublishers * NumTopicsPerPublisher; var totalNumTopics = _numPublishers * _numTopicsPerPublisher;
var topicIndexStep = totalNumTopics / (NumSubscribedTopicsPerSubscriber * NumSubscribers); var topicIndexStep = totalNumTopics / (_numSubscribedTopicsPerSubscriber * _numSubscribers);
if (topicIndexStep * NumSubscribedTopicsPerSubscriber * NumSubscribers != totalNumTopics) if (topicIndexStep * _numSubscribedTopicsPerSubscriber * _numSubscribers != totalNumTopics)
{ {
throw new Exception( throw new Exception(
string.Format( $"The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {totalNumTopics}, topic step: {topicIndexStep}");
"The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {0}, topic step: {1}",
totalNumTopics,
topicIndexStep));
} }
var topicIndex = 0; var topicIndex = 0;
foreach (var mqttSubscriber in _mqttSubscriberClients) foreach (var mqttSubscriber in _mqttSubscriberClients)
{ {
for (var i = 0; i < NumSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep) for (var i = 0; i < _numSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep)
{ {
var topic = allTopics[topicIndex]; var topic = allTopics[topicIndex];
_allSubscribedTopics.Add(topic); _allSubscribedTopics.Add(topic);
@@ -9,12 +9,12 @@ using Microsoft.AspNetCore.Hosting;
using MQTTnet.AspNetCore; using MQTTnet.AspNetCore;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MessageProcessingMqttConnectionContextBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MessageProcessingMqttConnectionContextBenchmark : BaseBenchmark
{
IWebHost _host; IWebHost _host;
IMqttClient _mqttClient; IMqttClient _mqttClient;
MqttApplicationMessage _message; MqttApplicationMessage _message;
@@ -30,7 +30,7 @@ namespace MQTTnet.Benchmarks
.AddMqttConnectionHandler(); .AddMqttConnectionHandler();
}) })
.Configure(app => { .Configure(app => {
app.UseMqttServer(s => { app.UseMqttServer(_ => {
}); });
}) })
@@ -69,5 +69,4 @@ namespace MQTTnet.Benchmarks
_mqttClient.PublishAsync(_message).GetAwaiter().GetResult(); _mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
} }
} }
}
} }
@@ -8,12 +8,12 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Formatter; using MQTTnet.Formatter;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttBufferReaderBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttBufferReaderBenchmark
{
byte[] _buffer; byte[] _buffer;
int _bufferLength; int _bufferLength;
@@ -39,5 +39,4 @@ namespace MQTTnet.Benchmarks
{ {
Encoding.UTF8.GetString(_buffer, 0, _bufferLength); Encoding.UTF8.GetString(_buffer, 0, _bufferLength);
} }
}
} }
@@ -8,12 +8,12 @@ using BenchmarkDotNet.Jobs;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttPacketReaderWriterBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttPacketReaderWriterBenchmark : BaseBenchmark
{
readonly byte[] _demoPayload = new byte[1024]; readonly byte[] _demoPayload = new byte[1024];
byte[] _readPayload; byte[] _readPayload;
@@ -92,5 +92,4 @@ namespace MQTTnet.Benchmarks
writer.Reset(0); writer.Reset(0);
} }
} }
}
} }
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Buffers; using System.Buffers;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
+3 -6
View File
@@ -5,14 +5,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
public static class Program
{ {
public static class Program
{
static int _selectedBenchmarkIndex; static int _selectedBenchmarkIndex;
static List<Type> _benchmarks; static List<Type> _benchmarks;
@@ -138,5 +136,4 @@ namespace MQTTnet.Benchmarks
Console.WriteLine(); Console.WriteLine();
} }
}
} }
@@ -1,31 +1,27 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore; using MQTTnet.AspNetCore;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets;
using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class ReaderExtensionsBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)] MqttPacketFormatterAdapter _mqttPacketFormatter;
[RPlotExporter, RankColumn] MemoryStream _stream;
[MemoryDiagnoser]
public class ReaderExtensionsBenchmark
{
MqttPacketFormatterAdapter mqttPacketFormatter;
MemoryStream stream;
[GlobalSetup] [GlobalSetup]
public void GlobalSetup() public void GlobalSetup()
{ {
mqttPacketFormatter = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535)); _mqttPacketFormatter = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var mqttMessage = new MqttApplicationMessageBuilder() var mqttMessage = new MqttApplicationMessageBuilder()
.WithTopic("topic") .WithTopic("topic")
.WithPayload(new byte[10 * 1024]) .WithPayload(new byte[10 * 1024])
@@ -33,66 +29,18 @@ namespace MQTTnet.Benchmarks
var packet = MqttPublishPacketFactory.Create(mqttMessage); var packet = MqttPublishPacketFactory.Create(mqttMessage);
var buffer = mqttPacketFormatter.Encode(packet); var buffer = _mqttPacketFormatter.Encode(packet);
stream = new MemoryStream(); _stream = new MemoryStream();
stream.Write(buffer.Packet); _stream.Write(buffer.Packet);
stream.Write(buffer.Payload.ToArray()); _stream.Write(buffer.Payload.ToArray());
mqttPacketFormatter.Cleanup(); _mqttPacketFormatter.Cleanup();
}
[Benchmark(Baseline = true)]
public async Task Before()
{
stream.Position = 0;
var input = PipeReader.Create(stream);
while (true)
{
ReadResult readResult;
var readTask = input.ReadAsync();
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}
var buffer = readResult.Buffer;
var consumed = buffer.Start;
var observed = buffer.Start;
try
{
if (!buffer.IsEmpty)
{
if (ReaderExtensions_Before.TryDecode(mqttPacketFormatter, buffer, out var packet, out consumed, out observed, out var received))
{
break;
}
}
else if (readResult.IsCompleted)
{
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
input.AdvanceTo(consumed, observed);
}
}
} }
[Benchmark] [Benchmark]
public async Task After() public async Task After()
{ {
stream.Position = 0; _stream.Position = 0;
var input = PipeReader.Create(stream); var input = PipeReader.Create(_stream);
while (true) while (true)
{ {
@@ -116,7 +64,7 @@ namespace MQTTnet.Benchmarks
{ {
if (!buffer.IsEmpty) if (!buffer.IsEmpty)
{ {
if (ReaderExtensions.TryDecode(mqttPacketFormatter, buffer, out var packet, out consumed, out observed, out var received)) if (_mqttPacketFormatter.TryDecode(buffer, out _, out consumed, out observed, out _))
{ {
break; break;
} }
@@ -135,106 +83,4 @@ namespace MQTTnet.Benchmarks
} }
} }
} }
public static class ReaderExtensions_Before
{
public static bool TryDecode(MqttPacketFormatterAdapter formatter,
in ReadOnlySequence<byte> input,
out MqttPacket packet,
out SequencePosition consumed,
out SequencePosition observed,
out int bytesRead)
{
ArgumentNullException.ThrowIfNull(formatter);
packet = null;
consumed = input.Start;
observed = input.End;
bytesRead = 0;
var copy = input;
if (copy.Length < 2)
{
return false;
}
var fixedHeader = copy.First.Span[0];
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
{
return false;
}
if (copy.Length < bodyLength)
{
return false;
}
var bodySlice = copy.Slice(0, bodyLength);
var buffer = GetMemory(bodySlice).ToArray();
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, new ArraySegment<byte>(buffer, 0, buffer.Length), buffer.Length + 2);
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
formatter.DetectProtocolVersion(receivedMqttPacket);
}
packet = formatter.Decode(receivedMqttPacket);
consumed = bodySlice.End;
observed = bodySlice.End;
bytesRead = headerLength + bodyLength;
return true;
}
static ReadOnlyMemory<byte> GetMemory(in ReadOnlySequence<byte> input)
{
if (input.IsSingleSegment)
{
return input.First;
}
// Should be rare
return input.ToArray();
}
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
{
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
var multiplier = 1;
var value = 0;
byte encodedByte;
var index = 1;
headerLength = 0;
bodyLength = 0;
var temp = GetMemory(input.Slice(0, Math.Min(5, input.Length))).Span;
do
{
if (index == temp.Length)
{
return false;
}
encodedByte = temp[index];
index++;
value += (byte)(encodedByte & 127) * multiplier;
if (multiplier > 128 * 128 * 128)
{
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
}
multiplier *= 128;
} while ((encodedByte & 128) != 0);
input = input.Slice(index);
headerLength = index;
bodyLength = value;
return true;
}
}
}
} }
@@ -3,13 +3,13 @@ using BenchmarkDotNet.Jobs;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
using MQTTnet.Tests.Server; using MQTTnet.Tests.Server;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class RoundtripProcessingBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class RoundtripProcessingBenchmark : BaseBenchmark
{
[GlobalSetup] [GlobalSetup]
public void GlobalSetup() public void GlobalSetup()
{ {
@@ -24,7 +24,6 @@ namespace MQTTnet.Benchmarks
[Benchmark] [Benchmark]
public void Handle_100_000_Messages_In_Receiving_Client() public void Handle_100_000_Messages_In_Receiving_Client()
{ {
new Load_Tests().Handle_100_000_Messages_In_Receiving_Client().GetAwaiter().GetResult(); //new Load_Tests().Handle_100_000_Messages_In_Receiving_Client().GetAwaiter().GetResult();
}
} }
} }
@@ -7,52 +7,51 @@ using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class SendPacketAsyncBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)] MemoryStream _stream;
[RPlotExporter, RankColumn] MqttPacketBuffer _buffer;
[MemoryDiagnoser]
public class SendPacketAsyncBenchmark : BaseBenchmark
{
MemoryStream stream;
MqttPacketBuffer buffer;
[GlobalSetup] [GlobalSetup]
public void GlobalSetup() public void GlobalSetup()
{ {
stream = new MemoryStream(1024); _stream = new MemoryStream(1024);
var packet = new ArraySegment<byte>(new byte[10]); var packet = new ArraySegment<byte>(new byte[10]);
buffer = new MqttPacketBuffer(packet); _buffer = new MqttPacketBuffer(packet);
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public async ValueTask Before() public async ValueTask Before()
{ {
stream.Position = 0; _stream.Position = 0;
var output = PipeWriter.Create(stream); var output = PipeWriter.Create(_stream);
WritePacketBuffer(output, buffer); WritePacketBuffer(output, _buffer);
await output.FlushAsync(); await output.FlushAsync();
} }
[Benchmark] [Benchmark]
public async ValueTask After() public async ValueTask After()
{ {
stream.Position = 0; _stream.Position = 0;
var output = PipeWriter.Create(stream); var output = PipeWriter.Create(_stream);
if (buffer.Payload.Length == 0) if (_buffer.Payload.Length == 0)
{ {
await output.WriteAsync(buffer.Packet).ConfigureAwait(false); await output.WriteAsync(_buffer.Packet).ConfigureAwait(false);
} }
else else
{ {
WritePacketBuffer(output, buffer); WritePacketBuffer(output, _buffer);
await output.FlushAsync().ConfigureAwait(false); await output.FlushAsync().ConfigureAwait(false);
} }
} }
static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer) static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
{ {
// copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter // copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
@@ -61,9 +60,8 @@ namespace MQTTnet.Benchmarks
var span = output.GetSpan(buffer.Length); var span = output.GetSpan(buffer.Length);
buffer.Packet.AsSpan().CopyTo(span); buffer.Packet.AsSpan().CopyTo(span);
buffer.Payload.CopyTo(span.Slice(buffer.Packet.Count)); buffer.Payload.CopyTo(span[buffer.Packet.Count..]);
output.Advance(buffer.Length); output.Advance(buffer.Length);
} }
}
} }
@@ -17,13 +17,13 @@ using MQTTnet.Diagnostics.Logger;
using System.Buffers; using System.Buffers;
using System.Net; using System.Net;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class SerializerBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class SerializerBenchmark : BaseBenchmark
{
MqttPacket _packet; MqttPacket _packet;
ArraySegment<byte> _serializedPacket; ArraySegment<byte> _serializedPacket;
IMqttPacketFormatter _serializer; IMqttPacketFormatter _serializer;
@@ -62,7 +62,7 @@ namespace MQTTnet.Benchmarks
{ {
channel.Reset(); channel.Reset();
var header = reader.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); reader.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
} }
} }
@@ -81,7 +81,7 @@ namespace MQTTnet.Benchmarks
public bool IsSecureConnection { get; } = false; public bool IsSecureConnection { get; } = false;
public X509Certificate2 ClientCertificate { get; } public X509Certificate2 ClientCertificate { get; set; }
public void Reset() public void Reset()
{ {
@@ -100,7 +100,7 @@ namespace MQTTnet.Benchmarks
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
Array.Copy(_buffer.Array, _position, buffer, offset, count); Array.Copy(_buffer!.Array!, _position, buffer, offset, count);
_position += count; _position += count;
return Task.FromResult(count); return Task.FromResult(count);
@@ -115,5 +115,4 @@ namespace MQTTnet.Benchmarks
{ {
} }
} }
}
} }
@@ -7,13 +7,13 @@ using BenchmarkDotNet.Jobs;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
using MQTTnet.Tests.Server; using MQTTnet.Tests.Server;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class ServerProcessingBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class ServerProcessingBenchmark : BaseBenchmark
{
[GlobalSetup] [GlobalSetup]
public void GlobalSetup() public void GlobalSetup()
{ {
@@ -28,13 +28,12 @@ namespace MQTTnet.Benchmarks
[Benchmark] [Benchmark]
public void Handle_100_000_Messages_In_Server_MqttClient() public void Handle_100_000_Messages_In_Server_MqttClient()
{ {
new Load_Tests().Handle_100_000_Messages_In_Server().GetAwaiter().GetResult(); //new Load_Tests().Handle_100_000_Messages_In_Server().GetAwaiter().GetResult();
} }
//[Benchmark] [Benchmark]
public void Handle_100_000_Messages_In_Server_LowLevelMqttClient() public void Handle_100_000_Messages_In_Server_LowLevelMqttClient()
{ {
new Load_Tests().Handle_100_000_Messages_In_Low_Level_Client().GetAwaiter().GetResult(); //new Load_Tests().Handle_100_000_Messages_In_Low_Level_Client().GetAwaiter().GetResult();
}
} }
} }
@@ -30,7 +30,7 @@ public class SubscribeBenchmark : BaseBenchmark
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher); TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out _, out _);
_topics = topicsByPublisher.Values.First(); _topics = topicsByPublisher.Values.First();
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
@@ -11,12 +11,12 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.AspNetCore; using MQTTnet.AspNetCore;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class TcpPipesBenchmark : BaseBenchmark
{ {
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class TcpPipesBenchmark : BaseBenchmark
{
IDuplexPipe _client; IDuplexPipe _client;
IDuplexPipe _server; IDuplexPipe _server;
@@ -76,5 +76,4 @@ namespace MQTTnet.Benchmarks
await output.WriteAsync(new byte[size], CancellationToken.None).ConfigureAwait(false); await output.WriteAsync(new byte[size], CancellationToken.None).ConfigureAwait(false);
} }
} }
}
} }
@@ -2,19 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
{
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class TopicFilterComparerBenchmark : BaseBenchmark
{
static readonly char[] TopicLevelSeparator = { '/' };
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class TopicFilterComparerBenchmark : BaseBenchmark
{
readonly string _longTopic = readonly string _longTopic =
"AAAAAAAAAAAAAssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"; "AAAAAAAAAAAAAssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
@@ -33,238 +30,4 @@ namespace MQTTnet.Benchmarks
MqttTopicFilterComparer.Compare(_longTopic, _longTopic); MqttTopicFilterComparer.Compare(_longTopic, _longTopic);
} }
} }
[Benchmark]
public void MqttTopicFilterComparer_10000_LoopMethod_Without_Pointer()
{
for (var i = 0; i < 100000; i++)
{
MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1", "sport/#");
MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1/ranking", "sport/#/ranking");
MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1/score/wimbledon", "sport/+/player1/#");
MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1", "sport/tennis/+");
MqttTopicFilterComparerWithoutPointer.Compare("/finance", "+/+");
MqttTopicFilterComparerWithoutPointer.Compare("/finance", "/+");
MqttTopicFilterComparerWithoutPointer.Compare("/finance", "+");
MqttTopicFilterComparerWithoutPointer.Compare(_longTopic, _longTopic);
}
}
[Benchmark]
public void MqttTopicFilterComparer_10000_StringSplitMethod()
{
for (var i = 0; i < 100000; i++)
{
LegacyMethodByStringSplit("sport/tennis/player1", "sport/#");
LegacyMethodByStringSplit("sport/tennis/player1/ranking", "sport/#/ranking");
LegacyMethodByStringSplit("sport/tennis/player1/score/wimbledon", "sport/+/player1/#");
LegacyMethodByStringSplit("sport/tennis/player1", "sport/tennis/+");
LegacyMethodByStringSplit("/finance", "+/+");
LegacyMethodByStringSplit("/finance", "/+");
LegacyMethodByStringSplit("/finance", "+");
MqttTopicFilterComparer.Compare(_longTopic, _longTopic);
}
}
[GlobalSetup]
public void Setup()
{
}
static bool LegacyMethodByStringSplit(string topic, string filter)
{
ArgumentNullException.ThrowIfNull(topic);
ArgumentNullException.ThrowIfNull(filter);
if (string.Equals(topic, filter, StringComparison.Ordinal))
{
return true;
}
var fragmentsTopic = topic.Split(TopicLevelSeparator, StringSplitOptions.None);
var fragmentsFilter = filter.Split(TopicLevelSeparator, StringSplitOptions.None);
// # > In either case it MUST be the last character specified in the Topic Filter [MQTT-4.7.1-2].
for (var i = 0; i < fragmentsFilter.Length; i++)
{
if (fragmentsFilter[i] == "+")
{
continue;
}
if (fragmentsFilter[i] == "#")
{
return true;
}
if (i >= fragmentsTopic.Length)
{
return false;
}
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i], StringComparison.Ordinal))
{
return false;
}
}
return fragmentsTopic.Length == fragmentsFilter.Length;
}
public static class MqttTopicFilterComparerWithoutPointer
{
public const char LevelSeparator = '/';
public const char MultiLevelWildcard = '#';
public const char SingleLevelWildcard = '+';
public const char ReservedTopicPrefix = '$';
public static MqttTopicFilterCompareResult Compare(string topic, string filter)
{
if (string.IsNullOrEmpty(topic))
{
return MqttTopicFilterCompareResult.TopicInvalid;
}
if (string.IsNullOrEmpty(filter))
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
var filterOffset = 0;
var filterLength = filter.Length;
var topicOffset = 0;
var topicLength = topic.Length;
var topicPointer = topic;
var filterPointer = filter;
var isMultiLevelFilter = filterPointer[filterLength - 1] == MultiLevelWildcard;
var isReservedTopic = topicPointer[0] == ReservedTopicPrefix;
if (isReservedTopic && filterLength == 1 && isMultiLevelFilter)
{
// It is not allowed to receive i.e. '$foo/bar' with filter '#'.
return MqttTopicFilterCompareResult.NoMatch;
}
if (isReservedTopic && filterPointer[0] == SingleLevelWildcard)
{
// It is not allowed to receive i.e. '$SYS/monitor/Clients' with filter '+/monitor/Clients'.
return MqttTopicFilterCompareResult.NoMatch;
}
if (filterLength == 1 && isMultiLevelFilter)
{
// Filter '#' matches basically everything.
return MqttTopicFilterCompareResult.IsMatch;
}
// Go through the filter char by char.
while (filterOffset < filterLength && topicOffset < topicLength)
{
// Check if the current char is a multi level wildcard. The char is only allowed
// at the very las position.
if (filterPointer[filterOffset] == MultiLevelWildcard && filterOffset != filterLength - 1)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
if (filterPointer[filterOffset] == topicPointer[topicOffset])
{
if (topicOffset == topicLength - 1)
{
// Check for e.g. "foo" matching "foo/#"
if (filterOffset == filterLength - 3 && filterPointer[filterOffset + 1] == LevelSeparator && isMultiLevelFilter)
{
return MqttTopicFilterCompareResult.IsMatch;
}
// Check for e.g. "foo/" matching "foo/#"
if (filterOffset == filterLength - 2 && filterPointer[filterOffset] == LevelSeparator && isMultiLevelFilter)
{
return MqttTopicFilterCompareResult.IsMatch;
}
}
filterOffset++;
topicOffset++;
// Check if the end was reached and i.e. "foo/bar" matches "foo/bar"
if (filterOffset == filterLength && topicOffset == topicLength)
{
return MqttTopicFilterCompareResult.IsMatch;
}
var endOfTopic = topicOffset == topicLength;
if (endOfTopic && filterOffset == filterLength - 1 && filterPointer[filterOffset] == SingleLevelWildcard)
{
if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
return MqttTopicFilterCompareResult.IsMatch;
}
}
else
{
if (filterPointer[filterOffset] == SingleLevelWildcard)
{
// Check for invalid "+foo" or "a/+foo" subscription
if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
// Check for bad "foo+" or "foo+/a" subscription
if (filterOffset < filterLength - 1 && filterPointer[filterOffset + 1] != LevelSeparator)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
filterOffset++;
while (topicOffset < topicLength && topicPointer[topicOffset] != LevelSeparator)
{
topicOffset++;
}
if (topicOffset == topicLength && filterOffset == filterLength)
{
return MqttTopicFilterCompareResult.IsMatch;
}
}
else if (filterPointer[filterOffset] == MultiLevelWildcard)
{
if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
if (filterOffset + 1 != filterLength)
{
return MqttTopicFilterCompareResult.FilterInvalid;
}
return MqttTopicFilterCompareResult.IsMatch;
}
else
{
// Check for e.g. "foo/bar" matching "foo/+/#".
if (filterOffset > 0 && filterOffset + 2 == filterLength && topicOffset == topicLength && filterPointer[filterOffset - 1] == SingleLevelWildcard &&
filterPointer[filterOffset] == LevelSeparator && isMultiLevelFilter)
{
return MqttTopicFilterCompareResult.IsMatch;
}
return MqttTopicFilterCompareResult.NoMatch;
}
}
}
return MqttTopicFilterCompareResult.NoMatch;
}
}
}
} }
+3 -4
View File
@@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks;
public class TopicGenerator
{ {
public class TopicGenerator
{
public static void Generate( public static void Generate(
int numPublishers, int numPublishers,
int numTopicsPerPublisher, int numTopicsPerPublisher,
@@ -80,5 +80,4 @@ namespace MQTTnet.Benchmarks
topicList.Add(topic); topicList.Add(topic);
} }
}
} }
@@ -14,15 +14,15 @@ public class UnsubscribeBenchmark : BaseBenchmark
{ {
const int NumPublishers = 1; const int NumPublishers = 1;
const int NumTopicsPerPublisher = 10000; const int NumTopicsPerPublisher = 10000;
IMqttClient _mqttClient; IMqttClient _mqttClient;
MqttServer _mqttServer; MqttServer _mqttServer;
List<string> _topics; List<string> _topics;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher); TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out _, out _);
_topics = topicsByPublisher.Values.First(); _topics = topicsByPublisher.Values.First();
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build(); var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
@@ -4,10 +4,10 @@
using System; using System;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
{ {
public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
{
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
{ {
ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(context);
@@ -26,5 +26,4 @@ namespace MQTTnet.Extensions.Rpc
ResponseTopic = responseTopic ResponseTopic = responseTopic
}; };
} }
}
} }
@@ -8,12 +8,16 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
{
public interface IMqttRpcClient : IDisposable
{
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string,object> parameters = null);
Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default); public interface IMqttRpcClient : IDisposable
} {
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object>? parameters = null);
Task<byte[]> ExecuteAsync(
string methodName,
byte[] payload,
MqttQualityOfServiceLevel qualityOfServiceLevel,
IDictionary<string, object>? parameters = null,
CancellationToken cancellationToken = default);
} }
@@ -2,10 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public interface IMqttRpcClientTopicGenerationStrategy
{ {
public interface IMqttRpcClientTopicGenerationStrategy
{
MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context); MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context);
}
} }
@@ -26,7 +26,7 @@
<PackageIcon>nuget.png</PackageIcon> <PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes> <PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers> <DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -36,13 +36,14 @@
<NuGetAudit>true</NuGetAudit> <NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel> <NuGetAuditLevel>low</NuGetAuditLevel>
<AnalysisLevel>latest-Recommended</AnalysisLevel> <AnalysisLevel>latest-Recommended</AnalysisLevel>
<IsAotCompatible>true</IsAotCompatible>
<LangVersion>default</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\Images\nuget.png"> <None Include="..\..\Images\nuget.png" Pack="true" PackagePath="" />
<Pack>True</Pack> <None Include="..\..\LICENSE" Pack="true" PackagePath="" />
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -4,10 +4,10 @@
using System; using System;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public static class MqttFactoryExtensions
{ {
public static class MqttFactoryExtensions
{
public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory clientFactory, IMqttClient mqttClient) public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory clientFactory, IMqttClient mqttClient)
{ {
return clientFactory.CreateMqttRpcClient( return clientFactory.CreateMqttRpcClient(
@@ -25,5 +25,4 @@ namespace MQTTnet.Extensions.Rpc
return new MqttRpcClient(mqttClient, rpcClientOptions); return new MqttRpcClient(mqttClient, rpcClientOptions);
} }
}
} }
+19 -14
View File
@@ -6,7 +6,6 @@ using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
@@ -14,14 +13,14 @@ using MQTTnet.Formatter;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public sealed class MqttRpcClient : IMqttRpcClient
{ {
public sealed class MqttRpcClient : IMqttRpcClient
{
readonly IMqttClient _mqttClient; readonly IMqttClient _mqttClient;
readonly MqttRpcClientOptions _options; readonly MqttRpcClientOptions _options;
readonly ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>>(); readonly ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>> _waitingCalls = new();
public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options) public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options)
{ {
@@ -43,10 +42,14 @@ namespace MQTTnet.Extensions.Rpc
_waitingCalls.Clear(); _waitingCalls.Clear();
} }
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null) public async Task<byte[]> ExecuteAsync(
{ TimeSpan timeout,
using (var timeoutToken = new CancellationTokenSource(timeout)) string methodName,
byte[] payload,
MqttQualityOfServiceLevel qualityOfServiceLevel,
IDictionary<string, object>? parameters = null)
{ {
using var timeoutToken = new CancellationTokenSource(timeout);
try try
{ {
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, parameters, timeoutToken.Token).ConfigureAwait(false); return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, parameters, timeoutToken.Token).ConfigureAwait(false);
@@ -61,9 +64,13 @@ namespace MQTTnet.Extensions.Rpc
throw; throw;
} }
} }
}
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default) public async Task<byte[]> ExecuteAsync(
string methodName,
byte[] payload,
MqttQualityOfServiceLevel qualityOfServiceLevel,
IDictionary<string, object>? parameters = null,
CancellationToken cancellationToken = default)
{ {
ArgumentNullException.ThrowIfNull(methodName); ArgumentNullException.ThrowIfNull(methodName);
@@ -85,7 +92,7 @@ namespace MQTTnet.Extensions.Rpc
var requestMessageBuilder = new MqttApplicationMessageBuilder().WithTopic(requestTopic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel); var requestMessageBuilder = new MqttApplicationMessageBuilder().WithTopic(requestTopic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel);
if (_mqttClient.Options.ProtocolVersion == MqttProtocolVersion.V500) if (_mqttClient.Options!.ProtocolVersion == MqttProtocolVersion.V500)
{ {
requestMessageBuilder.WithResponseTopic(responseTopic); requestMessageBuilder.WithResponseTopic(responseTopic);
} }
@@ -106,8 +113,7 @@ namespace MQTTnet.Extensions.Rpc
await _mqttClient.SubscribeAsync(subscribeOptions, cancellationToken).ConfigureAwait(false); await _mqttClient.SubscribeAsync(subscribeOptions, cancellationToken).ConfigureAwait(false);
await _mqttClient.PublishAsync(requestMessage, cancellationToken).ConfigureAwait(false); await _mqttClient.PublishAsync(requestMessage, cancellationToken).ConfigureAwait(false);
using (cancellationToken.Register( using (cancellationToken.Register(() =>
() =>
{ {
awaitable.TrySetCanceled(); awaitable.TrySetCanceled();
})) }))
@@ -137,5 +143,4 @@ namespace MQTTnet.Extensions.Rpc
return CompletedTask.Instance; return CompletedTask.Instance;
} }
}
} }
@@ -8,11 +8,11 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public static class MqttRpcClientExtensions
{ {
public static class MqttRpcClientExtensions public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string,object>? parameters = null)
{
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string,object> parameters = null)
{ {
if (client == null) throw new ArgumentNullException(nameof(client)); if (client == null) throw new ArgumentNullException(nameof(client));
@@ -20,5 +20,4 @@ namespace MQTTnet.Extensions.Rpc
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel, parameters); return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel, parameters);
} }
}
} }
@@ -2,10 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public sealed class MqttRpcClientOptions
{ {
public sealed class MqttRpcClientOptions
{
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy(); public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
}
} }
@@ -4,10 +4,10 @@
using System; using System;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public sealed class MqttRpcClientOptionsBuilder
{ {
public sealed class MqttRpcClientOptionsBuilder
{
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy(); IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
public MqttRpcClientOptions Build() public MqttRpcClientOptions Build()
@@ -24,5 +24,4 @@ namespace MQTTnet.Extensions.Rpc
return this; return this;
} }
}
} }
@@ -2,12 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
{
public sealed class MqttRpcTopicPair
{
public string RequestTopic { get; set; }
public string ResponseTopic { get; set; } public sealed class MqttRpcTopicPair
} {
public required string RequestTopic { get; init; }
public required string ResponseTopic { get; init; }
} }
@@ -6,12 +6,16 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc namespace MQTTnet.Extensions.Rpc;
public sealed class TopicGenerationContext
{ {
public sealed class TopicGenerationContext public TopicGenerationContext(
{ IMqttClient mqttClient,
public TopicGenerationContext(IMqttClient mqttClient, MqttRpcClientOptions options, string methodName, MqttRpcClientOptions options,
IDictionary<string, object> parameters, MqttQualityOfServiceLevel qualityOfServiceLevel) string methodName,
IDictionary<string, object>? parameters,
MqttQualityOfServiceLevel qualityOfServiceLevel)
{ {
MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName)); MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
Parameters = parameters; Parameters = parameters;
@@ -22,12 +26,12 @@ namespace MQTTnet.Extensions.Rpc
public string MethodName { get; } public string MethodName { get; }
public IDictionary<string, object> Parameters { get; }
public IMqttClient MqttClient { get; } public IMqttClient MqttClient { get; }
public MqttRpcClientOptions Options { get; } public MqttRpcClientOptions Options { get; }
public IDictionary<string, object>? Parameters { get; }
/// <summary> /// <summary>
/// Gets or sets the quality of service level. /// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message
@@ -38,5 +42,4 @@ namespace MQTTnet.Extensions.Rpc
/// - 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>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
}
} }
@@ -28,10 +28,13 @@
<PackageIcon>nuget.png</PackageIcon> <PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes> <PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591;NETSDK1138</NoWarn> <NoWarn>1591;NETSDK1138</NoWarn>
<IsAotCompatible>true</IsAotCompatible>
<LangVersion>default</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -39,15 +42,16 @@
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> <None Include="..\..\Images\nuget.png" Pack="true" PackagePath="" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <None Include="..\..\LICENSE" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -230,7 +230,7 @@ public sealed class MqttTopicTemplate : IEquatable<MqttTopicTemplate>
throw new ArgumentException("the topic has to match this template", nameof(topic)); throw new ArgumentException("the topic has to match this template", nameof(topic));
} }
return parseParameterValuesInternal(topic); return ParseParameterValuesInternal(topic);
} }
/// <summary> /// <summary>
@@ -330,7 +330,7 @@ public sealed class MqttTopicTemplate : IEquatable<MqttTopicTemplate>
return parameters.Aggregate(this, (t, p) => t.TrySetParameter(p.parameter, p.value)); return parameters.Aggregate(this, (t, p) => t.TrySetParameter(p.parameter, p.value));
} }
IEnumerable<(string parameter, int index, string value)> parseParameterValuesInternal(string topic) IEnumerable<(string parameter, int index, string value)> ParseParameterValuesInternal(string topic)
{ {
// because we have a match, we know the segment array is at least the template's length // because we have a match, we know the segment array is at least the template's length
var segments = topic.Split(MqttTopicFilterComparer.LevelSeparator); var segments = topic.Split(MqttTopicFilterComparer.LevelSeparator);
@@ -5,10 +5,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class MqttServerClientDisconnectOptions
{ {
public sealed class MqttServerClientDisconnectOptions
{
public MqttDisconnectReasonCode ReasonCode { get; set; } = MqttDisconnectReasonCode.NormalDisconnection; public MqttDisconnectReasonCode ReasonCode { get; set; } = MqttDisconnectReasonCode.NormalDisconnection;
/// <summary> /// <summary>
@@ -28,5 +28,4 @@ namespace MQTTnet.Server
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
}
} }
@@ -5,10 +5,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class MqttServerClientDisconnectOptionsBuilder
{ {
public sealed class MqttServerClientDisconnectOptionsBuilder
{
readonly MqttServerClientDisconnectOptions _options = new MqttServerClientDisconnectOptions(); readonly MqttServerClientDisconnectOptions _options = new MqttServerClientDisconnectOptions();
public MqttServerClientDisconnectOptions Build() public MqttServerClientDisconnectOptions Build()
@@ -50,5 +50,4 @@ namespace MQTTnet.Server
_options.UserProperties.Add(new MqttUserProperty(name, value)); _options.UserProperties.Add(new MqttUserProperty(name, value));
return this; return this;
} }
}
} }
@@ -8,9 +8,9 @@ namespace MQTTnet.Server.EnhancedAuthentication;
public sealed class ExchangeEnhancedAuthenticationOptions public sealed class ExchangeEnhancedAuthenticationOptions
{ {
public byte[] AuthenticationData { get; set; } public byte[]? AuthenticationData { get; set; }
public string ReasonString { get; set; } public string? ReasonString { get; set; }
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty>? UserProperties { get; set; }
} }
@@ -4,9 +4,9 @@ namespace MQTTnet.Server.EnhancedAuthentication;
public sealed class ExchangeEnhancedAuthenticationResult public sealed class ExchangeEnhancedAuthenticationResult
{ {
public string ReasonString { get; set; } public string? ReasonString { get; init; }
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty>? UserProperties { get; init; }
public byte[] AuthenticationData { get; set; } public byte[]? AuthenticationData { get; init; }
} }
@@ -2,12 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
namespace MQTTnet.Server public sealed class ApplicationMessageEnqueuedEventArgs : EventArgs
{ {
public sealed class ApplicationMessageEnqueuedEventArgs : EventArgs
{
public ApplicationMessageEnqueuedEventArgs(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage, bool isDropped) public ApplicationMessageEnqueuedEventArgs(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage, bool isDropped)
{ {
SenderClientId = senderClientId ?? throw new ArgumentNullException( nameof(senderClientId)); SenderClientId = senderClientId ?? throw new ArgumentNullException( nameof(senderClientId));
@@ -23,5 +21,4 @@ namespace MQTTnet.Server
public bool IsDropped { get; } public bool IsDropped { get; }
public MqttApplicationMessage ApplicationMessage { get; } public MqttApplicationMessage ApplicationMessage { get; }
}
} }
@@ -2,12 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
namespace MQTTnet.Server public sealed class ApplicationMessageNotConsumedEventArgs : EventArgs
{ {
public sealed class ApplicationMessageNotConsumedEventArgs : EventArgs
{
public ApplicationMessageNotConsumedEventArgs(MqttApplicationMessage applicationMessage, string senderId) public ApplicationMessageNotConsumedEventArgs(MqttApplicationMessage applicationMessage, string senderId)
{ {
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
@@ -23,5 +21,4 @@ namespace MQTTnet.Server
/// Gets the ID of the client which has sent the affected application message. /// Gets the ID of the client which has sent the affected application message.
/// </summary> /// </summary>
public string SenderId { get; } public string SenderId { get; }
}
} }
@@ -2,14 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ClientAcknowledgedPublishPacketEventArgs : EventArgs
{ {
public sealed class ClientAcknowledgedPublishPacketEventArgs : EventArgs
{
public ClientAcknowledgedPublishPacketEventArgs(string clientId, string userName, IDictionary sessionItems, MqttPublishPacket publishPacket, MqttPacketWithIdentifier acknowledgePacket) public ClientAcknowledgedPublishPacketEventArgs(string clientId, string userName, IDictionary sessionItems, MqttPublishPacket publishPacket, MqttPacketWithIdentifier acknowledgePacket)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
@@ -48,5 +47,4 @@ namespace MQTTnet.Server
/// Gets the session items which contain custom user data per session. /// Gets the session items which contain custom user data per session.
/// </summary> /// </summary>
public IDictionary SessionItems { get; } public IDictionary SessionItems { get; }
}
} }
@@ -2,18 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Text; using System.Text;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ClientConnectedEventArgs : EventArgs
{ {
public sealed class ClientConnectedEventArgs : EventArgs
{
readonly MqttConnectPacket _connectPacket; readonly MqttConnectPacket _connectPacket;
public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, EndPoint remoteEndPoint, IDictionary sessionItems) public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, EndPoint remoteEndPoint, IDictionary sessionItems)
@@ -24,15 +22,15 @@ namespace MQTTnet.Server
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
public byte[] AuthenticationData => _connectPacket.AuthenticationData; public byte[]? AuthenticationData => _connectPacket.AuthenticationData;
public string AuthenticationMethod => _connectPacket.AuthenticationMethod; public string? AuthenticationMethod => _connectPacket.AuthenticationMethod;
/// <summary> /// <summary>
/// Gets the client identifier of the connected client. /// Gets the client identifier of the connected client.
/// 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>
public string ClientId => _connectPacket.ClientId; public string ClientId => _connectPacket.ClientId!;
/// <summary> /// <summary>
/// Gets the endpoint of the connected client. /// Gets the endpoint of the connected client.
@@ -40,7 +38,7 @@ namespace MQTTnet.Server
public EndPoint RemoteEndPoint { get; } public EndPoint RemoteEndPoint { get; }
[Obsolete("Use RemoteEndPoint instead.")] [Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString(); public string? Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets the protocol version which is used by the connected client. /// Gets the protocol version which is used by the connected client.
@@ -55,17 +53,16 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets the user name of the connected client. /// Gets the user name of the connected client.
/// </summary> /// </summary>
public string UserName => _connectPacket.Username; public string? UserName => _connectPacket.Username;
/// <summary> /// <summary>
/// Gets the password of the connected client. /// Gets the password of the connected client.
/// </summary> /// </summary>
public string Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan()); public string? Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan());
/// <summary> /// <summary>
/// Gets the user properties sent by the client. /// Gets the user properties sent by the client.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public List<MqttUserProperty> UserProperties => _connectPacket?.UserProperties; public List<MqttUserProperty>? UserProperties => _connectPacket?.UserProperties;
}
} }
@@ -2,18 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Text; using System.Text;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ClientDisconnectedEventArgs : EventArgs
{ {
public sealed class ClientDisconnectedEventArgs : EventArgs
{
readonly MqttConnectPacket _connectPacket; readonly MqttConnectPacket _connectPacket;
readonly MqttDisconnectPacket _disconnectPacket; readonly MqttDisconnectPacket _disconnectPacket;
@@ -37,24 +35,24 @@ 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>
public string ClientId => _connectPacket.ClientId; public string ClientId => _connectPacket.ClientId!;
/// <summary> /// <summary>
/// Gets the user name of the client. /// Gets the user name of the client.
/// </summary> /// </summary>
public string UserName => _connectPacket.Username; public string? UserName => _connectPacket.Username;
/// <summary> /// <summary>
/// Gets the password of the client. /// Gets the password of the client.
/// </summary> /// </summary>
public string Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan()); public string? Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan());
public MqttClientDisconnectType DisconnectType { get; } public MqttClientDisconnectType DisconnectType { get; }
public EndPoint RemoteEndPoint { get; } public EndPoint RemoteEndPoint { get; }
[Obsolete("Use RemoteEndPoint instead.")] [Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString(); public string? Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets the reason code sent by the client. /// Gets the reason code sent by the client.
@@ -68,7 +66,7 @@ namespace MQTTnet.Server
/// Only available for clean disconnects. /// Only available for clean disconnects.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public string ReasonString => _disconnectPacket?.ReasonString; public string? ReasonString => _disconnectPacket?.ReasonString;
/// <summary> /// <summary>
/// Gets the session expiry interval sent by the client. /// Gets the session expiry interval sent by the client.
@@ -87,6 +85,5 @@ namespace MQTTnet.Server
/// Only available for clean disconnects. /// Only available for clean disconnects.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public List<MqttUserProperty> UserProperties => _disconnectPacket?.UserProperties; public List<MqttUserProperty>? UserProperties => _disconnectPacket?.UserProperties;
}
} }
@@ -2,14 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ClientSubscribedTopicEventArgs : EventArgs
{ {
public sealed class ClientSubscribedTopicEventArgs : EventArgs
{
public ClientSubscribedTopicEventArgs(string clientId, string userName, MqttTopicFilter topicFilter, IDictionary sessionItems) public ClientSubscribedTopicEventArgs(string clientId, string userName, MqttTopicFilter topicFilter, IDictionary sessionItems)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
@@ -39,5 +38,4 @@ namespace MQTTnet.Server
/// The topic filter can contain topics and wildcards. /// The topic filter can contain topics and wildcards.
/// </summary> /// </summary>
public MqttTopicFilter TopicFilter { get; } public MqttTopicFilter TopicFilter { get; }
}
} }
@@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ClientUnsubscribedTopicEventArgs : EventArgs
{ {
public sealed class ClientUnsubscribedTopicEventArgs : EventArgs
{
public ClientUnsubscribedTopicEventArgs(string clientId, string userName, string topicFilter, IDictionary sessionItems) public ClientUnsubscribedTopicEventArgs(string clientId, string userName, string topicFilter, IDictionary sessionItems)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
@@ -38,5 +37,4 @@ namespace MQTTnet.Server
/// The topic filter can contain topics and wildcards. /// The topic filter can contain topics and wildcards.
/// </summary> /// </summary>
public string TopicFilter { get; } public string TopicFilter { get; }
}
} }
@@ -2,12 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
namespace MQTTnet.Server public sealed class InterceptingClientApplicationMessageEnqueueEventArgs : EventArgs
{ {
public sealed class InterceptingClientApplicationMessageEnqueueEventArgs : EventArgs
{
public InterceptingClientApplicationMessageEnqueueEventArgs(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage) public InterceptingClientApplicationMessageEnqueueEventArgs(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage)
{ {
SenderClientId = senderClientId ?? throw new ArgumentNullException(nameof(senderClientId)); SenderClientId = senderClientId ?? throw new ArgumentNullException(nameof(senderClientId));
@@ -31,5 +29,4 @@ namespace MQTTnet.Server
public string ReceiverClientId { get; } public string ReceiverClientId { get; }
public string SenderClientId { get; } public string SenderClientId { get; }
}
} }
@@ -2,16 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Net; using System.Net;
using System.Threading;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class InterceptingPacketEventArgs : EventArgs
{ {
public sealed class InterceptingPacketEventArgs : EventArgs
{
public InterceptingPacketEventArgs(CancellationToken cancellationToken, string clientId, string userName, EndPoint remoteEndPoint, MqttPacket packet, IDictionary sessionItems) public InterceptingPacketEventArgs(CancellationToken cancellationToken, string clientId, string userName, EndPoint remoteEndPoint, MqttPacket packet, IDictionary sessionItems)
{ {
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
@@ -60,5 +58,4 @@ namespace MQTTnet.Server
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
public IDictionary SessionItems { get; } public IDictionary SessionItems { get; }
}
} }
@@ -2,14 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Threading;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class InterceptingPublishEventArgs : EventArgs
{ {
public sealed class InterceptingPublishEventArgs : EventArgs
{
public InterceptingPublishEventArgs(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems) public InterceptingPublishEventArgs(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems)
{ {
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
@@ -53,5 +51,4 @@ namespace MQTTnet.Server
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
public IDictionary SessionItems { get; } public IDictionary SessionItems { get; }
}
} }
@@ -2,16 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Threading;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class InterceptingSubscriptionEventArgs : EventArgs
{ {
public sealed class InterceptingSubscriptionEventArgs : EventArgs
{
public InterceptingSubscriptionEventArgs( public InterceptingSubscriptionEventArgs(
CancellationToken cancellationToken, CancellationToken cancellationToken,
string clientId, string clientId,
@@ -87,5 +84,4 @@ namespace MQTTnet.Server
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
}
} }
@@ -2,16 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Threading;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
{ {
public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
{
public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems, string topic, List<MqttUserProperty> userProperties) public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems, string topic, List<MqttUserProperty> userProperties)
{ {
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
@@ -74,5 +71,4 @@ namespace MQTTnet.Server
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary> /// </summary>
public List<MqttUserProperty> UserProperties { get; set; } public List<MqttUserProperty> UserProperties { get; set; }
}
} }
@@ -2,13 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
using System.Collections.Generic;
namespace MQTTnet.Server public sealed class LoadingRetainedMessagesEventArgs : EventArgs
{ {
public sealed class LoadingRetainedMessagesEventArgs : EventArgs
{
public List<MqttApplicationMessage> LoadedRetainedMessages { get; set; } = new List<MqttApplicationMessage>(); public List<MqttApplicationMessage> LoadedRetainedMessages { get; set; } = new List<MqttApplicationMessage>();
}
} }
@@ -2,15 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Server.Internal; using MQTTnet.Server.Internal;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class PreparingSessionEventArgs : EventArgs
{ {
public sealed class PreparingSessionEventArgs : EventArgs
{
public string Id { get; set; } public string Id { get; set; }
// TODO: Allow adding of packets to the queue etc. // TODO: Allow adding of packets to the queue etc.
@@ -47,5 +45,4 @@ namespace MQTTnet.Server
// </summary> // </summary>
// TODO: Use single properties. No entire will message. // TODO: Use single properties. No entire will message.
//MqttApplicationMessage WillMessage { get; set; } //MqttApplicationMessage WillMessage { get; set; }
}
} }
@@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class QueueMessageOverwrittenEventArgs : EventArgs
{ {
public sealed class QueueMessageOverwrittenEventArgs : EventArgs
{
public QueueMessageOverwrittenEventArgs(string receiverClientId, MqttPacket packet) public QueueMessageOverwrittenEventArgs(string receiverClientId, MqttPacket packet)
{ {
ReceiverClientId = receiverClientId ?? throw new ArgumentNullException(nameof(receiverClientId)); ReceiverClientId = receiverClientId ?? throw new ArgumentNullException(nameof(receiverClientId));
@@ -18,5 +17,4 @@ namespace MQTTnet.Server
public MqttPacket Packet { get; } public MqttPacket Packet { get; }
public string ReceiverClientId { get; } public string ReceiverClientId { get; }
}
} }
@@ -2,13 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
using System.Collections.Generic;
namespace MQTTnet.Server public sealed class RetainedMessageChangedEventArgs : EventArgs
{ {
public sealed class RetainedMessageChangedEventArgs : EventArgs
{
public RetainedMessageChangedEventArgs(string clientId, MqttApplicationMessage changedRetainedMessage, List<MqttApplicationMessage> storedRetainedMessages) public RetainedMessageChangedEventArgs(string clientId, MqttApplicationMessage changedRetainedMessage, List<MqttApplicationMessage> storedRetainedMessages)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
@@ -21,5 +18,4 @@ namespace MQTTnet.Server
public string ClientId { get; } public string ClientId { get; }
public List<MqttApplicationMessage> StoredRetainedMessages { get; } public List<MqttApplicationMessage> StoredRetainedMessages { get; }
}
} }
@@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class SessionDeletedEventArgs : EventArgs
{ {
public sealed class SessionDeletedEventArgs : EventArgs
{
public SessionDeletedEventArgs(string id, string userName, IDictionary sessionItems) public SessionDeletedEventArgs(string id, string userName, IDictionary sessionItems)
{ {
Id = id ?? throw new ArgumentNullException(nameof(id)); Id = id ?? throw new ArgumentNullException(nameof(id));
@@ -30,5 +29,4 @@ namespace MQTTnet.Server
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
public IDictionary SessionItems { get; } public IDictionary SessionItems { get; }
}
} }
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MQTTnet.Server.Exceptions;
public class MqttPendingMessagesOverflowException : Exception
{
public MqttPendingMessagesOverflowException(string sessionId, MqttPendingMessagesOverflowStrategy overflowStrategy) : base(
$"Send buffer max pending messages overflow occurred for session '{sessionId}'. Strategy: {overflowStrategy}.")
{
SessionId = sessionId;
OverflowStrategy = overflowStrategy;
}
public MqttPendingMessagesOverflowStrategy OverflowStrategy { get; }
public string SessionId { get; }
}
@@ -8,11 +8,11 @@ using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Internal; using MQTTnet.Internal;
namespace MQTTnet.Server.Internal.Adapter namespace MQTTnet.Server.Internal.Adapter;
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
{ {
public sealed class MqttTcpServerAdapter : IMqttServerAdapter readonly List<MqttTcpServerListener> _listeners = new();
{
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
CancellationTokenSource _cancellationTokenSource; CancellationTokenSource _cancellationTokenSource;
@@ -121,5 +121,4 @@ namespace MQTTnet.Server.Internal.Adapter
} }
} }
} }
}
} }
@@ -12,10 +12,10 @@ using MQTTnet.Formatter;
using MQTTnet.Implementations; using MQTTnet.Implementations;
using MQTTnet.Internal; using MQTTnet.Internal;
namespace MQTTnet.Server.Internal.Adapter namespace MQTTnet.Server.Internal.Adapter;
public sealed class MqttTcpServerListener : IDisposable
{ {
public sealed class MqttTcpServerListener : IDisposable
{
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
readonly IMqttNetLogger _rootLogger; readonly IMqttNetLogger _rootLogger;
readonly AddressFamily _addressFamily; readonly AddressFamily _addressFamily;
@@ -151,8 +151,7 @@ namespace MQTTnet.Server.Internal.Adapter
{ {
if (exception is SocketException socketException) if (exception is SocketException socketException)
{ {
if (socketException.SocketErrorCode == SocketError.ConnectionAborted || if (socketException.SocketErrorCode is SocketError.ConnectionAborted or SocketError.OperationAborted)
socketException.SocketErrorCode == SocketError.OperationAborted)
{ {
continue; continue;
} }
@@ -166,8 +165,8 @@ namespace MQTTnet.Server.Internal.Adapter
async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket) async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket)
{ {
Stream stream = null; Stream? stream = null;
EndPoint remoteEndPoint = null; EndPoint? remoteEndPoint = null;
try try
{ {
@@ -216,13 +215,11 @@ namespace MQTTnet.Server.Internal.Adapter
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax); var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter); var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, _rootLogger)) using var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, _rootLogger);
{
clientAdapter.AllowPacketFragmentation = _options.AllowPacketFragmentation; clientAdapter.AllowPacketFragmentation = _options.AllowPacketFragmentation;
await clientHandler(clientAdapter).ConfigureAwait(false); await clientHandler(clientAdapter).ConfigureAwait(false);
} }
} }
}
catch (Exception exception) catch (Exception exception)
{ {
if (exception is ObjectDisposedException) if (exception is ObjectDisposedException)
@@ -243,8 +240,11 @@ namespace MQTTnet.Server.Internal.Adapter
{ {
try try
{ {
// ReSharper disable once MethodHasAsyncOverload if (stream != null)
stream?.Dispose(); {
await stream.DisposeAsync().ConfigureAwait(false);
}
clientSocket?.Dispose(); clientSocket?.Dispose();
} }
catch (Exception disposeException) catch (Exception disposeException)
@@ -255,5 +255,4 @@ namespace MQTTnet.Server.Internal.Adapter
_logger.Verbose("TCP client '{0}' disconnected (Local endpoint={1})", remoteEndPoint, _localEndPoint); _logger.Verbose("TCP client '{0}' disconnected (Local endpoint={1})", remoteEndPoint, _localEndPoint);
} }
}
} }
@@ -4,10 +4,10 @@
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class CheckSubscriptionsResult
{ {
public sealed class CheckSubscriptionsResult
{
public static CheckSubscriptionsResult NotSubscribed { get; } = new CheckSubscriptionsResult(); public static CheckSubscriptionsResult NotSubscribed { get; } = new CheckSubscriptionsResult();
public bool IsSubscribed { get; set; } public bool IsSubscribed { get; set; }
@@ -17,5 +17,4 @@ namespace MQTTnet.Server.Internal
public List<uint> SubscriptionIdentifiers { get; set; } public List<uint> SubscriptionIdentifiers { get; set; }
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
}
} }
@@ -4,10 +4,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class DispatchApplicationMessageResult
{ {
public sealed class DispatchApplicationMessageResult
{
public DispatchApplicationMessageResult(int reasonCode, bool closeConnection, string reasonString, List<MqttUserProperty> userProperties) public DispatchApplicationMessageResult(int reasonCode, bool closeConnection, string reasonString, List<MqttUserProperty> userProperties)
{ {
ReasonCode = reasonCode; ReasonCode = reasonCode;
@@ -23,5 +23,4 @@ namespace MQTTnet.Server.Internal
public string ReasonString { get; } public string ReasonString { get; }
public List<MqttUserProperty> UserProperties { get; } public List<MqttUserProperty> UserProperties { get; }
}
} }
@@ -2,11 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public enum EnqueueDataPacketResult
{ {
public enum EnqueueDataPacketResult
{
Enqueued, Enqueued,
Dropped Dropped
}
} }
@@ -4,10 +4,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server.Internal.Formatter namespace MQTTnet.Server.Internal.Formatter;
public static class MqttUnsubAckPacketFactory
{ {
public static class MqttUnsubAckPacketFactory
{
public static MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, UnsubscribeResult unsubscribeResult) public static MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, UnsubscribeResult unsubscribeResult)
{ {
ArgumentNullException.ThrowIfNull(unsubscribePacket); ArgumentNullException.ThrowIfNull(unsubscribePacket);
@@ -23,5 +23,4 @@ namespace MQTTnet.Server.Internal.Formatter
return unsubAckPacket; return unsubAckPacket;
} }
}
} }
@@ -1,9 +1,8 @@
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public interface ISubscriptionChangedNotification
{ {
public interface ISubscriptionChangedNotification
{
void OnSubscriptionsAdded(MqttSession clientSession, List<string> subscriptionsTopics); void OnSubscriptionsAdded(MqttSession clientSession, List<string> subscriptionsTopics);
void OnSubscriptionsRemoved(MqttSession clientSession, List<string> subscriptionTopics); void OnSubscriptionsRemoved(MqttSession clientSession, List<string> subscriptionTopics);
}
} }
@@ -189,17 +189,10 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
continue; continue;
} }
if (_eventContainer.InterceptingClientEnqueueEvent.HasHandlers) if (await _eventContainer.ShouldSkipEnqueue(senderId, session.Id, applicationMessage))
{ {
var eventArgs = new InterceptingClientApplicationMessageEnqueueEventArgs(senderId, session.Id, applicationMessage);
await _eventContainer.InterceptingClientEnqueueEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
if (!eventArgs.AcceptEnqueue)
{
// Continue checking the other subscriptions
continue; continue;
} }
}
var publishPacketCopy = MqttPublishPacketFactory.Create(applicationMessage); var publishPacketCopy = MqttPublishPacketFactory.Create(applicationMessage);
publishPacketCopy.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel; publishPacketCopy.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel;
@@ -311,25 +304,6 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
return Task.FromResult((IList<MqttClientStatus>)result); return Task.FromResult((IList<MqttClientStatus>)result);
} }
public Task<MqttSessionStatus> GetSessionStatus(string id)
{
_sessionsManagementLock.EnterReadLock();
try
{
if (!_sessionsStorage.TryGetSession(id, out var session))
{
throw new InvalidOperationException($"Session with ID '{id}' not found.");
}
var sessionStatus = new MqttSessionStatus(session);
return Task.FromResult(sessionStatus);
}
finally
{
_sessionsManagementLock.ExitReadLock();
}
}
public Task<IList<MqttSessionStatus>> GetSessionsStatus() public Task<IList<MqttSessionStatus>> GetSessionsStatus()
{ {
var result = new List<MqttSessionStatus>(); var result = new List<MqttSessionStatus>();
@@ -351,6 +325,25 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
return Task.FromResult((IList<MqttSessionStatus>)result); return Task.FromResult((IList<MqttSessionStatus>)result);
} }
public Task<MqttSessionStatus> GetSessionStatus(string id)
{
_sessionsManagementLock.EnterReadLock();
try
{
if (!_sessionsStorage.TryGetSession(id, out var session))
{
throw new InvalidOperationException($"Session with ID '{id}' not found.");
}
var sessionStatus = new MqttSessionStatus(session);
return Task.FromResult(sessionStatus);
}
finally
{
_sessionsManagementLock.ExitReadLock();
}
}
public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{ {
MqttConnectedClient connectedClient = null; MqttConnectedClient connectedClient = null;
@@ -439,12 +432,10 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
} }
using (var timeout = new CancellationTokenSource(_options.DefaultCommunicationTimeout)) using var timeout = new CancellationTokenSource(_options.DefaultCommunicationTimeout);
{
await channelAdapter.DisconnectAsync(timeout.Token).ConfigureAwait(false); await channelAdapter.DisconnectAsync(timeout.Token).ConfigureAwait(false);
} }
} }
}
public void OnSubscriptionsAdded(MqttSession clientSession, List<string> topics) public void OnSubscriptionsAdded(MqttSession clientSession, List<string> topics)
{ {
@@ -510,15 +501,22 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
var subscribeResult = await clientSession.Subscribe(fakeSubscribePacket, CancellationToken.None).ConfigureAwait(false); var subscribeResult = await clientSession.Subscribe(fakeSubscribePacket, CancellationToken.None).ConfigureAwait(false);
if (subscribeResult.RetainedMessages != null) if (subscribeResult.RetainedMessages == null)
{ {
return;
}
foreach (var retainedMessageMatch in subscribeResult.RetainedMessages) foreach (var retainedMessageMatch in subscribeResult.RetainedMessages)
{ {
if (await _eventContainer.ShouldSkipEnqueue(string.Empty, clientId, retainedMessageMatch.ApplicationMessage))
{
continue;
}
var publishPacket = MqttPublishPacketFactory.Create(retainedMessageMatch); var publishPacket = MqttPublishPacketFactory.Create(retainedMessageMatch);
clientSession.EnqueueDataPacket(new MqttPacketBusItem(publishPacket)); clientSession.EnqueueDataPacket(new MqttPacketBusItem(publishPacket));
} }
} }
}
public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters) public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters)
{ {
@@ -669,20 +667,18 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
} }
async Task<MqttConnectPacket> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) async Task<MqttConnectPacket?> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{ {
try try
{ {
using (var timeoutToken = new CancellationTokenSource(_options.DefaultCommunicationTimeout)) using var timeoutToken = new CancellationTokenSource(_options.DefaultCommunicationTimeout);
using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken.Token, cancellationToken)) using var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken.Token, cancellationToken);
{
var firstPacket = await channelAdapter.ReceivePacketAsync(effectiveCancellationToken.Token).ConfigureAwait(false); var firstPacket = await channelAdapter.ReceivePacketAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
if (firstPacket is MqttConnectPacket connectPacket) if (firstPacket is MqttConnectPacket connectPacket)
{ {
return connectPacket; return connectPacket;
} }
} }
}
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.RemoteEndPoint); _logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.RemoteEndPoint);
@@ -740,6 +736,19 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
} }
async Task<bool> ShouldSkipEnqueue(string senderId, string clientId, MqttApplicationMessage applicationMessage)
{
if (!_eventContainer.InterceptingClientEnqueueEvent.HasHandlers)
{
return false;
}
var eventArgs = new InterceptingClientApplicationMessageEnqueueEventArgs(senderId, clientId, applicationMessage);
await _eventContainer.InterceptingClientEnqueueEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
return !eventArgs.AcceptEnqueue;
}
async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{ {
// TODO: Load session items from persisted sessions in the future. // TODO: Load session items from persisted sessions in the future.
@@ -4,10 +4,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class MqttClientStatistics
{ {
public sealed class MqttClientStatistics
{
Statistics _statistics = new Statistics // mutable struct, don't make readonly! Statistics _statistics = new Statistics // mutable struct, don't make readonly!
{ {
// Start with 1 because the CONNACK packet is not counted here. // Start with 1 because the CONNACK packet is not counted here.
@@ -100,5 +100,4 @@ namespace MQTTnet.Server.Internal
Volatile.Write(ref _sentApplicationMessagesCount, 0); Volatile.Write(ref _sentApplicationMessagesCount, 0);
} }
} }
}
} }
@@ -5,10 +5,10 @@
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class MqttClientSubscriptionsManager : IDisposable
{ {
public sealed class MqttClientSubscriptionsManager : IDisposable
{
static readonly List<uint> EmptySubscriptionIdentifiers = new List<uint>(); static readonly List<uint> EmptySubscriptionIdentifiers = new List<uint>();
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
@@ -512,5 +512,4 @@ namespace MQTTnet.Server.Internal
public MqttSubscription Subscription { get; set; } public MqttSubscription Subscription { get; set; }
} }
}
} }
@@ -284,15 +284,22 @@ public sealed class MqttConnectedClient : IDisposable
return; return;
} }
if (subscribeResult.RetainedMessages != null) if (subscribeResult.RetainedMessages == null)
{ {
return;
}
foreach (var retainedMessageMatch in subscribeResult.RetainedMessages) foreach (var retainedMessageMatch in subscribeResult.RetainedMessages)
{ {
if (await _eventContainer.ShouldSkipEnqueue(string.Empty, Id, retainedMessageMatch.ApplicationMessage))
{
continue;
}
var publishPacket = MqttPublishPacketFactory.Create(retainedMessageMatch); var publishPacket = MqttPublishPacketFactory.Create(retainedMessageMatch);
Session.EnqueueDataPacket(new MqttPacketBusItem(publishPacket)); Session.EnqueueDataPacket(new MqttPacketBusItem(publishPacket));
} }
} }
}
async Task HandleIncomingUnsubscribePacket(MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken) async Task HandleIncomingUnsubscribePacket(MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken)
{ {
@@ -2,16 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Internal; using MQTTnet.Internal;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class MqttRetainedMessagesManager
{ {
public sealed class MqttRetainedMessagesManager readonly Dictionary<string, MqttApplicationMessage> _messages = new(4096);
{ readonly AsyncLock _storageAccessLock = new();
readonly Dictionary<string, MqttApplicationMessage> _messages = new Dictionary<string, MqttApplicationMessage>(4096);
readonly AsyncLock _storageAccessLock = new AsyncLock();
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
@@ -62,6 +61,7 @@ namespace MQTTnet.Server.Internal
lock (_messages) lock (_messages)
{ {
var hasHandlers = _eventContainer.RetainedMessageChangedEvent.HasHandlers;
var payload = applicationMessage.Payload; var payload = applicationMessage.Payload;
var hasPayload = payload.Length > 0; var hasPayload = payload.Length > 0;
@@ -90,13 +90,15 @@ namespace MQTTnet.Server.Internal
_logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic); _logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
} }
if (saveIsRequired) if (saveIsRequired && hasHandlers)
{ {
messagesForSave = new List<MqttApplicationMessage>(_messages.Values); messagesForSave = new List<MqttApplicationMessage>(_messages.Values);
} }
} }
if (saveIsRequired) if (saveIsRequired)
{
if (_eventContainer.RetainedMessageChangedEvent.HasHandlers)
{ {
using (await _storageAccessLock.EnterAsync().ConfigureAwait(false)) using (await _storageAccessLock.EnterAsync().ConfigureAwait(false))
{ {
@@ -105,6 +107,7 @@ namespace MQTTnet.Server.Internal
} }
} }
} }
}
catch (Exception exception) catch (Exception exception)
{ {
_logger.Error(exception, "Unhandled exception while handling retained messages."); _logger.Error(exception, "Unhandled exception while handling retained messages.");
@@ -145,5 +148,4 @@ namespace MQTTnet.Server.Internal
await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false); await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
} }
} }
}
} }
@@ -4,52 +4,64 @@
using MQTTnet.Internal; using MQTTnet.Internal;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public class MqttServerEventContainer
{ {
public sealed class MqttServerEventContainer public AsyncEvent<ApplicationMessageNotConsumedEventArgs> ApplicationMessageNotConsumedEvent { get; } = new();
public AsyncEvent<ClientAcknowledgedPublishPacketEventArgs> ClientAcknowledgedPublishPacketEvent { get; } = new();
public AsyncEvent<ClientConnectedEventArgs> ClientConnectedEvent { get; } = new();
public AsyncEvent<ClientDisconnectedEventArgs> ClientDisconnectedEvent { get; } = new();
public AsyncEvent<ClientSubscribedTopicEventArgs> ClientSubscribedTopicEvent { get; } = new();
public AsyncEvent<ClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new();
public AsyncEvent<InterceptingClientApplicationMessageEnqueueEventArgs> InterceptingClientEnqueueEvent { get; } = new();
public AsyncEvent<ApplicationMessageEnqueuedEventArgs> ApplicationMessageEnqueuedOrDroppedEvent { get; } = new();
public AsyncEvent<QueueMessageOverwrittenEventArgs> QueuedApplicationMessageOverwrittenEvent { get; } = new();
public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new();
public AsyncEvent<InterceptingPacketEventArgs> InterceptingOutboundPacketEvent { get; } = new();
public AsyncEvent<InterceptingPublishEventArgs> InterceptingPublishEvent { get; } = new();
public AsyncEvent<InterceptingSubscriptionEventArgs> InterceptingSubscriptionEvent { get; } = new();
public AsyncEvent<InterceptingUnsubscriptionEventArgs> InterceptingUnsubscriptionEvent { get; } = new();
public AsyncEvent<LoadingRetainedMessagesEventArgs> LoadingRetainedMessagesEvent { get; } = new();
public AsyncEvent<EventArgs> PreparingSessionEvent { get; } = new();
public AsyncEvent<RetainedMessageChangedEventArgs> RetainedMessageChangedEvent { get; } = new();
public AsyncEvent<EventArgs> RetainedMessagesClearedEvent { get; } = new();
public AsyncEvent<SessionDeletedEventArgs> SessionDeletedEvent { get; } = new();
public AsyncEvent<EventArgs> StartedEvent { get; } = new();
public AsyncEvent<EventArgs> StoppedEvent { get; } = new();
public AsyncEvent<ValidatingConnectionEventArgs> ValidatingConnectionEvent { get; } = new();
public async Task<bool> ShouldSkipEnqueue(string senderClientId, string clientId, MqttApplicationMessage applicationMessage)
{ {
public AsyncEvent<ApplicationMessageNotConsumedEventArgs> ApplicationMessageNotConsumedEvent { get; } = new AsyncEvent<ApplicationMessageNotConsumedEventArgs>(); if (!InterceptingClientEnqueueEvent.HasHandlers)
{
return false;
}
public AsyncEvent<ClientAcknowledgedPublishPacketEventArgs> ClientAcknowledgedPublishPacketEvent { get; } = new AsyncEvent<ClientAcknowledgedPublishPacketEventArgs>(); var eventArgs = new InterceptingClientApplicationMessageEnqueueEventArgs(senderClientId, clientId, applicationMessage);
await InterceptingClientEnqueueEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
public AsyncEvent<ClientConnectedEventArgs> ClientConnectedEvent { get; } = new AsyncEvent<ClientConnectedEventArgs>(); return !eventArgs.AcceptEnqueue;
public AsyncEvent<ClientDisconnectedEventArgs> ClientDisconnectedEvent { get; } = new AsyncEvent<ClientDisconnectedEventArgs>();
public AsyncEvent<ClientSubscribedTopicEventArgs> ClientSubscribedTopicEvent { get; } = new AsyncEvent<ClientSubscribedTopicEventArgs>();
public AsyncEvent<ClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new AsyncEvent<ClientUnsubscribedTopicEventArgs>();
public AsyncEvent<InterceptingClientApplicationMessageEnqueueEventArgs> InterceptingClientEnqueueEvent { get; } = new AsyncEvent<InterceptingClientApplicationMessageEnqueueEventArgs>();
public AsyncEvent<ApplicationMessageEnqueuedEventArgs> ApplicationMessageEnqueuedOrDroppedEvent { get; } = new AsyncEvent<ApplicationMessageEnqueuedEventArgs>();
public AsyncEvent<QueueMessageOverwrittenEventArgs> QueuedApplicationMessageOverwrittenEvent { get; } = new AsyncEvent<QueueMessageOverwrittenEventArgs>();
public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
public AsyncEvent<InterceptingPacketEventArgs> InterceptingOutboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
public AsyncEvent<InterceptingPublishEventArgs> InterceptingPublishEvent { get; } = new AsyncEvent<InterceptingPublishEventArgs>();
public AsyncEvent<InterceptingSubscriptionEventArgs> InterceptingSubscriptionEvent { get; } = new AsyncEvent<InterceptingSubscriptionEventArgs>();
public AsyncEvent<InterceptingUnsubscriptionEventArgs> InterceptingUnsubscriptionEvent { get; } = new AsyncEvent<InterceptingUnsubscriptionEventArgs>();
public AsyncEvent<LoadingRetainedMessagesEventArgs> LoadingRetainedMessagesEvent { get; } = new AsyncEvent<LoadingRetainedMessagesEventArgs>();
public AsyncEvent<EventArgs> PreparingSessionEvent { get; } = new AsyncEvent<EventArgs>();
public AsyncEvent<RetainedMessageChangedEventArgs> RetainedMessageChangedEvent { get; } = new AsyncEvent<RetainedMessageChangedEventArgs>();
public AsyncEvent<EventArgs> RetainedMessagesClearedEvent { get; } = new AsyncEvent<EventArgs>();
public AsyncEvent<SessionDeletedEventArgs> SessionDeletedEvent { get; } = new AsyncEvent<SessionDeletedEventArgs>();
public AsyncEvent<EventArgs> StartedEvent { get; } = new AsyncEvent<EventArgs>();
public AsyncEvent<EventArgs> StoppedEvent { get; } = new AsyncEvent<EventArgs>();
public AsyncEvent<ValidatingConnectionEventArgs> ValidatingConnectionEvent { get; } = new AsyncEvent<ValidatingConnectionEventArgs>();
} }
} }
@@ -6,10 +6,10 @@ using MQTTnet.Diagnostics.Logger;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class MqttServerKeepAliveMonitor
{ {
public sealed class MqttServerKeepAliveMonitor
{
static readonly MqttServerClientDisconnectOptions DefaultDisconnectOptions = new MqttServerClientDisconnectOptions static readonly MqttServerClientDisconnectOptions DefaultDisconnectOptions = new MqttServerClientDisconnectOptions
{ {
ReasonCode = MqttDisconnectReasonCode.KeepAliveTimeout, ReasonCode = MqttDisconnectReasonCode.KeepAliveTimeout,
@@ -131,5 +131,4 @@ namespace MQTTnet.Server.Internal
TryProcessClient(client, now); TryProcessClient(client, now);
} }
} }
}
} }
@@ -6,6 +6,7 @@ using System.Collections;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server.Exceptions;
namespace MQTTnet.Server.Internal; namespace MQTTnet.Server.Internal;
@@ -111,10 +112,11 @@ public sealed class MqttSession : IDisposable
public EnqueueDataPacketResult EnqueueDataPacket(MqttPacketBusItem packetBusItem) public EnqueueDataPacketResult EnqueueDataPacket(MqttPacketBusItem packetBusItem)
{ {
if (_packetBus.ItemsCount(MqttPacketBusPartition.Data) >= _serverOptions.MaxPendingMessagesPerClient) if (PendingDataPacketsCount >= _serverOptions.MaxPendingMessagesPerClient)
{ {
if (_serverOptions.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage) if (_serverOptions.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
{ {
packetBusItem.Fail(new MqttPendingMessagesOverflowException(Id, _serverOptions.PendingMessagesOverflowStrategy));
return EnqueueDataPacketResult.Dropped; return EnqueueDataPacketResult.Dropped;
} }
@@ -123,13 +125,18 @@ public sealed class MqttSession : IDisposable
// Only drop from the data partition. Dropping from control partition might break the connection // Only drop from the data partition. Dropping from control partition might break the connection
// because the client does not receive PINGREQ packets etc. any longer. // because the client does not receive PINGREQ packets etc. any longer.
var firstItem = _packetBus.DropFirstItem(MqttPacketBusPartition.Data); var firstItem = _packetBus.DropFirstItem(MqttPacketBusPartition.Data);
if (firstItem != null && _eventContainer.QueuedApplicationMessageOverwrittenEvent.HasHandlers) if (firstItem != null)
{
firstItem.Fail(new MqttPendingMessagesOverflowException(Id, _serverOptions.PendingMessagesOverflowStrategy));
if (_eventContainer.QueuedApplicationMessageOverwrittenEvent.HasHandlers)
{ {
var eventArgs = new QueueMessageOverwrittenEventArgs(Id, firstItem.Packet); var eventArgs = new QueueMessageOverwrittenEventArgs(Id, firstItem.Packet);
_eventContainer.QueuedApplicationMessageOverwrittenEvent.InvokeAsync(eventArgs).ConfigureAwait(false); _eventContainer.QueuedApplicationMessageOverwrittenEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
} }
} }
} }
}
var publishPacket = (MqttPublishPacket)packetBusItem.Packet; var publishPacket = (MqttPublishPacket)packetBusItem.Packet;
@@ -4,10 +4,10 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace MQTTnet.Server.Internal namespace MQTTnet.Server.Internal;
public sealed class MqttSessionsStorage
{ {
public sealed class MqttSessionsStorage
{
readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096); readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
public void Clear() public void Clear()
@@ -77,5 +77,4 @@ namespace MQTTnet.Server.Internal
_sessions[id] = session; _sessions[id] = session;
} }
}
} }

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