mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 17:23:04 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0c5f20c5a |
@@ -1,4 +0,0 @@
|
|||||||
* [Core] Add validation of maximum string lengths (#1718).
|
|
||||||
* [Client] Added overloads for setting packet payload and will payload (#1720).
|
|
||||||
* [Client] The proper connect result is now exposed in the _Disconnected_ event when authentication fails (#1139).
|
|
||||||
* [Server] Improved performance by changing internal locking strategy for subscriptions (#1716, thanks to @zeheng).
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: CI
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "4.2.1.${{github.run_number}}"
|
VERSION: "4.1.0.${{github.run_number}}"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -22,7 +22,6 @@ jobs:
|
|||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
3.1.x
|
3.1.x
|
||||||
6.0.x
|
6.0.x
|
||||||
7.0.x
|
|
||||||
|
|
||||||
- name: Setup MSBuild
|
- name: Setup MSBuild
|
||||||
uses: microsoft/setup-msbuild@v1.1
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
@@ -62,3 +61,7 @@ jobs:
|
|||||||
- name: Publish MyGet nugets
|
- name: Publish MyGet nugets
|
||||||
if: ${{ github.event_name == 'push' }}
|
if: ${{ github.event_name == 'push' }}
|
||||||
run: dotnet nuget push **/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
|
run: dotnet nuget push **/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
|
||||||
|
|
||||||
|
# - name: Publish nugets
|
||||||
|
# if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
# run: dotnet nuget push **/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
|
||||||
+1
-10
@@ -210,7 +210,6 @@
|
|||||||
</Or>
|
</Or>
|
||||||
</Entry.Match>
|
</Entry.Match>
|
||||||
<Entry.SortBy>
|
<Entry.SortBy>
|
||||||
<Access Order="Public Internal Protected Private" />
|
|
||||||
<Name />
|
<Name />
|
||||||
</Entry.SortBy>
|
</Entry.SortBy>
|
||||||
</Entry>
|
</Entry>
|
||||||
@@ -240,14 +239,6 @@ See the LICENSE file in the project root for more information.</s:String>
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=CONNACK/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBACK/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREC/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREL/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=SUBACK/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNSUBACK/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://www.nuget.org/packages/MQTTnet)
|
[](https://www.nuget.org/packages/MQTTnet)
|
||||||
[](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
|
[](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
|
||||||
[](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
|
[](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
|
||||||

|

|
||||||
[](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# MQTTnet
|
# MQTTnet
|
||||||
|
|
||||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework version and CPU architecture.
|
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -50,21 +50,38 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
|||||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||||
|
|
||||||
## Getting Started
|
## Supported frameworks
|
||||||
|
|
||||||
MQTTnet is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/MQTTnet/
|
| Framwork | Version |
|
||||||
|
| ------------------ | ----------- |
|
||||||
|
|.NET | 5.0+ |
|
||||||
|
|.NET Framework | 4.5.2+ |
|
||||||
|
|.NET Standard | 1.3+ |
|
||||||
|
|.NET Core | 1.1+ |
|
||||||
|
|.NET Core App | 1.1+ |
|
||||||
|
| Mono | 5.2+ |
|
||||||
|
| UWP | 10.0.10240+ |
|
||||||
|
| Xamarin.Android | 7.5+ |
|
||||||
|
| Xamarin.iOS | 10.14+ |
|
||||||
|
| Blazor WebAssembly | 3.2.0+ |
|
||||||
|
|
||||||
Use these command in the Package Manager console to install MQTTnet manually:
|
## References
|
||||||
```
|
|
||||||
Install-Package MQTTnet
|
|
||||||
```
|
|
||||||
|
|
||||||
Samples for using MQTTnet are part of this repository. For starters these samples are recommended:
|
This library is used in the following projects:
|
||||||
|
|
||||||
- [Connect with a broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Connection_Samples.cs)
|
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
|
||||||
- [Subscribing to data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Subscribe_Samples.cs)
|
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||||
- [Publishing data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Publish_Samples.cs)
|
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
||||||
- [Host own broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Server/Server_Simple_Samples.cs)
|
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
|
||||||
|
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
||||||
|
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnetApp>)
|
||||||
|
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||||
|
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
|
||||||
|
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ) <https://github.com/BEagle1984/silverback>
|
||||||
|
|
||||||
|
Further projects using this project can be found under https://github.com/dotnet/MQTTnet/network/dependents.
|
||||||
|
|
||||||
|
If you use this library and want to see your project here please create a pull request.
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
|
|||||||
@@ -10,34 +10,12 @@ using System.Security.Authentication;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Extensions.WebSocket4Net;
|
using MQTTnet.Extensions.WebSocket4Net;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Samples.Helpers;
|
using MQTTnet.Samples.Shared;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Client;
|
namespace MQTTnet.Samples.Client;
|
||||||
|
|
||||||
public static class Client_Connection_Samples
|
public static class Client_Connection_Samples
|
||||||
{
|
{
|
||||||
public static async Task Clean_Disconnect()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
|
|
||||||
* to the server and close the connection afterwards.
|
|
||||||
*
|
|
||||||
* See sample _Connect_Client_ for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
// This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
|
|
||||||
// connection is closed in a "not clean" way. See MQTT specification for more details.
|
|
||||||
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Connect_Client()
|
public static async Task Connect_Client()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -71,32 +49,6 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Connect_With_Amazon_AWS()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
|
|
||||||
*
|
|
||||||
* The broker requires special settings which are set here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
|
||||||
.WithTcpServer("amazon.web.services.broker")
|
|
||||||
// Disabling packet fragmentation is very important!
|
|
||||||
.WithoutPacketFragmentation()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
|
||||||
|
|
||||||
await mqttClient.DisconnectAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Connect_Client_Timeout()
|
public static async Task Connect_Client_Timeout()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -182,17 +134,15 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Connect_Client_Using_WebSocket4Net()
|
public static async Task Connect_Client_Using_WebSockets()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
||||||
* Instead of the .NET implementation of WebSockets the implementation from WebSocket4Net is used. It provides more
|
|
||||||
* encryption algorithms and supports more platforms.
|
|
||||||
*
|
*
|
||||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory().UseWebSocket4Net();
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
@@ -206,15 +156,17 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Connect_Client_Using_WebSockets()
|
public static async Task Connect_Client_Using_WebSocket4Net()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
||||||
|
* Instead of the .NET implementation of WebSockets the implementaion from WebSocket4Net is used. It provides more
|
||||||
|
* encryption algorithms and supports more platforms.
|
||||||
*
|
*
|
||||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
var mqttFactory = new MqttFactory().UseWebSocket4Net();
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
@@ -262,49 +214,6 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Disconnect_Clean()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample disconnects from the server with sending a DISCONNECT packet.
|
|
||||||
* This way of disconnecting is treated as a clean disconnect which will not
|
|
||||||
* trigger sending the last will etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
|
|
||||||
// Using a reason code requires MQTT version 5.0.0!
|
|
||||||
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Disconnect_Non_Clean()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample disconnects from the server without sending a DISCONNECT packet.
|
|
||||||
* This way of disconnecting is treated as a non clean disconnect which will
|
|
||||||
* trigger sending the last will etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
var mqttClient = mqttFactory.CreateMqttClient();
|
|
||||||
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
|
|
||||||
// without sending a DISCONNECT packet to the server.
|
|
||||||
mqttClient.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Inspect_Certificate_Validation_Errors()
|
public static async Task Inspect_Certificate_Validation_Errors()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -345,7 +254,7 @@ public static class Client_Connection_Samples
|
|||||||
/*
|
/*
|
||||||
* This sample sends a PINGREQ packet to the server and waits for a reply.
|
* This sample sends a PINGREQ packet to the server and waits for a reply.
|
||||||
*
|
*
|
||||||
* This is only supported in MQTTv5.0.0+.
|
* This is only supported in METTv5.0.0+.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
var mqttFactory = new MqttFactory();
|
||||||
@@ -412,12 +321,14 @@ public static class Client_Connection_Samples
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
|
// This code will also do the very first connect! So no call to _ConnectAsync_ is required
|
||||||
if (!await mqttClient.TryPingAsync())
|
// in the first place.
|
||||||
|
if (!mqttClient.IsConnected)
|
||||||
{
|
{
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
// Subscribe to topics when session is clean etc.
|
// Subscribe to topics when session is clean etc.
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,53 +40,6 @@ public static class Client_Publish_Samples
|
|||||||
|
|
||||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
|
|
||||||
await mqttClient.DisconnectAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("MQTT application message is published.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Publish_Multiple_Application_Messages()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample pushes multiple simple application message including a topic and a payload.
|
|
||||||
*
|
|
||||||
* See sample _Publish_Application_Message_ for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
|
||||||
.WithTcpServer("broker.hivemq.com")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
|
||||||
.WithTopic("samples/temperature/living_room")
|
|
||||||
.WithPayload("19.5")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
|
||||||
|
|
||||||
applicationMessage = new MqttApplicationMessageBuilder()
|
|
||||||
.WithTopic("samples/temperature/living_room")
|
|
||||||
.WithPayload("20.0")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
|
||||||
|
|
||||||
applicationMessage = new MqttApplicationMessageBuilder()
|
|
||||||
.WithTopic("samples/temperature/living_room")
|
|
||||||
.WithPayload("21.0")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
|
||||||
|
|
||||||
await mqttClient.DisconnectAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("MQTT application message is published.");
|
Console.WriteLine("MQTT application message is published.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Samples.Shared;
|
||||||
using MQTTnet.Protocol;
|
|
||||||
using MQTTnet.Samples.Helpers;
|
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Client;
|
namespace MQTTnet.Samples.Client;
|
||||||
|
|
||||||
@@ -25,7 +23,9 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
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
|
||||||
// are also handled properly. When there is no event handler attached all
|
// are also handled properly. When there is no event handler attached all
|
||||||
@@ -41,11 +41,7 @@ public static class Client_Subscribe_Samples
|
|||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
.WithTopicFilter(
|
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/2");
|
|
||||||
})
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
@@ -57,95 +53,6 @@ public static class Client_Subscribe_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Send_Responses()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample subscribes to a topic and sends a response to the broker. This requires at least QoS level 1 to work!
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
|
|
||||||
{
|
|
||||||
// Do some work with the message...
|
|
||||||
|
|
||||||
// Now respond to the broker with a reason code other than success.
|
|
||||||
args.ReasonCode = MqttApplicationMessageReceivedReasonCode.ImplementationSpecificError;
|
|
||||||
args.ResponseReasonString = "That did not work!";
|
|
||||||
|
|
||||||
// User properties require MQTT v5!
|
|
||||||
args.ResponseUserProperties.Add(new MqttUserProperty("My", "Data"));
|
|
||||||
|
|
||||||
// Now the broker will resend the message again.
|
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/1");
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
Console.WriteLine("MQTT client subscribed to topic.");
|
|
||||||
|
|
||||||
// The response contains additional data sent by the server after subscribing.
|
|
||||||
response.DumpToConsole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Subscribe_Multiple_Topics()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample subscribes to several topics in a single request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
// Create the subscribe options including several topics with different options.
|
|
||||||
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/1");
|
|
||||||
})
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/2").WithNoLocal();
|
|
||||||
})
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/3").WithRetainHandling(MqttRetainHandling.SendAtSubscribe);
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
|
||||||
|
|
||||||
Console.WriteLine("MQTT client subscribed to topics.");
|
|
||||||
|
|
||||||
// The response contains additional data sent by the server after subscribing.
|
|
||||||
response.DumpToConsole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task Subscribe_Topic()
|
public static async Task Subscribe_Topic()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -156,16 +63,14 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
.WithTopicFilter(
|
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/1"); })
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic("mqttnet/samples/topic/1");
|
|
||||||
})
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Samples.Shared;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Diagnostics;
|
namespace MQTTnet.Samples.Diagnostics;
|
||||||
|
|
||||||
@@ -22,9 +23,7 @@ public static class Logger_Samples
|
|||||||
|
|
||||||
var mqttFactory = new MqttFactory(new MyLogger());
|
var mqttFactory = new MqttFactory(new MyLogger());
|
||||||
|
|
||||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.WithTcpServer("broker.hivemq.com")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
@@ -32,13 +31,31 @@ public static class Logger_Samples
|
|||||||
|
|
||||||
Console.WriteLine("MQTT client is connected.");
|
Console.WriteLine("MQTT client is connected.");
|
||||||
|
|
||||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task Log_To_Console()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample covers the creation of a custom logger which is used to log to the console.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory(new ConsoleLogger());
|
||||||
|
|
||||||
|
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
Console.WriteLine("MQTT client is connected. Press any key to exit.");
|
||||||
|
Console.ReadKey(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task Use_Event_Logger()
|
public static async Task Use_Event_Logger()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -64,9 +81,7 @@ public static class Logger_Samples
|
|||||||
|
|
||||||
var mqttFactory = new MqttFactory(mqttEventLogger);
|
var mqttFactory = new MqttFactory(mqttEventLogger);
|
||||||
|
|
||||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.WithTcpServer("broker.hivemq.com")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
@@ -74,8 +89,7 @@ public static class Logger_Samples
|
|||||||
|
|
||||||
Console.WriteLine("MQTT client is connected.");
|
Console.WriteLine("MQTT client is connected.");
|
||||||
|
|
||||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||||
}
|
}
|
||||||
@@ -83,7 +97,7 @@ public static class Logger_Samples
|
|||||||
|
|
||||||
sealed class MyLogger : IMqttNetLogger
|
sealed class MyLogger : IMqttNetLogger
|
||||||
{
|
{
|
||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; } = true;
|
||||||
|
|
||||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public static class PackageInspection_Samples
|
|||||||
.WithTcpServer("broker.hivemq.com")
|
.WithTcpServer("broker.hivemq.com")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
mqttClient.InspectPacketAsync += OnInspectPacket;
|
mqttClient.InspectPackage += OnInspectPackage;
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public static class PackageInspection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs)
|
static Task OnInspectPackage(InspectMqttPacketEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
|
|
||||||
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
|
<ProjectReference Include="..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
|
||||||
|
|||||||
+2
-18
@@ -4,16 +4,11 @@
|
|||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
Console.BackgroundColor = ConsoleColor.White;
|
Console.WriteLine("Welcome to MQTTnet samples!");
|
||||||
Console.BackgroundColor = ConsoleColor.Red;
|
|
||||||
Console.Title = "Samples - MQTTnet";
|
|
||||||
Console.WriteLine("----------------------------------------------");
|
|
||||||
Console.WriteLine("- Welcome to MQTTnet samples! -");
|
|
||||||
Console.WriteLine("----------------------------------------------");
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
||||||
|
sampleClasses.RemoveAll(c => c.Namespace.Contains(".Shared"));
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var sampleClass in sampleClasses)
|
foreach (var sampleClass in sampleClasses)
|
||||||
@@ -22,11 +17,7 @@ foreach (var sampleClass in sampleClasses)
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.Write("Please choose sample class (press Enter to continue): ");
|
Console.Write("Please choose sample class (press Enter to continue): ");
|
||||||
Console.ResetColor();
|
|
||||||
|
|
||||||
var input = Console.ReadLine();
|
var input = Console.ReadLine();
|
||||||
var selectedIndex = int.Parse(input ?? "0");
|
var selectedIndex = int.Parse(input ?? "0");
|
||||||
var selectedSampleClass = sampleClasses[selectedIndex];
|
var selectedSampleClass = sampleClasses[selectedIndex];
|
||||||
@@ -39,19 +30,12 @@ foreach (var sampleMethod in sampleMethods)
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.Write("Please choose sample (press Enter to continue): ");
|
Console.Write("Please choose sample (press Enter to continue): ");
|
||||||
Console.ResetColor();
|
|
||||||
|
|
||||||
input = Console.ReadLine();
|
input = Console.ReadLine();
|
||||||
selectedIndex = int.Parse(input ?? "0");
|
selectedIndex = int.Parse(input ?? "0");
|
||||||
var selectedSampleMethod = sampleMethods[selectedIndex];
|
var selectedSampleMethod = sampleMethods[selectedIndex];
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
|
||||||
Console.WriteLine("Executing sample...");
|
Console.WriteLine("Executing sample...");
|
||||||
Console.ResetColor();
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ using MQTTnet.Server;
|
|||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
public static class Server_ASP_NET_Samples
|
public static class Server_Asp_Net_Samples
|
||||||
{
|
{
|
||||||
public static Task Start_Server_With_WebSockets_Support()
|
public static Task Start_Server_With_WebSockets_Support()
|
||||||
{
|
{
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
|
||||||
|
|
||||||
// ReSharper disable UnusedType.Global
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
// ReSharper disable EmptyConstructor
|
|
||||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
|
||||||
|
|
||||||
public static class Server_Diagnostics_Samples
|
|
||||||
{
|
|
||||||
public static async Task Get_Notified_When_Client_Received_Message()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample starts a MQTT server and attaches an event which gets fired whenever
|
|
||||||
* a client received and acknowledged a PUBLISH packet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
|
||||||
|
|
||||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
|
||||||
{
|
|
||||||
// Attach the event handler.
|
|
||||||
mqttServer.ClientAcknowledgedPublishPacketAsync += e =>
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Client '{e.ClientId}' acknowledged packet {e.PublishPacket.PacketIdentifier} with topic '{e.PublishPacket.Topic}'");
|
|
||||||
|
|
||||||
// It is also possible to read additional data from the client response. This requires casting the response packet.
|
|
||||||
var qos1AcknowledgePacket = e.AcknowledgePacket as MqttPubAckPacket;
|
|
||||||
Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}");
|
|
||||||
|
|
||||||
var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket;
|
|
||||||
Console.WriteLine($"QoS 2 reason code: {qos1AcknowledgePacket?.ReasonCode}");
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
await mqttServer.StartAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("Press Enter to exit.");
|
|
||||||
Console.ReadLine();
|
|
||||||
|
|
||||||
await mqttServer.StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
// ReSharper disable UnusedType.Global
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
// ReSharper disable EmptyConstructor
|
|
||||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
|
||||||
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
|
||||||
|
|
||||||
public static class Server_Intercepting_Samples
|
|
||||||
{
|
|
||||||
public static async Task Intercept_Application_Messages()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample starts a simple MQTT server which manipulate all processed application messages.
|
|
||||||
* Please see _Server_Simple_Samples_ for more details on how to start a server.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
|
||||||
|
|
||||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
|
||||||
{
|
|
||||||
mqttServer.InterceptingPublishAsync += args =>
|
|
||||||
{
|
|
||||||
// Here we only change the topic of the received application message.
|
|
||||||
// but also changing the payload etc. is required. Changing the QoS after
|
|
||||||
// transmitting is not supported and makes no sense at all.
|
|
||||||
args.ApplicationMessage.Topic += "/manipulated";
|
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
await mqttServer.StartAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("Press Enter to exit.");
|
|
||||||
Console.ReadLine();
|
|
||||||
await mqttServer.StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Samples.Shared;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
namespace MQTTnet.Samples.Server;
|
||||||
@@ -170,50 +170,4 @@ public static class Server_Simple_Samples
|
|||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConsoleLogger : IMqttNetLogger
|
|
||||||
{
|
|
||||||
readonly object _consoleSyncRoot = new();
|
|
||||||
|
|
||||||
public bool IsEnabled => true;
|
|
||||||
|
|
||||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
|
||||||
{
|
|
||||||
var foregroundColor = ConsoleColor.White;
|
|
||||||
switch (logLevel)
|
|
||||||
{
|
|
||||||
case MqttNetLogLevel.Verbose:
|
|
||||||
foregroundColor = ConsoleColor.White;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Info:
|
|
||||||
foregroundColor = ConsoleColor.Green;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Warning:
|
|
||||||
foregroundColor = ConsoleColor.DarkYellow;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Error:
|
|
||||||
foregroundColor = ConsoleColor.Red;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters?.Length > 0)
|
|
||||||
{
|
|
||||||
message = string.Format(message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_consoleSyncRoot)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = foregroundColor;
|
|
||||||
Console.WriteLine(message);
|
|
||||||
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
Console.WriteLine(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
// ReSharper disable UnusedType.Global
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
|
|
||||||
using System.Net;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
|
||||||
|
|
||||||
public static class Server_TLS_Samples
|
|
||||||
{
|
|
||||||
public static async Task Run_Server_With_Self_Signed_Certificate()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
|
||||||
* It also has an encrypted connection using a self signed TLS certificate.
|
|
||||||
*
|
|
||||||
* See sample "Run_Minimal_Server" for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
|
||||||
|
|
||||||
// This certificate is self signed so that
|
|
||||||
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
|
||||||
|
|
||||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
|
|
||||||
|
|
||||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
|
||||||
{
|
|
||||||
await mqttServer.StartAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("Press Enter to exit.");
|
|
||||||
Console.ReadLine();
|
|
||||||
|
|
||||||
// Stop and dispose the MQTT server if it is no longer needed!
|
|
||||||
await mqttServer.StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
|
||||||
{
|
|
||||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
|
||||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
|
||||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
|
||||||
sanBuilder.AddDnsName("localhost");
|
|
||||||
|
|
||||||
using (var rsa = RSA.Create())
|
|
||||||
{
|
|
||||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
|
||||||
|
|
||||||
certRequest.CertificateExtensions.Add(
|
|
||||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
|
||||||
|
|
||||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false));
|
|
||||||
|
|
||||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
|
||||||
|
|
||||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
|
||||||
{
|
|
||||||
var pfxCertificate = new X509Certificate2(
|
|
||||||
certificate.Export(X509ContentType.Pfx),
|
|
||||||
(string)null!,
|
|
||||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
|
||||||
|
|
||||||
return pfxCertificate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet.Samples.Shared;
|
||||||
|
|
||||||
|
public sealed class ConsoleLogger : IMqttNetLogger
|
||||||
|
{
|
||||||
|
readonly object _consoleSyncRoot = new();
|
||||||
|
|
||||||
|
public bool IsEnabled => true;
|
||||||
|
|
||||||
|
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
||||||
|
{
|
||||||
|
var foregroundColor = ConsoleColor.White;
|
||||||
|
switch (logLevel)
|
||||||
|
{
|
||||||
|
case MqttNetLogLevel.Verbose:
|
||||||
|
foregroundColor = ConsoleColor.White;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Info:
|
||||||
|
foregroundColor = ConsoleColor.Green;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Warning:
|
||||||
|
foregroundColor = ConsoleColor.DarkYellow;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Error:
|
||||||
|
foregroundColor = ConsoleColor.Red;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters?.Length > 0)
|
||||||
|
{
|
||||||
|
message = string.Format(message, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_consoleSyncRoot)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = foregroundColor;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Helpers;
|
namespace MQTTnet.Samples.Shared;
|
||||||
|
|
||||||
internal static class ObjectExtensions
|
internal static class ObjectExtensions
|
||||||
{
|
{
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
|
{
|
||||||
|
public class ConnectionContextMockup : ConnectionContext
|
||||||
|
{
|
||||||
|
public override string ConnectionId { get; set; }
|
||||||
|
|
||||||
|
public override IFeatureCollection Features { get; }
|
||||||
|
|
||||||
|
public override IDictionary<object, object> Items { get; set; }
|
||||||
|
public override IDuplexPipe Transport { get; set; }
|
||||||
|
|
||||||
|
public ConnectionContextMockup()
|
||||||
|
{
|
||||||
|
//Transport = new DefaultConnectionContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,24 +2,20 @@
|
|||||||
// 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.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class ConnectionHandlerMockup : IMqttServerAdapter
|
public class ConnectionHandlerMockup: IMqttServerAdapter
|
||||||
{
|
{
|
||||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
|
||||||
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new TaskCompletionSource<MqttConnectionContext>();
|
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new TaskCompletionSource<MqttConnectionContext>();
|
||||||
|
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnConnectedAsync(ConnectionContext connection)
|
public async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
@@ -46,5 +42,9 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ using System.IO.Pipelines;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class DuplexPipeMockup : IDuplexPipe
|
public class DuplexPipeMockup : IDuplexPipe
|
||||||
{
|
{
|
||||||
public DuplexPipeMockup()
|
public DuplexPipeMockup()
|
||||||
{
|
{
|
||||||
@@ -20,8 +20,9 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
|
|
||||||
PipeWriter IDuplexPipe.Output => Send.Writer;
|
PipeWriter IDuplexPipe.Output => Send.Writer;
|
||||||
|
|
||||||
public Pipe Receive { get; }
|
|
||||||
|
|
||||||
public Pipe Send { get; }
|
public Pipe Receive { get; set; }
|
||||||
|
|
||||||
|
public Pipe Send { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Buffers;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class LimitedMemoryPool : MemoryPool<byte>
|
public class LimitedMemoryPool : MemoryPool<byte>
|
||||||
{
|
{
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ using System.Buffers;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class MemoryOwner : IMemoryOwner<byte>
|
public class MemoryOwner : IMemoryOwner<byte>
|
||||||
{
|
{
|
||||||
readonly byte[] _raw;
|
private readonly byte[] _raw;
|
||||||
|
|
||||||
public MemoryOwner(int size)
|
public MemoryOwner(int size)
|
||||||
{
|
{
|
||||||
@@ -27,11 +27,11 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
Memory = _raw;
|
Memory = _raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Memory<byte> Memory { get; }
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ArrayPool<byte>.Shared.Return(_raw);
|
ArrayPool<byte>.Shared.Return(_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Memory<byte> Memory { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,29 +2,44 @@
|
|||||||
// 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.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.AspNetCore.Tests.Mockups;
|
using MQTTnet.AspNetCore.Tests.Mockups;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Helpers;
|
using MQTTnet.Tests.Extensions;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests
|
namespace MQTTnet.AspNetCore.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttConnectionContextTest
|
public class MqttConnectionContextTest
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
||||||
|
{
|
||||||
|
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||||
|
var pipe = new DuplexPipeMockup();
|
||||||
|
var connection = new DefaultConnectionContext();
|
||||||
|
connection.Transport = pipe;
|
||||||
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
|
pipe.Receive.Writer.Complete();
|
||||||
|
|
||||||
|
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestCorruptedConnectPacket()
|
public async Task TestCorruptedConnectPacket()
|
||||||
{
|
{
|
||||||
@@ -35,42 +50,12 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
connection.Transport = pipe;
|
connection.Transport = pipe;
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, Array.Empty<byte>()));
|
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0]));
|
||||||
|
|
||||||
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
|
|
||||||
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
||||||
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TestEndpoint()
|
|
||||||
{
|
|
||||||
var mockup = new ConnectionHandlerMockup();
|
|
||||||
|
|
||||||
|
|
||||||
using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.ConfigureServices(
|
|
||||||
(hostContext, services) =>
|
|
||||||
{
|
|
||||||
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
|
||||||
services.AddSingleton<IMqttServerAdapter>(mockup);
|
|
||||||
})
|
|
||||||
.Build())
|
|
||||||
|
|
||||||
using (var client = new MqttFactory().CreateMqttClient())
|
|
||||||
{
|
|
||||||
host.Start();
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
|
|
||||||
|
|
||||||
var ctx = await mockup.Context.Task;
|
|
||||||
#if NETCOREAPP3_1
|
|
||||||
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
|
||||||
Assert.IsNotNull(ep);
|
|
||||||
#endif
|
|
||||||
Assert.IsNotNull(ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
||||||
@@ -103,31 +88,49 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
connection.Transport = pipe;
|
connection.Transport = pipe;
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new ArraySegment<byte>(new byte[20_000]) }, CancellationToken.None).ConfigureAwait(false);
|
await ctx.SendPacketAsync(new MqttPublishPacket { Payload = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
var readResult = await pipe.Send.Reader.ReadAsync();
|
var readResult = await pipe.Send.Reader.ReadAsync();
|
||||||
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
private class Startup
|
||||||
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
|
||||||
{
|
|
||||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
|
||||||
var pipe = new DuplexPipeMockup();
|
|
||||||
var connection = new DefaultConnectionContext();
|
|
||||||
connection.Transport = pipe;
|
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
|
||||||
|
|
||||||
pipe.Receive.Writer.Complete();
|
|
||||||
|
|
||||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
|
||||||
}
|
|
||||||
|
|
||||||
class Startup
|
|
||||||
{
|
{
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task TestEndpoint()
|
||||||
|
{
|
||||||
|
var mockup = new ConnectionHandlerMockup();
|
||||||
|
|
||||||
|
|
||||||
|
using (var host = new WebHostBuilder()
|
||||||
|
.UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.ConfigureServices((hostContext, services) =>
|
||||||
|
{
|
||||||
|
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
||||||
|
services.AddSingleton<IMqttServerAdapter>(mockup);
|
||||||
|
})
|
||||||
|
.Build())
|
||||||
|
|
||||||
|
using (var client = new MqttFactory().CreateMqttClient())
|
||||||
|
{
|
||||||
|
host.Start();
|
||||||
|
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost")
|
||||||
|
.Build(), CancellationToken.None);
|
||||||
|
|
||||||
|
var ctx = await mockup.Context.Task;
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
||||||
|
Assert.IsNotNull(ep);
|
||||||
|
#endif
|
||||||
|
Assert.IsNotNull(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,14 +12,14 @@ using MQTTnet.Packets;
|
|||||||
namespace MQTTnet.AspNetCore.Tests
|
namespace MQTTnet.AspNetCore.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class ReaderExtensionsTest
|
public class ReaderExtensionsTest
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestTryDeserialize()
|
public void TestTryDeserialize()
|
||||||
{
|
{
|
||||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||||
|
|
||||||
var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", PayloadSegment = new byte[5]}).Join();
|
var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", Payload = new byte[5]}).Join();
|
||||||
|
|
||||||
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
|
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,17 @@
|
|||||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="wwwroot\" />
|
<Folder Include="wwwroot\"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
|||||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||||
}
|
}
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||||
public override ValueTask DisposeAsync()
|
public override ValueTask DisposeAsync()
|
||||||
#else
|
#else
|
||||||
public Task DisposeAsync()
|
public Task DisposeAsync()
|
||||||
@@ -57,7 +57,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
|||||||
|
|
||||||
_socket?.Dispose();
|
_socket?.Dispose();
|
||||||
|
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||||
|
|
||||||
return base.DisposeAsync();
|
return base.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||||
|
|
||||||
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
|
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
|
||||||
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
|
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
|
||||||
@@ -32,8 +32,7 @@
|
|||||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -51,11 +50,11 @@
|
|||||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' ">
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' and '$(TargetFramework)' != 'net7.0' ">
|
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' ">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -2,27 +2,24 @@
|
|||||||
// 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.IO.Pipelines;
|
|
||||||
using System.Net;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.AspNetCore.Client.Tcp;
|
using MQTTnet.AspNetCore.Client.Tcp;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
using System;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
public sealed class MqttConnectionContext : IMqttChannelAdapter
|
public sealed class MqttConnectionContext : IMqttChannelAdapter
|
||||||
{
|
{
|
||||||
readonly ConnectionContext _connection;
|
|
||||||
readonly AsyncLock _writerLock = new AsyncLock();
|
readonly AsyncLock _writerLock = new AsyncLock();
|
||||||
|
|
||||||
PipeReader _input;
|
PipeReader _input;
|
||||||
@@ -31,30 +28,12 @@ namespace MQTTnet.AspNetCore
|
|||||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||||
{
|
{
|
||||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||||
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||||
|
|
||||||
_input = connection.Transport.Input;
|
if (Connection.Transport != null)
|
||||||
_output = connection.Transport.Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long BytesReceived { get; private set; }
|
|
||||||
|
|
||||||
public long BytesSent { get; private set; }
|
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
{
|
||||||
// mqtt over tcp
|
_input = Connection.Transport.Input;
|
||||||
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
|
_output = Connection.Transport.Output;
|
||||||
if (tlsFeature != null)
|
|
||||||
{
|
|
||||||
return tlsFeature.ClientCertificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mqtt over websocket
|
|
||||||
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
|
|
||||||
return httpFeature?.HttpContext?.Connection.ClientCertificate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,59 +41,47 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// mqtt over tcp
|
#if NETCOREAPP3_1
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
if (Connection?.RemoteEndPoint != null)
|
||||||
if (_connection.RemoteEndPoint != null)
|
|
||||||
{
|
{
|
||||||
return _connection.RemoteEndPoint.ToString();
|
return Connection.RemoteEndPoint.ToString();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// mqtt over websocket
|
var connection = Http?.HttpContext?.Connection;
|
||||||
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
|
if (connection == null)
|
||||||
if (httpFeature?.RemoteIpAddress != null)
|
|
||||||
{
|
{
|
||||||
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort).ToString();
|
return Connection.ConnectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsReadingPacket { get; private set; }
|
public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
|
||||||
|
|
||||||
public bool IsSecureConnection
|
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// mqtt over tcp
|
|
||||||
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
|
|
||||||
if (tlsFeature != null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mqtt over websocket
|
public ConnectionContext Connection { get; }
|
||||||
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
|
|
||||||
if (httpFeature?.HttpContext != null)
|
|
||||||
{
|
|
||||||
return httpFeature.HttpContext.Request.IsHttps;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
|
public long BytesSent { get; set; }
|
||||||
|
|
||||||
|
public long BytesReceived { get; set; }
|
||||||
|
|
||||||
|
public bool IsReadingPacket { get; private set; }
|
||||||
|
|
||||||
|
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_connection is TcpConnection tcp && !tcp.IsConnected)
|
if (Connection is TcpConnection tcp && !tcp.IsConnected)
|
||||||
{
|
{
|
||||||
await tcp.StartAsync().ConfigureAwait(false);
|
await tcp.StartAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_input = _connection.Transport.Input;
|
_input = Connection.Transport.Input;
|
||||||
_output = _connection.Transport.Output;
|
_output = Connection.Transport.Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
@@ -125,19 +92,16 @@ namespace MQTTnet.AspNetCore
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_writerLock.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var input = Connection.Transport.Input;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
ReadResult readResult;
|
ReadResult readResult;
|
||||||
var readTask = _input.ReadAsync(cancellationToken);
|
var readTask = input.ReadAsync(cancellationToken);
|
||||||
if (readTask.IsCompleted)
|
if (readTask.IsCompleted)
|
||||||
{
|
{
|
||||||
readResult = readTask.Result;
|
readResult = readTask.Result;
|
||||||
@@ -177,15 +141,15 @@ namespace MQTTnet.AspNetCore
|
|||||||
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
|
// 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
|
// 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.
|
// before yielding the read again.
|
||||||
_input.AdvanceTo(consumed, observed);
|
input.AdvanceTo(consumed, observed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// completing the channel makes sure that there is no more data read after a protocol error
|
// completing the channel makes sure that there is no more data read after a protocol error
|
||||||
_input?.Complete(exception);
|
_input?.Complete(e);
|
||||||
_output?.Complete(exception);
|
_output?.Complete(e);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -205,44 +169,25 @@ namespace MQTTnet.AspNetCore
|
|||||||
|
|
||||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
var formatter = PacketFormatterAdapter;
|
||||||
|
|
||||||
|
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
try
|
var buffer = formatter.Encode(packet);
|
||||||
|
var msg = buffer.Join().AsMemory();
|
||||||
|
var output = _output;
|
||||||
|
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (result.IsCompleted)
|
||||||
{
|
{
|
||||||
var buffer = PacketFormatterAdapter.Encode(packet);
|
BytesSent += msg.Length;
|
||||||
|
|
||||||
if (buffer.Payload.Count == 0)
|
|
||||||
{
|
|
||||||
// zero copy
|
|
||||||
// https://github.com/dotnet/runtime/blob/e31ddfdc4f574b26231233dc10c9a9c402f40590/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs#L279
|
|
||||||
await _output.WriteAsync(buffer.Packet, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WritePacketBuffer(_output, buffer);
|
|
||||||
await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
BytesSent += buffer.Length;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
PacketFormatterAdapter.Cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PacketFormatterAdapter.Cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
|
|
||||||
// MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
|
|
||||||
|
|
||||||
var span = output.GetSpan(buffer.Length);
|
|
||||||
|
|
||||||
buffer.Packet.AsSpan().CopyTo(span);
|
|
||||||
buffer.Payload.AsSpan().CopyTo(span.Slice(buffer.Packet.Count));
|
|
||||||
|
|
||||||
output.Advance(buffer.Length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,34 +2,38 @@
|
|||||||
// 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.Net.WebSockets;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
using System;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||||
{
|
{
|
||||||
IMqttNetLogger _logger = MqttNetNullLogger.Instance;
|
IMqttNetLogger _logger = new MqttNetNullLogger();
|
||||||
|
|
||||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (webSocket == null)
|
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(webSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||||
|
|
||||||
@@ -44,7 +48,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
||||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||||
|
|
||||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
|
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
|
||||||
{
|
{
|
||||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -56,15 +60,8 @@ namespace MQTTnet.AspNetCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.Buffers;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
@@ -35,22 +33,22 @@ namespace MQTTnet.AspNetCore
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryReadBodyLength(ref copy, out var headerLength, out var bodyLength))
|
var fixedHeader = copy.First.Span[0];
|
||||||
|
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fixedHeader = copy.First.Span[0];
|
|
||||||
copy = copy.Slice(headerLength);
|
|
||||||
if (copy.Length < bodyLength)
|
if (copy.Length < bodyLength)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodySlice = copy.Slice(0, bodyLength);
|
var bodySlice = copy.Slice(0, bodyLength);
|
||||||
var bodySegment = GetArraySegment(ref bodySlice);
|
var buffer = bodySlice.GetMemory().ToArray();
|
||||||
|
|
||||||
|
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, new ArraySegment<byte>(buffer, 0, buffer.Length), buffer.Length + 2);
|
||||||
|
|
||||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, bodySegment, headerLength + bodyLength);
|
|
||||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||||
{
|
{
|
||||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||||
@@ -63,41 +61,18 @@ namespace MQTTnet.AspNetCore
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ArraySegment<byte> GetArraySegment(ref ReadOnlySequence<byte> input)
|
static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
|
||||||
{
|
{
|
||||||
if (input.IsSingleSegment && MemoryMarshal.TryGetArray(input.First, out var segment))
|
if (input.IsSingleSegment)
|
||||||
{
|
{
|
||||||
return segment;
|
return input.First;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be rare
|
// Should be rare
|
||||||
var array = input.ToArray();
|
return input.ToArray();
|
||||||
return new ArraySegment<byte>(array);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||||
{
|
|
||||||
var valueSequence = input.Slice(0, Math.Min(5, input.Length));
|
|
||||||
if (valueSequence.IsSingleSegment)
|
|
||||||
{
|
|
||||||
var valueSpan =
|
|
||||||
#if NET5_0_OR_GREATER
|
|
||||||
valueSequence.FirstSpan;
|
|
||||||
#else
|
|
||||||
valueSequence.First.Span;
|
|
||||||
#endif
|
|
||||||
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<byte> valueSpan = stackalloc byte[8];
|
|
||||||
valueSequence.CopyTo(valueSpan);
|
|
||||||
valueSpan = valueSpan.Slice(0, (int)valueSequence.Length);
|
|
||||||
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool TryReadBodyLength(ReadOnlySpan<byte> span, 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.
|
// 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 multiplier = 1;
|
||||||
@@ -107,34 +82,32 @@ namespace MQTTnet.AspNetCore
|
|||||||
headerLength = 0;
|
headerLength = 0;
|
||||||
bodyLength = 0;
|
bodyLength = 0;
|
||||||
|
|
||||||
|
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory().Span;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (index == span.Length)
|
if (index == temp.Length)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
encodedByte = span[index];
|
encodedByte = temp[index];
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
value += (byte)(encodedByte & 127) * multiplier;
|
value += (byte)(encodedByte & 127) * multiplier;
|
||||||
if (multiplier > 128 * 128 * 128)
|
if (multiplier > 128 * 128 * 128)
|
||||||
{
|
{
|
||||||
ThrowProtocolViolationException(span, index);
|
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
multiplier *= 128;
|
multiplier *= 128;
|
||||||
} while ((encodedByte & 128) != 0);
|
} while ((encodedByte & 128) != 0);
|
||||||
|
|
||||||
|
input = input.Slice(index);
|
||||||
|
|
||||||
headerLength = index;
|
headerLength = index;
|
||||||
bodyLength = value;
|
bodyLength = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ThrowProtocolViolationException(ReadOnlySpan<byte> valueSpan, int index)
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", valueSpan.Slice(1, index).ToArray())}).");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
|
||||||
{
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class AsyncLockBenchmark : BaseBenchmark
|
|
||||||
{
|
|
||||||
[Benchmark]
|
|
||||||
public async Task Synchronize_100_Tasks()
|
|
||||||
{
|
|
||||||
const int tasksCount = 100;
|
|
||||||
|
|
||||||
var tasks = new Task[tasksCount];
|
|
||||||
var asyncLock = new AsyncLock();
|
|
||||||
var globalI = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < tasksCount; i++)
|
|
||||||
{
|
|
||||||
tasks[i] = Task.Run(
|
|
||||||
async () =>
|
|
||||||
{
|
|
||||||
using (await asyncLock.EnterAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
var localI = globalI;
|
|
||||||
await Task.Delay(5).ConfigureAwait(false); // Increase the chance for wrong data.
|
|
||||||
localI++;
|
|
||||||
globalI = localI;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (globalI != tasksCount)
|
|
||||||
{
|
|
||||||
throw new Exception($"Code is broken ({globalI})!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public async Task Wait_100_000_Times()
|
|
||||||
{
|
|
||||||
var asyncLock = new AsyncLock();
|
|
||||||
|
|
||||||
using (var cancellationToken = new CancellationTokenSource())
|
|
||||||
{
|
|
||||||
for (var i = 0; i < 100000; i++)
|
|
||||||
{
|
|
||||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
// 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.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
public abstract class BaseBenchmark
|
public abstract class BaseBenchmark
|
||||||
|
|||||||
@@ -2,25 +2,54 @@
|
|||||||
// 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 BenchmarkDotNet.Attributes;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Packets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public sealed class ChannelAdapterBenchmark : BaseBenchmark
|
public sealed class ChannelAdapterBenchmark
|
||||||
{
|
{
|
||||||
MqttChannelAdapter _channelAdapter;
|
MqttChannelAdapter _channelAdapter;
|
||||||
int _iterations;
|
int _iterations;
|
||||||
MqttPublishPacket _packet;
|
|
||||||
MemoryStream _stream;
|
MemoryStream _stream;
|
||||||
|
MqttPublishPacket _packet;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_packet = new MqttPublishPacket
|
||||||
|
{
|
||||||
|
Topic = "A"
|
||||||
|
};
|
||||||
|
|
||||||
|
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||||
|
|
||||||
|
var serializedPacket = Join(serializer.Encode(_packet).Join());
|
||||||
|
|
||||||
|
_iterations = 10000;
|
||||||
|
|
||||||
|
_stream = new MemoryStream(_iterations * serializedPacket.Length);
|
||||||
|
|
||||||
|
for (var i = 0; i < _iterations; i++)
|
||||||
|
{
|
||||||
|
_stream.Write(serializedPacket, 0, serializedPacket.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream.Position = 0;
|
||||||
|
|
||||||
|
var channel = new MemoryMqttChannel(_stream);
|
||||||
|
|
||||||
|
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
||||||
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public void Receive_10000_Messages()
|
public void Receive_10000_Messages()
|
||||||
@@ -48,34 +77,6 @@ namespace MQTTnet.Benchmarks
|
|||||||
_stream.Position = 0;
|
_stream.Position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_packet = new MqttPublishPacket
|
|
||||||
{
|
|
||||||
Topic = "A"
|
|
||||||
};
|
|
||||||
|
|
||||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
|
||||||
|
|
||||||
var serializedPacket = Join(serializer.Encode(_packet).Join());
|
|
||||||
|
|
||||||
_iterations = 10000;
|
|
||||||
|
|
||||||
_stream = new MemoryStream(_iterations * serializedPacket.Length);
|
|
||||||
|
|
||||||
for (var i = 0; i < _iterations; i++)
|
|
||||||
{
|
|
||||||
_stream.Write(serializedPacket, 0, serializedPacket.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream.Position = 0;
|
|
||||||
|
|
||||||
var channel = new MemoryMqttChannel(_stream);
|
|
||||||
|
|
||||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetEventLogger());
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] Join(params ArraySegment<byte>[] chunks)
|
static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||||
{
|
{
|
||||||
var buffer = new MemoryStream();
|
var buffer = new MemoryStream();
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using BenchmarkDotNet.Validators;
|
||||||
|
|
||||||
|
namespace MQTTnet.Benchmarks.Configurations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// this options may be used to run benchmarks in debugmode and attach a performance profiler
|
||||||
|
/// https://benchmarkdotnet.org/Configs/Configs.htm
|
||||||
|
/// </summary>
|
||||||
|
public class AllowNonOptimized : BaseConfig
|
||||||
|
{
|
||||||
|
public AllowNonOptimized()
|
||||||
|
{
|
||||||
|
AddValidator(JitOptimizationsValidator.DontFailOnError); // ALLOW NON-OPTIMIZED DLLS
|
||||||
|
AddJob(Job.InProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MQTTnet.Benchmarks.Configurations
|
||||||
|
{
|
||||||
|
public class BaseConfig : ManualConfig
|
||||||
|
{
|
||||||
|
public BaseConfig()
|
||||||
|
{
|
||||||
|
AddLogger(DefaultConfig.Instance.GetLoggers().ToArray()); // manual config has no loggers by default
|
||||||
|
AddExporter(DefaultConfig.Instance.GetExporters().ToArray()); // manual config has no exporters by default
|
||||||
|
AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray()); // manual config has no columns by default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Environments;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using BenchmarkDotNet.Toolchains.CsProj;
|
||||||
|
|
||||||
|
namespace MQTTnet.Benchmarks.Configurations
|
||||||
|
{
|
||||||
|
public class RuntimeCompareConfig : BaseConfig
|
||||||
|
{
|
||||||
|
public RuntimeCompareConfig()
|
||||||
|
{
|
||||||
|
AddJob(Job.Default.WithRuntime(ClrRuntime.Net48));
|
||||||
|
AddJob(Job.Default.WithRuntime(CoreRuntime.Core50).WithToolchain(CsProjCoreToolchain.NetCoreApp50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter]
|
[RPlotExporter]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class LoggerBenchmark : BaseBenchmark
|
public class LoggerBenchmark
|
||||||
{
|
{
|
||||||
MqttNetNullLogger _nullLogger;
|
MqttNetNullLogger _nullLogger;
|
||||||
MqttNetSourceLogger _sourceNullLogger;
|
MqttNetSourceLogger _sourceNullLogger;
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<DebugType>Full</DebugType>
|
<DebugType>Full</DebugType>
|
||||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||||
<LangVersion>7.2</LangVersion>
|
<LangVersion>7.2</LangVersion>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0'">
|
||||||
|
<PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -26,12 +28,11 @@
|
|||||||
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="7.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
<ProjectReference Include="..\MQTTnet.Tests\MQTTnet.Tests.csproj" />
|
<ProjectReference Include="..\MQTTnet.Tests\MQTTnet.Tests.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
|
||||||
{
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[RPlotExporter, RankColumn]
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class MemoryCopyBenchmark
|
|
||||||
{
|
|
||||||
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)]
|
|
||||||
public int Length { get; set; }
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
source = new byte[max_length];
|
|
||||||
target = new byte[max_length];
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void Array_Copy()
|
|
||||||
{
|
|
||||||
Array.Copy(source, 0, target, 0, Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Memory_Copy()
|
|
||||||
{
|
|
||||||
MQTTnet.Internal.MqttMemoryHelper.Copy(source, 0, target, 0, Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
using System;
|
||||||
@@ -11,10 +8,14 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a number of topics, publish, subscribe, and wait for response
|
||||||
|
/// </summary>
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MessageDeliveryBenchmark : BaseBenchmark
|
public class MessageDeliveryBenchmark
|
||||||
{
|
{
|
||||||
List<MqttApplicationMessage> _topicPublishMessages;
|
List<MqttApplicationMessage> _topicPublishMessages;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MessageProcessingBenchmark : BaseBenchmark
|
public class MessageProcessingBenchmark
|
||||||
{
|
{
|
||||||
MqttServer _mqttServer;
|
MqttServer _mqttServer;
|
||||||
IMqttClient _mqttClient;
|
IMqttClient _mqttClient;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MessageProcessingMqttConnectionContextBenchmark : BaseBenchmark
|
public class MessageProcessingMqttConnectionContextBenchmark
|
||||||
{
|
{
|
||||||
IWebHost _host;
|
IWebHost _host;
|
||||||
IMqttClient _mqttClient;
|
IMqttClient _mqttClient;
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
|
||||||
{
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class MqttBufferReaderBenchmark
|
|
||||||
{
|
|
||||||
byte[] _buffer;
|
|
||||||
int _bufferLength;
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void GlobalSetup()
|
|
||||||
{
|
|
||||||
var writer = new MqttBufferWriter(1024, 1024);
|
|
||||||
writer.WriteString("hgfjkdfkjlghfdjghdfljkdfhgdlkjfshgsldkfjghsdflkjghdsflkjhrstiuoghlkfjbhnfbutghjoiöjhklötnbhtroliöuhbjntluiobkjzbhtdrlskbhtruhjkfthgbkftgjhgfiklhotriuöhbjtrsioöbtrsötrhträhtrühjtriüoätrhjtsrölbktrbnhtrulöbionhströloubinströoliubhnsöotrunbtöroisntröointröioujhgötiohjgötorshjnbgtöorihbnjtröoihbjntröobntröoibntrjhötrohjbtröoihntröoibnrtoiöbtrjnboöitrhjtnriohötrhjtöroihjtroöihjtroösibntsroönbotöirsbntöoihjntröoihntroöbtrboöitrnhoöitrhjntröoishbnjtröosbhtröbntriohjtröoijtöoitbjöotibjnhöotirhbjntroöibhnjrtoöibnhtroöibnhtörsbnhtöoirbnhtöroibntoörhjnbträöbtrbträbtrbtirbätrsibohjntrsöiobthnjiohjsrtoib");
|
|
||||||
|
|
||||||
_buffer = writer.GetBuffer();
|
|
||||||
_bufferLength = writer.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Use_Span()
|
|
||||||
{
|
|
||||||
var span = _buffer.AsSpan(0, _bufferLength);
|
|
||||||
Encoding.UTF8.GetString(span);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Use_Encoding()
|
|
||||||
{
|
|
||||||
Encoding.UTF8.GetString(_buffer, 0, _bufferLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
@@ -12,7 +9,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MqttPacketReaderWriterBenchmark : BaseBenchmark
|
public class MqttPacketReaderWriterBenchmark
|
||||||
{
|
{
|
||||||
readonly byte[] _demoPayload = new byte[1024];
|
readonly byte[] _demoPayload = new byte[1024];
|
||||||
|
|
||||||
@@ -30,17 +27,17 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
var writer = new MqttBufferWriter(4096, 65535);
|
var writer = new MqttBufferWriter(4096, 65535);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteBinary(_demoPayload);
|
writer.WriteBinaryData(_demoPayload);
|
||||||
writer.WriteByte(0x01);
|
writer.WriteByte(0x01);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteVariableByteInteger(5647382);
|
writer.WriteVariableByteInteger(5647382);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteVariableByteInteger(8574489);
|
writer.WriteVariableByteInteger(8574489);
|
||||||
writer.WriteBinary(_demoPayload);
|
writer.WriteBinaryData(_demoPayload);
|
||||||
writer.WriteByte(2);
|
writer.WriteByte(2);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||||
writer.WriteBinary(_demoPayload);
|
writer.WriteBinaryData(_demoPayload);
|
||||||
|
|
||||||
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||||
}
|
}
|
||||||
@@ -83,11 +80,11 @@ namespace MQTTnet.Benchmarks
|
|||||||
writer.WriteVariableByteInteger(5647382);
|
writer.WriteVariableByteInteger(5647382);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteVariableByteInteger(8574589);
|
writer.WriteVariableByteInteger(8574589);
|
||||||
writer.WriteBinary(_demoPayload);
|
writer.WriteBinaryData(_demoPayload);
|
||||||
writer.WriteByte(2);
|
writer.WriteByte(2);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||||
writer.WriteBinary(_demoPayload);
|
writer.WriteBinaryData(_demoPayload);
|
||||||
|
|
||||||
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 BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
@@ -17,7 +16,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MqttTcpChannelBenchmark : BaseBenchmark
|
public sealed class MqttTcpChannelBenchmark
|
||||||
{
|
{
|
||||||
MqttServer _mqttServer;
|
MqttServer _mqttServer;
|
||||||
IMqttChannel _serverChannel;
|
IMqttChannel _serverChannel;
|
||||||
@@ -71,8 +70,8 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
while (read < expected)
|
while (read < expected)
|
||||||
{
|
{
|
||||||
var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
var readresult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||||
read += readResult;
|
read += readresult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +79,9 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
|
|
||||||
var buffer = new ArraySegment<byte>(new byte[size]);
|
|
||||||
|
|
||||||
for (var i = 0; i < iterations; i++)
|
for (var i = 0; i < iterations; i++)
|
||||||
{
|
{
|
||||||
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
|
await _serverChannel.WriteAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,140 +3,81 @@
|
|||||||
// 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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using BenchmarkDotNet.Loggers;
|
|
||||||
using BenchmarkDotNet.Running;
|
using BenchmarkDotNet.Running;
|
||||||
|
using MQTTnet.Benchmarks.Configurations;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
static int _selectedBenchmarkIndex;
|
public static void Main(string[] args)
|
||||||
static List<Type> _benchmarks;
|
|
||||||
|
|
||||||
public static void Main(string[] arguments)
|
|
||||||
{
|
{
|
||||||
_benchmarks = CollectBenchmarks();
|
|
||||||
HandleArguments(arguments);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
RenderMenu();
|
|
||||||
|
|
||||||
var key = Console.ReadKey(true);
|
|
||||||
|
|
||||||
if (key.Key == ConsoleKey.DownArrow)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex++;
|
|
||||||
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (key.Key == ConsoleKey.UpArrow)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex--;
|
|
||||||
if (_selectedBenchmarkIndex < 0)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex = _benchmarks.Count - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (key.Key == ConsoleKey.Escape)
|
|
||||||
{
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
else if (key.Key == ConsoleKey.Enter)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BenchmarkRunner.Run(_benchmarks[_selectedBenchmarkIndex]);
|
|
||||||
|
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
||||||
Console.WriteLine("Press any key to exit");
|
|
||||||
Console.ReadLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Type> CollectBenchmarks()
|
|
||||||
{
|
|
||||||
var benchmarks = new List<Type>();
|
|
||||||
|
|
||||||
var types = typeof(Program).Assembly.GetExportedTypes();
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
if (typeof(BaseBenchmark).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
if (type != typeof(BaseBenchmark))
|
|
||||||
{
|
|
||||||
benchmarks.Add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return benchmarks.OrderBy(b => b.Name).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void HandleArguments(string[] arguments)
|
|
||||||
{
|
|
||||||
if (arguments.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow for preselection to avoid developer frustration.
|
|
||||||
|
|
||||||
if (int.TryParse(arguments[0], out var benchmarkIndex))
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex = benchmarkIndex;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_selectedBenchmarkIndex = _benchmarks.FindIndex(b => b.Name.Equals(arguments[0]));
|
|
||||||
|
|
||||||
if (_selectedBenchmarkIndex < 0)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
|
|
||||||
{
|
|
||||||
_selectedBenchmarkIndex = _benchmarks.Count - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RenderMenu()
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
|
|
||||||
Console.WriteLine($"MQTTnet - Benchmarks ({TargetFrameworkProvider.TargetFramework})");
|
Console.WriteLine($"MQTTnet - Benchmarks ({TargetFrameworkProvider.TargetFramework})");
|
||||||
Console.WriteLine("-----------------------------------------------");
|
Console.WriteLine("--------------------------------------------------------");
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
Console.WriteLine("1 = MessageProcessingBenchmark");
|
||||||
Console.WriteLine("Press arrow keys for benchmark selection");
|
Console.WriteLine("2 = SerializerBenchmark");
|
||||||
Console.WriteLine("Press Enter for benchmark execution");
|
Console.WriteLine("3 = LoggerBenchmark");
|
||||||
Console.WriteLine("Press Esc for exit");
|
Console.WriteLine("4 = TopicFilterComparerBenchmark");
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.WriteLine("5 = ChannelAdapterBenchmark");
|
||||||
Console.WriteLine();
|
Console.WriteLine("6 = MqttTcpChannelBenchmark");
|
||||||
|
Console.WriteLine("7 = TcpPipesBenchmark");
|
||||||
|
Console.WriteLine("8 = MessageProcessingMqttConnectionContextBenchmark");
|
||||||
|
Console.WriteLine("9 = ServerProcessingBenchmark");
|
||||||
|
Console.WriteLine("a = MqttPacketReaderWriterBenchmark");
|
||||||
|
Console.WriteLine("b = RoundtripBenchmark");
|
||||||
|
Console.WriteLine("c = SubscribeBenchmark");
|
||||||
|
Console.WriteLine("d = UnsubscribeBenchmark");
|
||||||
|
Console.WriteLine("e = MessageDeliveryBenchmark");
|
||||||
|
|
||||||
for (var index = 0; index < _benchmarks.Count; index++)
|
var pressedKey = Console.ReadKey(true);
|
||||||
|
switch (pressedKey.KeyChar)
|
||||||
{
|
{
|
||||||
var benchmark = _benchmarks[index];
|
case '1':
|
||||||
|
BenchmarkRunner.Run<MessageProcessingBenchmark>();
|
||||||
if (_selectedBenchmarkIndex == index)
|
break;
|
||||||
{
|
case '2':
|
||||||
Console.Write("-> ");
|
BenchmarkRunner.Run<SerializerBenchmark>();
|
||||||
}
|
break;
|
||||||
else
|
case '3':
|
||||||
{
|
BenchmarkRunner.Run<LoggerBenchmark>();
|
||||||
Console.Write(" ");
|
break;
|
||||||
}
|
case '4':
|
||||||
|
BenchmarkRunner.Run<TopicFilterComparerBenchmark>();
|
||||||
Console.WriteLine(benchmark.Name);
|
break;
|
||||||
|
case '5':
|
||||||
|
BenchmarkRunner.Run<ChannelAdapterBenchmark>();
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
BenchmarkRunner.Run<MqttTcpChannelBenchmark>();
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
BenchmarkRunner.Run<TcpPipesBenchmark>();
|
||||||
|
break;
|
||||||
|
case '8':
|
||||||
|
BenchmarkRunner.Run<MessageProcessingMqttConnectionContextBenchmark>(new RuntimeCompareConfig());
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
BenchmarkRunner.Run<ServerProcessingBenchmark>();
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
BenchmarkRunner.Run(typeof(MqttPacketReaderWriterBenchmark));
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
BenchmarkRunner.Run<RoundtripProcessingBenchmark>();
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
BenchmarkRunner.Run<SubscribeBenchmark>();
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
BenchmarkRunner.Run<UnsubscribeBenchmark>();
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.AspNetCore;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Pipelines;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
|
||||||
{
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[RPlotExporter, RankColumn]
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class ReaderExtensionsBenchmark
|
|
||||||
{
|
|
||||||
MqttPacketFormatterAdapter mqttPacketFormatter;
|
|
||||||
MemoryStream stream;
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void GlobalSetup()
|
|
||||||
{
|
|
||||||
mqttPacketFormatter = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
|
||||||
var mqttMessage = new MqttApplicationMessageBuilder()
|
|
||||||
.WithTopic("topic")
|
|
||||||
.WithPayload(new byte[10 * 1024])
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var packet = MqttPacketFactories.Publish.Create(mqttMessage);
|
|
||||||
|
|
||||||
var buffer = mqttPacketFormatter.Encode(packet);
|
|
||||||
stream = new MemoryStream();
|
|
||||||
stream.Write(buffer.Packet);
|
|
||||||
stream.Write(buffer.Payload);
|
|
||||||
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]
|
|
||||||
public async Task After()
|
|
||||||
{
|
|
||||||
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.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (formatter == null) throw new ArgumentNullException(nameof(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class RoundtripProcessingBenchmark : BaseBenchmark
|
public class RoundtripProcessingBenchmark
|
||||||
{
|
{
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void GlobalSetup()
|
public void GlobalSetup()
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Pipelines;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
|
||||||
{
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[RPlotExporter, RankColumn]
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class SendPacketAsyncBenchmark : BaseBenchmark
|
|
||||||
{
|
|
||||||
MemoryStream stream;
|
|
||||||
MqttPacketBuffer buffer;
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void GlobalSetup()
|
|
||||||
{
|
|
||||||
stream = new MemoryStream(1024);
|
|
||||||
var packet = new ArraySegment<byte>(new byte[10]);
|
|
||||||
buffer = new MqttPacketBuffer(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public async ValueTask Before()
|
|
||||||
{
|
|
||||||
stream.Position = 0;
|
|
||||||
var output = PipeWriter.Create(stream);
|
|
||||||
|
|
||||||
WritePacketBuffer(output, buffer);
|
|
||||||
await output.FlushAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public async ValueTask After()
|
|
||||||
{
|
|
||||||
stream.Position = 0;
|
|
||||||
var output = PipeWriter.Create(stream);
|
|
||||||
|
|
||||||
if (buffer.Payload.Count == 0)
|
|
||||||
{
|
|
||||||
await output.WriteAsync(buffer.Packet).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WritePacketBuffer(output, buffer);
|
|
||||||
await output.FlushAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
|
|
||||||
{
|
|
||||||
// copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
|
|
||||||
// MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
|
|
||||||
|
|
||||||
var span = output.GetSpan(buffer.Length);
|
|
||||||
|
|
||||||
buffer.Packet.AsSpan().CopyTo(span);
|
|
||||||
buffer.Payload.AsSpan().CopyTo(span.Slice(buffer.Packet.Count));
|
|
||||||
|
|
||||||
output.Advance(buffer.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
public void Deserialize_10000_Messages()
|
public void Deserialize_10000_Messages()
|
||||||
{
|
{
|
||||||
var channel = new BenchmarkMqttChannel(_serializedPacket);
|
var channel = new BenchmarkMqttChannel(_serializedPacket);
|
||||||
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), new MqttNetEventLogger());
|
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
||||||
|
|
||||||
for (var i = 0; i < 10000; i++)
|
for (var i = 0; i < 10000; i++)
|
||||||
{
|
{
|
||||||
@@ -104,7 +104,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
return Task.FromResult(count);
|
return Task.FromResult(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
@@ -12,7 +8,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class ServerProcessingBenchmark : BaseBenchmark
|
public class ServerProcessingBenchmark
|
||||||
{
|
{
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void GlobalSetup()
|
public void GlobalSetup()
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
@@ -12,7 +8,7 @@ using System.Linq;
|
|||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class SubscribeBenchmark : BaseBenchmark
|
public class SubscribeBenchmark
|
||||||
{
|
{
|
||||||
MqttServer _mqttServer;
|
MqttServer _mqttServer;
|
||||||
IMqttClient _mqttClient;
|
IMqttClient _mqttClient;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class TcpPipesBenchmark : BaseBenchmark
|
public class TcpPipesBenchmark
|
||||||
{
|
{
|
||||||
IDuplexPipe _client;
|
IDuplexPipe _client;
|
||||||
IDuplexPipe _server;
|
IDuplexPipe _server;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter]
|
[RPlotExporter]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class TopicFilterComparerBenchmark : BaseBenchmark
|
public class TopicFilterComparerBenchmark
|
||||||
{
|
{
|
||||||
static readonly char[] TopicLevelSeparator = { '/' };
|
static readonly char[] TopicLevelSeparator = { '/' };
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
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,
|
out Dictionary<string, List<string>> topicsByPublisher,
|
||||||
out Dictionary<string, List<string>> topicsByPublisher,
|
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
||||||
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher
|
||||||
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher)
|
)
|
||||||
{
|
{
|
||||||
topicsByPublisher = new Dictionary<string, List<string>>();
|
topicsByPublisher = new Dictionary<string, List<string>>();
|
||||||
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||||
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||||
|
|
||||||
// Find some reasonable distribution across three topic levels
|
// Find some reasonable distribution across three topic levels
|
||||||
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, 1.0 / 3.0);
|
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
|
||||||
if (topicsPerLevel <= 0)
|
if (topicsPerLevel <= 0)
|
||||||
{
|
{
|
||||||
topicsPerLevel = 1;
|
topicsPerLevel = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var numLevel1Topics = topicsPerLevel;
|
int numLevel1Topics = topicsPerLevel;
|
||||||
var numLevel2Topics = topicsPerLevel;
|
int numLevel2Topics = topicsPerLevel;
|
||||||
|
|
||||||
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
||||||
if (maxNumLevel3Topics <= 0)
|
if (maxNumLevel3Topics <= 0)
|
||||||
@@ -34,7 +35,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
for (var p = 0; p < numPublishers; ++p)
|
for (var p = 0; p < numPublishers; ++p)
|
||||||
{
|
{
|
||||||
var publisherTopicCount = 0;
|
int publisherTopicCount = 0;
|
||||||
var publisherName = "pub" + p;
|
var publisherName = "pub" + p;
|
||||||
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
||||||
{
|
{
|
||||||
@@ -43,22 +44,19 @@ namespace MQTTnet.Benchmarks
|
|||||||
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
||||||
{
|
{
|
||||||
if (publisherTopicCount >= numTopicsPerPublisher)
|
if (publisherTopicCount >= numTopicsPerPublisher)
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
var topic = $"{publisherName}/building{l1 + 1}/level{l2 + 1}/sensor{l3 + 1}";
|
var topic = string.Format("{0}/building{1}/level{2}/sensor{3}", publisherName, l1 + 1, l2 + 1, l3 + 1);
|
||||||
AddPublisherTopic(publisherName, topic, topicsByPublisher);
|
AddPublisherTopic(publisherName, topic, topicsByPublisher);
|
||||||
|
|
||||||
if (l2 == 0)
|
if (l2 == 0)
|
||||||
{
|
{
|
||||||
var singleWildcardTopic = $"{publisherName}/building{l1 + 1}/+/sensor{l3 + 1}";
|
var singleWildcardTopic = string.Format("{0}/building{1}/+/sensor{2}", publisherName, l1 + 1, l3 + 1);
|
||||||
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
||||||
}
|
}
|
||||||
|
if ((l1 == 0) && (l3 == 0))
|
||||||
if (l1 == 0 && l3 == 0)
|
|
||||||
{
|
{
|
||||||
var multiWildcardTopic = $"{publisherName}/+/level{l2 + 1}/+";
|
var multiWildcardTopic = string.Format("{0}/+/level{1}/+", publisherName, l2 + 1);
|
||||||
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +75,6 @@ namespace MQTTnet.Benchmarks
|
|||||||
topicList = new List<string>();
|
topicList = new List<string>();
|
||||||
topicsByPublisher.Add(publisherName, topicList);
|
topicsByPublisher.Add(publisherName, topicList);
|
||||||
}
|
}
|
||||||
|
|
||||||
topicList.Add(topic);
|
topicList.Add(topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -11,7 +7,7 @@ using System.Linq;
|
|||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class UnsubscribeBenchmark : BaseBenchmark
|
public class UnsubscribeBenchmark
|
||||||
{
|
{
|
||||||
MqttServer _mqttServer;
|
MqttServer _mqttServer;
|
||||||
IMqttClient _mqttClient;
|
IMqttClient _mqttClient;
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="BenchmarkDotNet" version="0.10.14" targetFramework="net462" />
|
||||||
|
<package id="BenchmarkDotNet.Core" version="0.10.14" targetFramework="net462" />
|
||||||
|
<package id="BenchmarkDotNet.Toolchains.Roslyn" version="0.10.14" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.CodeAnalysis.Common" version="2.6.1" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.CodeAnalysis.CSharp" version="2.6.1" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.DotNet.InternalAbstractions" version="1.0.0" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.DotNet.PlatformAbstractions" version="1.1.1" targetFramework="net462" />
|
||||||
|
<package id="Microsoft.Win32.Registry" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Collections.Immutable" version="1.3.1" targetFramework="net462" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net462" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Threading.Thread" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.ValueTuple" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.XmlSerializer" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.XPath" version="4.3.0" targetFramework="net462" />
|
||||||
|
<package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net462" />
|
||||||
|
</packages>
|
||||||
@@ -43,7 +43,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
Task StartAsync(ManagedMqttClientOptions options);
|
Task StartAsync(ManagedMqttClientOptions options);
|
||||||
|
|
||||||
Task StopAsync(bool cleanDisconnect = true);
|
Task StopAsync();
|
||||||
|
|
||||||
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<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>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
CancellationTokenSource _publishingCancellationToken;
|
CancellationTokenSource _publishingCancellationToken;
|
||||||
|
|
||||||
ManagedMqttClientStorageManager _storageManager;
|
ManagedMqttClientStorageManager _storageManager;
|
||||||
bool _isCleanDisconnect;
|
|
||||||
|
|
||||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
@@ -161,14 +160,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
throw new InvalidOperationException("call StartAsync before publishing messages");
|
throw new InvalidOperationException("call StartAsync before publishing messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage);
|
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
||||||
|
|
||||||
ManagedMqttApplicationMessage removedMessage = null;
|
ManagedMqttApplicationMessage removedMessage = null;
|
||||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false))
|
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||||
{
|
{
|
||||||
@@ -256,12 +255,10 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
_logger.Info("Started");
|
_logger.Info("Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync(bool cleanDisconnect = true)
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
_isCleanDisconnect = cleanDisconnect;
|
|
||||||
|
|
||||||
StopPublishing();
|
StopPublishing();
|
||||||
StopMaintainingConnection();
|
StopMaintainingConnection();
|
||||||
|
|
||||||
@@ -299,7 +296,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UnsubscribeAsync(ICollection<string> topics)
|
public Task UnsubscribeAsync(ICollection<string> topics)
|
||||||
@@ -322,7 +319,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -353,13 +350,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task HandleSubscriptionExceptionAsync(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
async Task HandleSubscriptionExceptionAsync(Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||||
|
|
||||||
if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
|
if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception, addedSubscriptions, removedSubscriptions)).ConfigureAwait(false);
|
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,12 +382,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isCleanDisconnect)
|
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
||||||
{
|
{
|
||||||
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
||||||
{
|
|
||||||
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -457,13 +451,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
_logger.Info("Publishing subscriptions at reconnect");
|
_logger.Info("Publishing subscriptions at reconnect");
|
||||||
|
|
||||||
List<MqttTopicFilter> topicFilters = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_reconnectSubscriptions.Any())
|
if (_reconnectSubscriptions.Any())
|
||||||
{
|
{
|
||||||
topicFilters = new List<MqttTopicFilter>();
|
var topicFilters = new List<MqttTopicFilter>();
|
||||||
|
|
||||||
foreach (var sub in _reconnectSubscriptions)
|
foreach (var sub in _reconnectSubscriptions)
|
||||||
{
|
{
|
||||||
@@ -481,7 +473,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await HandleSubscriptionExceptionAsync(exception, topicFilters, null).ConfigureAwait(false);
|
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,9 +585,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
|
await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
//clear because these worked, maybe the subscribe below will fail, only report those
|
|
||||||
removedSubscriptions.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addedSubscriptions != null && addedSubscriptions.Any())
|
if (addedSubscriptions != null && addedSubscriptions.Any())
|
||||||
@@ -612,7 +601,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await HandleSubscriptionExceptionAsync(exception, addedSubscriptions, removedSubscriptions).ConfigureAwait(false);
|
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,7 +693,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||||
|
|
||||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||||
{
|
{
|
||||||
// While publishing this message, this.PublishAsync could have booted this
|
// While publishing this message, this.PublishAsync could have booted this
|
||||||
// message off the queue to make room for another (when using a cap
|
// message off the queue to make room for another (when using a cap
|
||||||
@@ -735,7 +724,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
//contradict the expected behavior of QoS 1 and 2, that's also true
|
//contradict the expected behavior of QoS 1 and 2, that's also true
|
||||||
//for the usage of a message queue cap, so it's still consistent
|
//for the usage of a message queue cap, so it's still consistent
|
||||||
//with prior behavior in that way.
|
//with prior behavior in that way.
|
||||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||||
{
|
{
|
||||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
||||||
/// Amazon Web Services (AWS) limits this number to 8. The default is int.MaxValue.
|
/// Amazon AWS limits this number to 8. The default is int.MaxValue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
_messages.Add(applicationMessage);
|
_messages.Add(applicationMessage);
|
||||||
await SaveAsync().ConfigureAwait(false);
|
await SaveAsync().ConfigureAwait(false);
|
||||||
@@ -45,7 +45,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var index = _messages.IndexOf(applicationMessage);
|
var index = _messages.IndexOf(applicationMessage);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
|
|||||||
@@ -2,33 +2,17 @@
|
|||||||
// 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 MQTTnet.Packets;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.ManagedClient
|
namespace MQTTnet.Extensions.ManagedClient
|
||||||
{
|
{
|
||||||
public class ManagedProcessFailedEventArgs : EventArgs
|
public class ManagedProcessFailedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public ManagedProcessFailedEventArgs(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
public ManagedProcessFailedEventArgs(Exception exception)
|
||||||
{
|
{
|
||||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||||
|
|
||||||
if (addedSubscriptions != null)
|
|
||||||
{
|
|
||||||
AddedSubscriptions = new List<string>(addedSubscriptions.Select(item => item.Topic));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removedSubscriptions != null)
|
|
||||||
{
|
|
||||||
RemovedSubscriptions = new List<string>(removedSubscriptions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception Exception { get; }
|
public Exception Exception { get; }
|
||||||
|
|
||||||
public List<string> AddedSubscriptions { get; }
|
|
||||||
public List<string> RemovedSubscriptions { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,10 +12,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||||
{
|
{
|
||||||
if (factory == null)
|
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mqttClient == null)
|
if (mqttClient == null)
|
||||||
{
|
{
|
||||||
@@ -27,22 +24,10 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
if (factory == null)
|
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||||
{
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
throw new ArgumentNullException(nameof(factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory _)
|
|
||||||
{
|
|
||||||
return new ManagedMqttClientOptionsBuilder();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
@@ -33,8 +33,7 @@
|
|||||||
<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>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqttClient mqttClient)
|
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqttClient mqttClient)
|
||||||
{
|
{
|
||||||
return factory.CreateMqttRpcClient(
|
return factory.CreateMqttRpcClient(mqttClient, new MqttRpcClientOptions
|
||||||
mqttClient,
|
{
|
||||||
new MqttRpcClientOptions
|
TopicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy()
|
||||||
{
|
});
|
||||||
TopicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
|
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
|
||||||
|
|||||||
@@ -2,26 +2,23 @@
|
|||||||
// 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.Concurrent;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.Rpc
|
namespace MQTTnet.Extensions.Rpc
|
||||||
{
|
{
|
||||||
public sealed class MqttRpcClient : IMqttRpcClient
|
public sealed class MqttRpcClient : IMqttRpcClient
|
||||||
{
|
{
|
||||||
|
readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||||
readonly IMqttClient _mqttClient;
|
readonly IMqttClient _mqttClient;
|
||||||
readonly MqttRpcClientOptions _options;
|
readonly MqttRpcClientOptions _options;
|
||||||
|
|
||||||
readonly ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>>();
|
|
||||||
|
|
||||||
public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options)
|
public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options)
|
||||||
{
|
{
|
||||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||||
@@ -30,18 +27,6 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
_mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
|
_mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
|
|
||||||
|
|
||||||
foreach (var tcs in _waitingCalls)
|
|
||||||
{
|
|
||||||
tcs.Value.TrySetCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
_waitingCalls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||||
@@ -64,13 +49,15 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
|
|
||||||
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken = default)
|
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (methodName == null)
|
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(methodName));
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = new TopicGenerationContext(_mqttClient, _options, methodName, qualityOfServiceLevel);
|
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(new TopicGenerationContext
|
||||||
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
|
{
|
||||||
|
MethodName = methodName,
|
||||||
|
QualityOfServiceLevel = qualityOfServiceLevel,
|
||||||
|
MqttClient = _mqttClient,
|
||||||
|
Options = _options
|
||||||
|
});
|
||||||
|
|
||||||
var requestTopic = topicNames.RequestTopic;
|
var requestTopic = topicNames.RequestTopic;
|
||||||
var responseTopic = topicNames.ResponseTopic;
|
var responseTopic = topicNames.ResponseTopic;
|
||||||
@@ -85,34 +72,34 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
throw new MqttProtocolViolationException("RPC response topic is empty.");
|
throw new MqttProtocolViolationException("RPC response topic is empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestMessageBuilder = new MqttApplicationMessageBuilder().WithTopic(requestTopic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel);
|
var requestMessage = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic(requestTopic)
|
||||||
if (_mqttClient.Options.ProtocolVersion == MqttProtocolVersion.V500)
|
.WithPayload(payload)
|
||||||
{
|
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||||
requestMessageBuilder.WithResponseTopic(responseTopic);
|
.WithResponseTopic(responseTopic)
|
||||||
}
|
.Build();
|
||||||
|
|
||||||
var requestMessage = requestMessageBuilder.Build();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var awaitable = new AsyncTaskCompletionSource<byte[]>();
|
#if NET452
|
||||||
|
var awaitable = new TaskCompletionSource<byte[]>();
|
||||||
|
#else
|
||||||
|
var awaitable = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!_waitingCalls.TryAdd(responseTopic, awaitable))
|
if (!_waitingCalls.TryAdd(responseTopic, awaitable))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(responseTopic, qualityOfServiceLevel).Build();
|
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
|
||||||
|
.WithTopicFilter(responseTopic, qualityOfServiceLevel)
|
||||||
|
.Build();
|
||||||
|
|
||||||
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();
|
|
||||||
}))
|
|
||||||
{
|
{
|
||||||
return await awaitable.Task.ConfigureAwait(false);
|
return await awaitable.Task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -120,7 +107,8 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_waitingCalls.TryRemove(responseTopic, out _);
|
_waitingCalls.TryRemove(responseTopic, out _);
|
||||||
await _mqttClient.UnsubscribeAsync(responseTopic, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
|
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,16 +116,31 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
||||||
{
|
{
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray();
|
#if NET452
|
||||||
awaitable.TrySetResult(payloadBuffer);
|
Task.Run(() => awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload));
|
||||||
|
#else
|
||||||
|
awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set this message to handled to that other code can avoid execution etc.
|
// Set this message to handled to that other code can avoid execution etc.
|
||||||
eventArgs.IsHandled = true;
|
eventArgs.IsHandled = true;
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
|
||||||
|
|
||||||
|
foreach (var tcs in _waitingCalls)
|
||||||
|
{
|
||||||
|
tcs.Value.TrySetCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
_waitingCalls.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,13 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
|
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
|
||||||
|
|
||||||
|
public MqttRpcClientOptionsBuilder WithTopicGenerationStrategy(IMqttRpcClientTopicGenerationStrategy value)
|
||||||
|
{
|
||||||
|
_topicGenerationStrategy = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttRpcClientOptions Build()
|
public MqttRpcClientOptions Build()
|
||||||
{
|
{
|
||||||
return new MqttRpcClientOptions
|
return new MqttRpcClientOptions
|
||||||
@@ -17,12 +24,5 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
TopicGenerationStrategy = _topicGenerationStrategy
|
TopicGenerationStrategy = _topicGenerationStrategy
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttRpcClientOptionsBuilder WithTopicGenerationStrategy(IMqttRpcClientTopicGenerationStrategy value)
|
|
||||||
{
|
|
||||||
_topicGenerationStrategy = value ?? throw new ArgumentNullException(nameof(value));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-5
@@ -10,11 +10,6 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
|
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||||
|
|||||||
@@ -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 MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -10,29 +9,20 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
public sealed class TopicGenerationContext
|
public sealed class TopicGenerationContext
|
||||||
{
|
{
|
||||||
public TopicGenerationContext(IMqttClient mqttClient, MqttRpcClientOptions options, string methodName, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
public string MethodName { get; set; }
|
||||||
{
|
|
||||||
MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
|
|
||||||
QualityOfServiceLevel = qualityOfServiceLevel;
|
|
||||||
MqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
|
||||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MethodName { get; }
|
|
||||||
|
|
||||||
public IMqttClient MqttClient { get; }
|
|
||||||
|
|
||||||
public MqttRpcClientOptions Options { 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 that defines the guarantee of delivery for a specific message.
|
||||||
/// that defines the guarantee of delivery for a specific message.
|
/// There are 3 QoS levels in MQTT:
|
||||||
/// There are 3 QoS levels in MQTT:
|
/// - At most once (0): Message gets delivered no time, once or multiple times.
|
||||||
/// - At most once (0): Message gets delivered no time, once or multiple times.
|
/// - At least once (1): Message gets delivered at least once (one time or more often).
|
||||||
/// - At least once (1): Message gets delivered at least once (one time or more often).
|
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
|
||||||
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
|
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||||
|
|
||||||
|
public IMqttClient MqttClient { get; set; }
|
||||||
|
|
||||||
|
public MqttRpcClientOptions Options { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
@@ -33,8 +33,7 @@
|
|||||||
<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>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
{
|
{
|
||||||
public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory)
|
public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory)
|
||||||
{
|
{
|
||||||
if (mqttFactory == null)
|
if (mqttFactory == null) throw new ArgumentNullException(nameof(mqttFactory));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(mqttFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory());
|
return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
@@ -15,17 +14,16 @@ using System.Threading.Tasks;
|
|||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Internal;
|
|
||||||
using SuperSocket.ClientEngine;
|
using SuperSocket.ClientEngine;
|
||||||
using WebSocket4Net;
|
using WebSocket4Net;
|
||||||
using DataReceivedEventArgs = WebSocket4Net.DataReceivedEventArgs;
|
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.WebSocket4Net
|
namespace MQTTnet.Extensions.WebSocket4Net
|
||||||
{
|
{
|
||||||
public sealed class WebSocket4NetMqttChannel : IMqttChannel
|
public sealed class WebSocket4NetMqttChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
readonly MqttClientOptions _clientOptions;
|
|
||||||
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||||
|
|
||||||
|
readonly MqttClientOptions _clientOptions;
|
||||||
readonly MqttClientWebSocketOptions _webSocketOptions;
|
readonly MqttClientWebSocketOptions _webSocketOptions;
|
||||||
|
|
||||||
WebSocket _webSocket;
|
WebSocket _webSocket;
|
||||||
@@ -36,13 +34,13 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
|
_webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; }
|
|
||||||
|
|
||||||
public string Endpoint => _webSocketOptions.Uri;
|
public string Endpoint => _webSocketOptions.Uri;
|
||||||
|
|
||||||
public bool IsSecureConnection { get; private set; }
|
public bool IsSecureConnection { get; private set; }
|
||||||
|
|
||||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
public X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var uri = _webSocketOptions.Uri;
|
var uri = _webSocketOptions.Uri;
|
||||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
|
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -57,12 +55,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
|
#if NET48 || NETCOREAPP3_1 || NET5 || NET6
|
||||||
|
var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||||
#if NET48 || NETCOREAPP3_0_OR_GREATER
|
|
||||||
var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | SslProtocols.Tls13;
|
|
||||||
#else
|
#else
|
||||||
var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
|
var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
||||||
@@ -93,7 +89,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
var receiveBufferSize = 0;
|
var receiveBufferSize = 0;
|
||||||
|
|
||||||
var certificates = new X509CertificateCollection();
|
var certificates = new X509CertificateCollection();
|
||||||
if (_webSocketOptions.TlsOptions?.Certificates != null)
|
if (_webSocketOptions?.TlsOptions?.Certificates != null)
|
||||||
{
|
{
|
||||||
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
|
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
|
||||||
{
|
{
|
||||||
@@ -110,13 +106,15 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
NoDelay = true,
|
NoDelay = true,
|
||||||
Security =
|
Security =
|
||||||
{
|
{
|
||||||
AllowUnstrustedCertificate = _webSocketOptions.TlsOptions?.AllowUntrustedCertificates == true,
|
AllowUnstrustedCertificate = _webSocketOptions?.TlsOptions?.AllowUntrustedCertificates == true,
|
||||||
AllowCertificateChainErrors = _webSocketOptions.TlsOptions?.IgnoreCertificateChainErrors == true,
|
AllowCertificateChainErrors = _webSocketOptions?.TlsOptions?.IgnoreCertificateChainErrors == true,
|
||||||
Certificates = certificates
|
Certificates = certificates
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return ConnectInternalAsync(cancellationToken);
|
await ConnectInternalAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
@@ -126,20 +124,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocket.Close();
|
_webSocket.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_webSocket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_webSocket.DataReceived -= OnDataReceived;
|
|
||||||
_webSocket.Error -= OnError;
|
|
||||||
_webSocket.Dispose();
|
|
||||||
_webSocket = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
@@ -169,10 +154,36 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
return Task.FromResult(readBytes);
|
return Task.FromResult(readBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_webSocket.Send(buffer.Array, buffer.Offset, buffer.Count);
|
_webSocket.Send(buffer, offset, count);
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_webSocket == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_webSocket.DataReceived -= OnDataReceived;
|
||||||
|
_webSocket.Error -= OnError;
|
||||||
|
_webSocket.Dispose();
|
||||||
|
_webSocket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnError(object sender, ErrorEventArgs e)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.Write(e.Exception.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var @byte in e.Data)
|
||||||
|
{
|
||||||
|
_receiveBuffer.Add(@byte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
||||||
@@ -180,45 +191,39 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocket.Error += OnError;
|
_webSocket.Error += OnError;
|
||||||
_webSocket.DataReceived += OnDataReceived;
|
_webSocket.DataReceived += OnDataReceived;
|
||||||
|
|
||||||
var promise = new AsyncTaskCompletionSource<bool>();
|
var taskCompletionSource = new TaskCompletionSource<Exception>();
|
||||||
|
|
||||||
void ErrorHandler(object sender, ErrorEventArgs e)
|
void ErrorHandler(object sender, ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
promise.TrySetException(e.Exception);
|
taskCompletionSource.TrySetResult(e.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenedHandler(object sender, EventArgs e)
|
void SuccessHandler(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
promise.TrySetResult(true);
|
taskCompletionSource.TrySetResult(null);
|
||||||
}
|
|
||||||
|
|
||||||
void ClosedHandler(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
promise.TrySetException(new MqttCommunicationException("Connection closed."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_webSocket.Opened += OpenedHandler;
|
_webSocket.Opened += SuccessHandler;
|
||||||
_webSocket.Closed += ClosedHandler;
|
|
||||||
_webSocket.Error += ErrorHandler;
|
_webSocket.Error += ErrorHandler;
|
||||||
|
|
||||||
using (var timeout = new CancellationTokenSource(_clientOptions.Timeout))
|
#pragma warning disable AsyncFixer02 // Long-running or blocking operations inside an async method
|
||||||
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken))
|
_webSocket.Open();
|
||||||
|
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
|
||||||
|
|
||||||
|
using (var timeoutCts = new CancellationTokenSource(_clientOptions.Timeout))
|
||||||
|
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
|
||||||
{
|
{
|
||||||
using (linkedCts.Token.Register(() => promise.TrySetCanceled()))
|
using (linkedCts.Token.Register(() => taskCompletionSource.TrySetCanceled()))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// OpenAsync does basically the same but without support for a cancellation token.
|
await taskCompletionSource.Task.ConfigureAwait(false);
|
||||||
// So we do it on our own!
|
|
||||||
_webSocket.Open();
|
|
||||||
|
|
||||||
await promise.Task.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
var timeoutReached = timeout.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
|
var timeoutReached = timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
|
||||||
if (timeoutReached)
|
if (timeoutReached)
|
||||||
{
|
{
|
||||||
throw new MqttCommunicationTimedOutException(exception);
|
throw new MqttCommunicationTimedOutException(exception);
|
||||||
@@ -245,23 +250,9 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_webSocket.Opened -= OpenedHandler;
|
_webSocket.Opened -= SuccessHandler;
|
||||||
_webSocket.Closed -= ClosedHandler;
|
|
||||||
_webSocket.Error -= ErrorHandler;
|
_webSocket.Error -= ErrorHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDataReceived(object sender, DataReceivedEventArgs e)
|
|
||||||
{
|
|
||||||
foreach (var @byte in e.Data)
|
|
||||||
{
|
|
||||||
_receiveBuffer.Add(@byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnError(object sender, ErrorEventArgs e)
|
|
||||||
{
|
|
||||||
Debug.Write(e.Exception.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,12 +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.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using System;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.WebSocket4Net
|
namespace MQTTnet.Extensions.WebSocket4Net
|
||||||
{
|
{
|
||||||
@@ -15,10 +15,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
{
|
{
|
||||||
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (options.ChannelOptions)
|
switch (options.ChannelOptions)
|
||||||
{
|
{
|
||||||
@@ -26,28 +23,24 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
{
|
{
|
||||||
return new MqttChannelAdapter(
|
return new MqttChannelAdapter(
|
||||||
new MqttTcpChannel(options),
|
new MqttTcpChannel(options),
|
||||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535)),
|
||||||
logger)
|
packetInspector,
|
||||||
{
|
logger);
|
||||||
PacketInspector = packetInspector
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MqttClientWebSocketOptions webSocketOptions:
|
case MqttClientWebSocketOptions webSocketOptions:
|
||||||
{
|
{
|
||||||
return new MqttChannelAdapter(
|
return new MqttChannelAdapter(
|
||||||
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
||||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4068, 65535)),
|
||||||
logger)
|
packetInspector,
|
||||||
{
|
logger);
|
||||||
PacketInspector = packetInspector
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
|
||||||
{
|
|
||||||
public sealed class AsyncLockTest
|
|
||||||
{
|
|
||||||
public async Task Run()
|
|
||||||
{
|
|
||||||
var asyncLock = new AsyncLock();
|
|
||||||
|
|
||||||
using (var cancellationToken = new CancellationTokenSource())
|
|
||||||
{
|
|
||||||
for (var i = 0; i < 100000; i++)
|
|
||||||
{
|
|
||||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -33,23 +33,14 @@ namespace MQTTnet.TestApp
|
|||||||
|
|
||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
var payloadText = string.Empty;
|
|
||||||
if (e.ApplicationMessage.PayloadSegment.Count > 0)
|
|
||||||
{
|
|
||||||
payloadText = Encoding.UTF8.GetString(
|
|
||||||
e.ApplicationMessage.PayloadSegment.Array,
|
|
||||||
e.ApplicationMessage.PayloadSegment.Offset,
|
|
||||||
e.ApplicationMessage.PayloadSegment.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
|
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
|
||||||
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
|
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
|
||||||
Console.WriteLine($"+ Payload = {payloadText}");
|
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
|
||||||
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
|
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
|
||||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
client.ConnectedAsync += async e =>
|
client.ConnectedAsync += async e =>
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<DebugType>Full</DebugType>
|
<DebugType>Full</DebugType>
|
||||||
<TargetFrameworks>net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||||
<NoWarn>1591;NETSDK1138</NoWarn>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Newtonsoft.Json;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -42,7 +42,7 @@ namespace MQTTnet.TestApp
|
|||||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
|
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.StartAsync(options);
|
await managedClient.StartAsync(options);
|
||||||
@@ -85,7 +85,7 @@ namespace MQTTnet.TestApp
|
|||||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
|
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MQTTnet.Internal;
|
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
{
|
{
|
||||||
@@ -125,7 +124,7 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
return CompletedTask.Instance;
|
return Implementations.PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Cleanup()
|
public async Task Cleanup()
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ namespace MQTTnet.TestApp
|
|||||||
return new MqttApplicationMessage
|
return new MqttApplicationMessage
|
||||||
{
|
{
|
||||||
Topic = "A/B/C",
|
Topic = "A/B/C",
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(Payload)),
|
Payload = Encoding.UTF8.GetBytes(Payload),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -229,7 +229,7 @@ namespace MQTTnet.TestApp
|
|||||||
var message = new MqttApplicationMessage
|
var message = new MqttApplicationMessage
|
||||||
{
|
{
|
||||||
Topic = "A/B/C",
|
Topic = "A/B/C",
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ namespace MQTTnet.TestApp
|
|||||||
var message = new MqttApplicationMessage
|
var message = new MqttApplicationMessage
|
||||||
{
|
{
|
||||||
Topic = "A/B/C",
|
Topic = "A/B/C",
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ namespace MQTTnet.TestApp
|
|||||||
var message = new MqttApplicationMessage
|
var message = new MqttApplicationMessage
|
||||||
{
|
{
|
||||||
Topic = "A/B/C",
|
Topic = "A/B/C",
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ namespace MQTTnet.TestApp
|
|||||||
Console.WriteLine("c = Start QoS 0 benchmark");
|
Console.WriteLine("c = Start QoS 0 benchmark");
|
||||||
Console.WriteLine("d = Start server with logging");
|
Console.WriteLine("d = Start server with logging");
|
||||||
Console.WriteLine("e = Start Message Throughput Test");
|
Console.WriteLine("e = Start Message Throughput Test");
|
||||||
Console.WriteLine("f = Start AsyncLock Test");
|
|
||||||
|
|
||||||
var pressedKey = Console.ReadKey(true);
|
var pressedKey = Console.ReadKey(true);
|
||||||
if (pressedKey.KeyChar == '1')
|
if (pressedKey.KeyChar == '1')
|
||||||
@@ -89,10 +88,6 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
Task.Run(new MessageThroughputTest().Run);
|
Task.Run(new MessageThroughputTest().Run);
|
||||||
}
|
}
|
||||||
else if (pressedKey.KeyChar == 'f')
|
|
||||||
{
|
|
||||||
Task.Run(new AsyncLockTest().Run);
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(Timeout.Infinite);
|
Thread.Sleep(Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 MQTTnet.Client;
|
||||||
using System;
|
using System;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Extensions.WebSocket4Net;
|
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -18,6 +17,10 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
public static async Task RunAsync()
|
public static async Task RunAsync()
|
||||||
{
|
{
|
||||||
|
// MqttNetConsoleLogger.ForwardToConsole();
|
||||||
|
|
||||||
|
// For most of these connections to work, set output target to Net5.0.
|
||||||
|
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
// TLS13 is only available in Net5.0
|
// TLS13 is only available in Net5.0
|
||||||
var unsafeTls13 = new MqttClientOptionsBuilderTlsParameters
|
var unsafeTls13 = new MqttClientOptionsBuilderTlsParameters
|
||||||
@@ -28,6 +31,7 @@ namespace MQTTnet.TestApp
|
|||||||
CertificateValidationHandler = w => true
|
CertificateValidationHandler = w => true
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Also defining TLS12 for servers that don't seem no to support TLS13.
|
// Also defining TLS12 for servers that don't seem no to support TLS13.
|
||||||
var unsafeTls12 = new MqttClientOptionsBuilderTlsParameters
|
var unsafeTls12 = new MqttClientOptionsBuilderTlsParameters
|
||||||
{
|
{
|
||||||
@@ -38,37 +42,33 @@ namespace MQTTnet.TestApp
|
|||||||
};
|
};
|
||||||
|
|
||||||
// mqtt.eclipseprojects.io
|
// mqtt.eclipseprojects.io
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("mqtt.eclipseprojects.io TCP",
|
||||||
"mqtt.eclipseprojects.io TCP",
|
new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("mqtt.eclipseprojects.io WS",
|
||||||
"mqtt.eclipseprojects.io WS",
|
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:80/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:80/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13",
|
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13",
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
|
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13 (WebSocket4Net)",
|
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
|
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build(),
|
|
||||||
true);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// test.mosquitto.org
|
// test.mosquitto.org
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org TCP",
|
||||||
"test.mosquitto.org TCP",
|
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org TCP - Authenticated",
|
||||||
"test.mosquitto.org TCP - Authenticated",
|
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884).WithCredentials("rw", "readwrite").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithCredentials("rw", "readwrite")
|
||||||
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org TCP TLS12",
|
||||||
"test.mosquitto.org TCP TLS12",
|
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||||
|
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
await ExecuteTestAsync("test.mosquitto.org TCP TLS13",
|
await ExecuteTestAsync("test.mosquitto.org TCP TLS13",
|
||||||
@@ -76,40 +76,27 @@ namespace MQTTnet.TestApp
|
|||||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org TCP TLS12 - Authenticated",
|
||||||
"test.mosquitto.org TCP TLS12 - Authenticated",
|
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
|
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
|
||||||
.WithCredentials("rw", "readwrite")
|
.WithCredentials("rw", "readwrite")
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||||
.WithTls(unsafeTls12)
|
|
||||||
.Build());
|
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org WS",
|
||||||
"test.mosquitto.org WS",
|
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("test.mosquitto.org WS TLS12",
|
||||||
"test.mosquitto.org WS (WebSocket4Net)",
|
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||||
true);
|
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
|
||||||
"test.mosquitto.org WS TLS12",
|
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
|
||||||
|
|
||||||
// await ExecuteTestAsync(
|
|
||||||
// "test.mosquitto.org WS TLS12 (WebSocket4Net)",
|
|
||||||
// new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
|
|
||||||
// true);
|
|
||||||
|
|
||||||
// broker.emqx.io
|
// broker.emqx.io
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.emqx.io TCP",
|
||||||
"broker.emqx.io TCP",
|
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.emqx.io TCP TLS12",
|
||||||
"broker.emqx.io TCP TLS12",
|
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||||
|
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
await ExecuteTestAsync("broker.emqx.io TCP TLS13",
|
await ExecuteTestAsync("broker.emqx.io TCP TLS13",
|
||||||
@@ -117,37 +104,23 @@ namespace MQTTnet.TestApp
|
|||||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.emqx.io WS",
|
||||||
"broker.emqx.io WS",
|
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8083/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8083/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.emqx.io WS TLS12",
|
||||||
"broker.emqx.io WS (WebSocket4Net)",
|
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||||
true);
|
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
|
||||||
"broker.emqx.io WS TLS12",
|
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
|
||||||
"broker.emqx.io WS TLS12 (WebSocket4Net)",
|
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
|
|
||||||
true);
|
|
||||||
|
|
||||||
// broker.hivemq.com
|
// broker.hivemq.com
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.hivemq.com TCP",
|
||||||
"broker.hivemq.com TCP",
|
new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883)
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
await ExecuteTestAsync("broker.hivemq.com WS",
|
||||||
"broker.hivemq.com WS",
|
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt")
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
await ExecuteTestAsync(
|
|
||||||
"broker.hivemq.com WS (WebSocket4Net)",
|
|
||||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
|
||||||
true);
|
|
||||||
|
|
||||||
// mqtt.swifitch.cz: Does not seem to operate any more
|
// mqtt.swifitch.cz: Does not seem to operate any more
|
||||||
|
|
||||||
@@ -157,43 +130,36 @@ namespace MQTTnet.TestApp
|
|||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async Task ExecuteTestAsync(string name, MqttClientOptions options, bool useWebSocket4Net = false)
|
static async Task ExecuteTestAsync(string name, MqttClientOptions options)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Write("Testing '" + name + "'... ", ConsoleColor.Gray);
|
Write("Testing '" + name + "'... ", ConsoleColor.Gray);
|
||||||
|
|
||||||
var factory = new MqttFactory();
|
var factory = new MqttFactory();
|
||||||
if (useWebSocket4Net)
|
//factory.UseWebSocket4Net();
|
||||||
|
var client = factory.CreateMqttClient();
|
||||||
|
var topic = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
MqttApplicationMessage receivedMessage = null;
|
||||||
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
factory.UseWebSocket4Net();
|
receivedMessage = e.ApplicationMessage;
|
||||||
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ConnectAsync(options);
|
||||||
|
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
|
||||||
|
|
||||||
|
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
|
||||||
|
{
|
||||||
|
throw new Exception("Message invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var client = factory.CreateMqttClient())
|
await client.UnsubscribeAsync(topic);
|
||||||
{
|
await client.DisconnectAsync();
|
||||||
MqttApplicationMessage receivedMessage = null;
|
|
||||||
client.ApplicationMessageReceivedAsync += e =>
|
|
||||||
{
|
|
||||||
receivedMessage = e.ApplicationMessage;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var topic = Guid.NewGuid().ToString();
|
|
||||||
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
|
||||||
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
|
|
||||||
|
|
||||||
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
|
|
||||||
{
|
|
||||||
throw new Exception("Message invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.UnsubscribeAsync(topic).ConfigureAwait(false);
|
|
||||||
await client.DisconnectAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Write("[OK]\n", ConsoleColor.Green);
|
Write("[OK]\n", ConsoleColor.Green);
|
||||||
}
|
}
|
||||||
@@ -208,5 +174,6 @@ namespace MQTTnet.TestApp
|
|||||||
Console.ForegroundColor = color;
|
Console.ForegroundColor = color;
|
||||||
Console.Write(message);
|
Console.Write(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -68,13 +69,13 @@ namespace MQTTnet.TestApp
|
|||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.RetainedMessagesClearedAsync += e =>
|
mqttServer.RetainedMessagesClearedAsync += e =>
|
||||||
{
|
{
|
||||||
File.Delete(Filename);
|
File.Delete(Filename);
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||||
@@ -92,7 +93,7 @@ namespace MQTTnet.TestApp
|
|||||||
|
|
||||||
e.LoadedRetainedMessages = retainedMessages;
|
e.LoadedRetainedMessages = retainedMessages;
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
@@ -101,7 +102,7 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
// Replace the payload with the timestamp. But also extending a JSON
|
// Replace the payload with the timestamp. But also extending a JSON
|
||||||
// based payload with the timestamp is a suitable use case.
|
// based payload with the timestamp is a suitable use case.
|
||||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")));
|
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(DateTime.Now.ToString("O"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.ApplicationMessage.Topic == "not_allowed_topic")
|
if (e.ApplicationMessage.Topic == "not_allowed_topic")
|
||||||
@@ -110,7 +111,7 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.ValidatingConnectionAsync += e =>
|
mqttServer.ValidatingConnectionAsync += e =>
|
||||||
@@ -123,7 +124,7 @@ namespace MQTTnet.TestApp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||||
@@ -139,22 +140,16 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
var payloadText = string.Empty;
|
MqttNetConsoleLogger.PrintToConsole(
|
||||||
if (e.ApplicationMessage.PayloadSegment.Count > 0)
|
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
||||||
{
|
ConsoleColor.Magenta);
|
||||||
payloadText = Encoding.UTF8.GetString(
|
|
||||||
e.ApplicationMessage.PayloadSegment.Array,
|
|
||||||
e.ApplicationMessage.PayloadSegment.Offset,
|
|
||||||
e.ApplicationMessage.PayloadSegment.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
MqttNetConsoleLogger.PrintToConsole($"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{payloadText}'", ConsoleColor.Magenta);
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//options.ApplicationMessageInterceptor = c =>
|
//options.ApplicationMessageInterceptor = c =>
|
||||||
@@ -182,7 +177,7 @@ namespace MQTTnet.TestApp
|
|||||||
mqttServer.ClientConnectedAsync += e =>
|
mqttServer.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Console.Write("Client disconnected event fired.");
|
Console.Write("Client disconnected event fired.");
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttServer.StartAsync();
|
await mqttServer.StartAsync();
|
||||||
|
|||||||
+1
-5
@@ -14,7 +14,7 @@ using MQTTnet.LowLevelClient;
|
|||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients.LowLevelMqttClient
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class LowLevelMqttClient_Tests : BaseTestClass
|
public sealed class LowLevelMqttClient_Tests : BaseTestClass
|
||||||
@@ -80,8 +80,6 @@ namespace MQTTnet.Tests.Clients.LowLevelMqttClient
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
testEnvironment.IgnoreServerLogErrors = true;
|
|
||||||
|
|
||||||
testEnvironment.ServerPort = 8364;
|
testEnvironment.ServerPort = 8364;
|
||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
@@ -118,8 +116,6 @@ namespace MQTTnet.Tests.Clients.LowLevelMqttClient
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
testEnvironment.IgnoreServerLogErrors = true;
|
|
||||||
|
|
||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
||||||
+35
-119
@@ -11,110 +11,18 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
using MqttClient = MQTTnet.Client.MqttClient;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
[TestMethod]
|
|
||||||
public async Task Expose_Custom_Connection_Error()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
server.ValidatingConnectionAsync += args =>
|
|
||||||
{
|
|
||||||
args.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
|
||||||
|
|
||||||
MqttClientDisconnectedEventArgs disconnectedArgs = null;
|
|
||||||
managedClient.DisconnectedAsync += args =>
|
|
||||||
{
|
|
||||||
disconnectedArgs = args;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var clientOptions = testEnvironment.Factory.CreateManagedMqttClientOptionsBuilder().WithClientOptions(testEnvironment.CreateDefaultClientOptions()).Build();
|
|
||||||
await managedClient.StartAsync(clientOptions);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
Assert.IsNotNull(disconnectedArgs);
|
|
||||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, disconnectedArgs.ConnectResult.ResultCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
|
||||||
|
|
||||||
var senderClient = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
// Prepare managed client.
|
|
||||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
|
||||||
await managedClient.SubscribeAsync("#");
|
|
||||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(managedClient);
|
|
||||||
|
|
||||||
var managedClientOptions = new ManagedMqttClientOptions
|
|
||||||
{
|
|
||||||
ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder()
|
|
||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
|
||||||
.WithClientId(nameof(Receive_While_Not_Cleanly_Disconnected) + "_managed")
|
|
||||||
.WithCleanSession(false)
|
|
||||||
.Build()
|
|
||||||
};
|
|
||||||
|
|
||||||
await managedClient.StartAsync(managedClientOptions);
|
|
||||||
await LongTestDelay();
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
// Send test data.
|
|
||||||
await senderClient.PublishStringAsync("topic1");
|
|
||||||
await LongTestDelay();
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
receivedMessages.AssertReceivedCountEquals(1);
|
|
||||||
|
|
||||||
// Stop the managed client but keep session at server (not clean disconnect required).
|
|
||||||
await managedClient.StopAsync(false);
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
// Send new messages in the meantime.
|
|
||||||
await senderClient.PublishStringAsync("topic2", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
// Start the managed client, it should receive the new message.
|
|
||||||
await managedClient.StartAsync(managedClientOptions);
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
receivedMessages.AssertReceivedCountEquals(2);
|
|
||||||
|
|
||||||
// Stop and start again, no new message should be received.
|
|
||||||
for (var i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
await managedClient.StopAsync(false);
|
|
||||||
await LongTestDelay();
|
|
||||||
await managedClient.StartAsync(managedClientOptions);
|
|
||||||
await LongTestDelay();
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedMessages.AssertReceivedCountEquals(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Connect_To_Invalid_Server()
|
public async Task Connect_To_Invalid_Server()
|
||||||
{
|
{
|
||||||
@@ -127,10 +35,11 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
||||||
|
|
||||||
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
||||||
|
|
||||||
managedClient.ConnectingFailedAsync += args =>
|
managedClient.ConnectingFailedAsync += args =>
|
||||||
{
|
{
|
||||||
connectingFailedEventArgs = args;
|
connectingFailedEventArgs = args;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.StartAsync(
|
await managedClient.StartAsync(
|
||||||
@@ -196,7 +105,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var dyingClient = testEnvironment.CreateClient();
|
var dyingClient = testEnvironment.CreateClient();
|
||||||
var dyingManagedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(dyingClient, testEnvironment.ClientLogger);
|
var dyingManagedClient = new ManagedMqttClient(dyingClient, testEnvironment.ClientLogger);
|
||||||
|
|
||||||
await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
|
await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
|
||||||
|
|
||||||
@@ -208,7 +117,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
receivingClient.ApplicationMessageReceivedAsync += e =>
|
receivingClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receivingClient.SubscribeAsync("My/last/will");
|
await receivingClient.SubscribeAsync("My/last/will");
|
||||||
@@ -231,7 +140,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
||||||
|
|
||||||
var connected = GetConnectedTask(managedClient);
|
var connected = GetConnectedTask(managedClient);
|
||||||
@@ -258,7 +167,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
|
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
||||||
var storage = new ManagedMqttClientTestStorage();
|
var storage = new ManagedMqttClientTestStorage();
|
||||||
|
|
||||||
@@ -363,10 +272,16 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
await testEnvironment.StartServer().ConfigureAwait(false);
|
await testEnvironment.StartServer().ConfigureAwait(false);
|
||||||
|
|
||||||
var sendingClient = await testEnvironment.ConnectClient().ConfigureAwait(false);
|
var sendingClient = await testEnvironment.ConnectClient().ConfigureAwait(false);
|
||||||
await sendingClient.PublishStringAsync("topic", "A", retain: true);
|
await sendingClient.PublishAsync(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "topic",
|
||||||
|
Payload = new byte[] { 1 },
|
||||||
|
Retain = true
|
||||||
|
});
|
||||||
|
|
||||||
// Wait a bit for the retained message to be available
|
// Wait a bit for the retained message to be available
|
||||||
await LongTestDelay();
|
await Task.Delay(1000);
|
||||||
|
|
||||||
await sendingClient.DisconnectAsync();
|
await sendingClient.DisconnectAsync();
|
||||||
|
|
||||||
@@ -375,26 +290,28 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
||||||
|
|
||||||
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
||||||
|
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(testEnvironment.CreateClient());
|
|
||||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedManagedMessages.Add(e.ApplicationMessage);
|
receivedManagedMessages.Add(e.ApplicationMessage);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.SubscribeAsync("topic");
|
await managedClient.SubscribeAsync("topic");
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(0.5)).Build());
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
await LongTestDelay();
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||||
|
|
||||||
await managedClient.StopAsync();
|
await managedClient.StopAsync();
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(0.5)).Build());
|
await Task.Delay(500);
|
||||||
await LongTestDelay();
|
|
||||||
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
// After reconnect and then some delay, the retained message must not be received,
|
// After reconnect and then some delay, the retained message must not be received,
|
||||||
// showing that the subscriptions were cleared
|
// showing that the subscriptions were cleared
|
||||||
@@ -402,8 +319,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
|
|
||||||
// Make sure that it gets received after subscribing again.
|
// Make sure that it gets received after subscribing again.
|
||||||
await managedClient.SubscribeAsync("topic");
|
await managedClient.SubscribeAsync("topic");
|
||||||
await LongTestDelay();
|
await Task.Delay(500);
|
||||||
|
|
||||||
Assert.AreEqual(2, receivedManagedMessages.Count);
|
Assert.AreEqual(2, receivedManagedMessages.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,7 +387,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MQTTnet.Extensions.ManagedClient.ManagedMqttClient> CreateManagedClientAsync(
|
async Task<ManagedMqttClient> CreateManagedClientAsync(
|
||||||
TestEnvironment testEnvironment,
|
TestEnvironment testEnvironment,
|
||||||
IMqttClient underlyingClient = null,
|
IMqttClient underlyingClient = null,
|
||||||
TimeSpan? connectionCheckInterval = null,
|
TimeSpan? connectionCheckInterval = null,
|
||||||
@@ -488,7 +404,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
// at connection check intervals
|
// at connection check intervals
|
||||||
managedOptions.ConnectionCheckInterval = connectionCheckInterval ?? TimeSpan.FromSeconds(0.1);
|
managedOptions.ConnectionCheckInterval = connectionCheckInterval ?? TimeSpan.FromSeconds(0.1);
|
||||||
|
|
||||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(underlyingClient ?? testEnvironment.CreateClient(), new MqttNetEventLogger());
|
var managedClient = new ManagedMqttClient(underlyingClient ?? testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||||
|
|
||||||
var connected = GetConnectedTask(managedClient);
|
var connected = GetConnectedTask(managedClient);
|
||||||
|
|
||||||
@@ -505,14 +421,14 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
/// name="managedClient" />
|
/// name="managedClient" />
|
||||||
/// has connected
|
/// has connected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task GetConnectedTask(MQTTnet.Extensions.ManagedClient.ManagedMqttClient managedClient)
|
Task GetConnectedTask(ManagedMqttClient managedClient)
|
||||||
{
|
{
|
||||||
var connected = new TaskCompletionSource<bool>();
|
var connected = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
managedClient.ConnectedAsync += e =>
|
managedClient.ConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
connected.TrySetResult(true);
|
connected.TrySetResult(true);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
return connected.Task;
|
return connected.Task;
|
||||||
@@ -527,7 +443,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
/// name="expectedNumberOfMessages" />
|
/// name="expectedNumberOfMessages" />
|
||||||
/// have been received
|
/// have been received
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<List<MqttApplicationMessage>> SetupReceivingOfMessages(MQTTnet.Extensions.ManagedClient.ManagedMqttClient managedClient, int expectedNumberOfMessages)
|
Task<List<MqttApplicationMessage>> SetupReceivingOfMessages(ManagedMqttClient managedClient, int expectedNumberOfMessages)
|
||||||
{
|
{
|
||||||
var receivedMessages = new List<MqttApplicationMessage>();
|
var receivedMessages = new List<MqttApplicationMessage>();
|
||||||
var result = new AsyncTaskCompletionSource<List<MqttApplicationMessage>>();
|
var result = new AsyncTaskCompletionSource<List<MqttApplicationMessage>>();
|
||||||
@@ -541,7 +457,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
result.TrySetResult(receivedMessages);
|
result.TrySetResult(receivedMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
return result.Task;
|
return result.Task;
|
||||||
@@ -570,7 +486,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
_messages = messages;
|
_messages = messages;
|
||||||
return CompletedTask.Instance;
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -6,8 +6,9 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Extensions;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttClientOptionsBuilder_Tests
|
public class MqttClientOptionsBuilder_Tests
|
||||||
+79
-58
@@ -14,17 +14,15 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
namespace MQTTnet.Tests.Client
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients.MqttClient
|
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttClient_Tests : BaseTestClass
|
public sealed class Client_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||||
@@ -58,7 +56,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = InvokeInternal();
|
_ = InvokeInternal();
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
||||||
@@ -71,34 +69,60 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Connect_Disconnect_Connect()
|
[ExpectedException(typeof(MqttCommunicationException))]
|
||||||
|
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
var client = new MqttFactory().CreateMqttClient();
|
||||||
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(5)).Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(OperationCanceledException))]
|
||||||
|
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
||||||
|
{
|
||||||
|
var client = new MqttFactory().CreateMqttClient();
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
await client.DisconnectAsync();
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(InvalidOperationException))]
|
[ExpectedException(typeof(MqttCommunicationException))]
|
||||||
public async Task Connect_Multiple_Times_Should_Fail()
|
public async Task Connect_To_Invalid_Server_Wrong_Protocol()
|
||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
var client = new MqttFactory().CreateMqttClient();
|
||||||
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ConnectTimeout_Throws_Exception()
|
||||||
|
{
|
||||||
|
var factory = new MqttFactory();
|
||||||
|
using (var client = factory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
var disconnectHandlerCalled = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.DisconnectedAsync += args =>
|
||||||
|
{
|
||||||
|
disconnectHandlerCalled = true;
|
||||||
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
|
|
||||||
await client.ConnectAsync(clientOptions);
|
Assert.Fail("Must fail!");
|
||||||
await client.ConnectAsync(clientOptions);
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(exception);
|
||||||
|
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
|
||||||
|
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(100); // disconnected handler is called async
|
||||||
|
Assert.IsTrue(disconnectHandlerCalled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +136,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
ex = e.Exception;
|
ex = e.Exception;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -121,7 +145,6 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Ignore errors.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -144,7 +167,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
server.InterceptingPublishAsync += c =>
|
server.InterceptingPublishAsync += c =>
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.SendAsync(
|
await client.SendAsync(
|
||||||
@@ -185,7 +208,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
handlerFired = true;
|
handlerFired = true;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -216,10 +239,10 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
|
|
||||||
for (var i = 0; i < 98; i++)
|
for (var i = 0; i < 98; i++)
|
||||||
{
|
{
|
||||||
Assert.IsFalse(clients[i].TryPingAsync().GetAwaiter().GetResult(), $"clients[{i}] is not connected");
|
Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(clients[99].TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(clients[99].IsConnected);
|
||||||
|
|
||||||
Assert.AreEqual(1, clientStatus.Count);
|
Assert.AreEqual(1, clientStatus.Count);
|
||||||
Assert.AreEqual(1, sessionStatus.Count);
|
Assert.AreEqual(1, sessionStatus.Count);
|
||||||
@@ -229,7 +252,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
receiveClient.ApplicationMessageReceivedAsync += e =>
|
receiveClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiveClient.SubscribeAsync("x");
|
await receiveClient.SubscribeAsync("x");
|
||||||
@@ -287,7 +310,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||||
@@ -309,7 +332,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,11 +343,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client.IsConnected);
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
|
|
||||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsFalse(client.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +453,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
receivedValues.Add(value);
|
receivedValues.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||||
@@ -508,7 +531,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
client2.ApplicationMessageReceivedAsync += e =>
|
client2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client2.SubscribeAsync(client2Topic);
|
await client2.SubscribeAsync(client2Topic);
|
||||||
@@ -543,7 +566,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync("a");
|
await client1.SubscribeAsync("a");
|
||||||
@@ -568,17 +591,17 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(await client.TryPingAsync());
|
Assert.IsTrue(client.IsConnected);
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(await client.TryPingAsync());
|
Assert.IsFalse(client.IsConnected);
|
||||||
|
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||||
Assert.IsTrue(await client.TryPingAsync());
|
Assert.IsTrue(client.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,7 +637,6 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Ignore errors.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
||||||
@@ -634,11 +656,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client.IsConnected);
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsFalse(client.IsConnected);
|
||||||
|
|
||||||
for (var i = 0; i < 5; i++)
|
for (var i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
@@ -649,7 +671,6 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Ignore errors.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,7 +678,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||||
await client.PingAsync();
|
Assert.IsTrue(client.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +723,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
replies.Add(eventArgs.ApplicationMessage.Topic);
|
replies.Add(eventArgs.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += Handler2;
|
client2.ApplicationMessageReceivedAsync += Handler2;
|
||||||
@@ -740,7 +761,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += async e =>
|
client2.ApplicationMessageReceivedAsync += async e =>
|
||||||
@@ -791,7 +812,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
@@ -807,14 +828,14 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client.IsConnected);
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
@@ -830,11 +851,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client.IsConnected);
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -871,7 +892,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
||||||
@@ -896,14 +917,14 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
client1.DisconnectedAsync += c =>
|
client1.DisconnectedAsync += c =>
|
||||||
{
|
{
|
||||||
disconnectedFired = true;
|
disconnectedFired = true;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageReceived = false;
|
var messageReceived = false;
|
||||||
client1.ApplicationMessageReceivedAsync += e =>
|
client1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
messageReceived = true;
|
messageReceived = true;
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
||||||
@@ -920,7 +941,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
Assert.IsTrue(messageReceived);
|
Assert.IsTrue(messageReceived);
|
||||||
Assert.IsTrue(client1.TryPingAsync().GetAwaiter().GetResult());
|
Assert.IsTrue(client1.IsConnected);
|
||||||
Assert.IsFalse(disconnectedFired);
|
Assert.IsFalse(disconnectedFired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients.MqttClient
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public sealed class Extended_Authentication_Tests : BaseTestClass
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
public async Task ReAuthenticate()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
server.ValidatingConnectionAsync += async args =>
|
|
||||||
{
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
|
||||||
{
|
|
||||||
// Just do a simple custom authentication.
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, cancellationToken: timeout.Token);
|
|
||||||
var clientResponse = await args.ReceiveAuthenticationDataAsync(timeout.Token);
|
|
||||||
CollectionAssert.AreEqual(clientResponse.AuthenticationData, Encoding.ASCII.GetBytes("TOKEN"));
|
|
||||||
|
|
||||||
var userProperties = new MqttUserPropertiesBuilder().With("x", "y").Build();
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, cancellationToken: timeout.Token, userProperties: userProperties);
|
|
||||||
|
|
||||||
args.ReasonCode = MqttConnectReasonCode.Success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
server.ClientReAuthenticatingAsync += args =>
|
|
||||||
{
|
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
client.ExtendedAuthenticationExchangeAsync += async args =>
|
|
||||||
{
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, Encoding.ASCII.GetBytes("TOKEN"));
|
|
||||||
|
|
||||||
var serverResponse = await args.ReceiveAuthenticationDataAsync(CancellationToken.None);
|
|
||||||
Assert.IsNotNull(serverResponse.UserProperties);
|
|
||||||
Assert.AreEqual("x", serverResponse.UserProperties[0].Name);
|
|
||||||
Assert.AreEqual("y", serverResponse.UserProperties[0].Value);
|
|
||||||
};
|
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithTimeout(TimeSpan.FromSeconds(10)).WithAuthentication("CUSTOM").Build();
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
await client.ReAuthenticateAsync();
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
var pingResult = await client.TryPingAsync();
|
|
||||||
|
|
||||||
Assert.IsTrue(pingResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Use_Extended_Authentication()
|
|
||||||
{
|
|
||||||
var initialContextToken = Encoding.ASCII.GetBytes("initial context token");
|
|
||||||
var replyContextToken = Encoding.ASCII.GetBytes("reply context token");
|
|
||||||
var outcomeOfAuthentication = Encoding.ASCII.GetBytes("outcome of authentication");
|
|
||||||
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Sample flow from RFC:
|
|
||||||
|
|
||||||
1. Client to Server CONNECT Authentication Method="GS2-KRB5"
|
|
||||||
2. Server to Client AUTH rc=0x18 Authentication Method="GS2-KRB5"
|
|
||||||
3. Client to Server AUTH rc=0x18 Authentication Method="GS2-KRB5" Authentication Data=initial context token
|
|
||||||
4. Server to Client AUTH rc=0x18 Authentication Method="GS2-KRB5" Authentication Data=reply context token
|
|
||||||
5. Client to Server AUTH rc=0x18 Authentication Method="GS2-KRB5"
|
|
||||||
6. Server to Client CONNACK rc=0 Authentication Method="GS2-KRB5" Authentication Data=outcome of authentication
|
|
||||||
*/
|
|
||||||
|
|
||||||
server.ValidatingConnectionAsync += async args =>
|
|
||||||
{
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
|
||||||
{
|
|
||||||
// 1.
|
|
||||||
if (args.AuthenticationMethod != "GS2-KRB5")
|
|
||||||
{
|
|
||||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, cancellationToken: timeout.Token);
|
|
||||||
|
|
||||||
// 3.
|
|
||||||
var clientResponse = await args.ReceiveAuthenticationDataAsync(timeout.Token);
|
|
||||||
CollectionAssert.AreEqual(clientResponse.AuthenticationData, initialContextToken);
|
|
||||||
Assert.AreEqual(MqttAuthenticateReasonCode.ContinueAuthentication, clientResponse.ReasonCode);
|
|
||||||
|
|
||||||
// 4.
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, replyContextToken, cancellationToken: timeout.Token);
|
|
||||||
|
|
||||||
// 5.
|
|
||||||
clientResponse = await args.ReceiveAuthenticationDataAsync(timeout.Token);
|
|
||||||
Assert.AreEqual(clientResponse.AuthenticationData, null);
|
|
||||||
Assert.AreEqual(MqttAuthenticateReasonCode.ContinueAuthentication, clientResponse.ReasonCode);
|
|
||||||
|
|
||||||
// 6.
|
|
||||||
args.ResponseAuthenticationData = outcomeOfAuthentication;
|
|
||||||
args.ReasonCode = MqttConnectReasonCode.Success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
client.ExtendedAuthenticationExchangeAsync += async args =>
|
|
||||||
{
|
|
||||||
if (args.AuthenticationMethod != "GS2-KRB5")
|
|
||||||
{
|
|
||||||
Assert.Fail("Authentication method is wrong.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. THE FACT THAT THE SERVER SENDS THE AUTH PACKET WILL TRIGGER THIS EVENT SO THE INITIAL DATA IS IN THE EVENT ARGS!
|
|
||||||
Assert.AreEqual(MqttAuthenticateReasonCode.ContinueAuthentication, args.ReasonCode);
|
|
||||||
|
|
||||||
// 3.
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication, initialContextToken);
|
|
||||||
|
|
||||||
// 4.
|
|
||||||
var serverResponse = await args.ReceiveAuthenticationDataAsync(CancellationToken.None);
|
|
||||||
CollectionAssert.AreEqual(serverResponse.AuthenticationData, replyContextToken);
|
|
||||||
Assert.AreEqual(MqttAuthenticateReasonCode.ContinueAuthentication, serverResponse.ReasonCode);
|
|
||||||
|
|
||||||
// 5.
|
|
||||||
await args.SendAuthenticationDataAsync(MqttAuthenticateReasonCode.ContinueAuthentication);
|
|
||||||
};
|
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithTimeout(TimeSpan.FromSeconds(10)).WithAuthentication("GS2-KRB5").Build();
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Clients.MqttClient
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(MqttCommunicationException))]
|
|
||||||
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
|
||||||
{
|
|
||||||
var client = new MqttFactory().CreateMqttClient();
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
|
||||||
{
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).Build(), timeout.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(OperationCanceledException))]
|
|
||||||
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
|
||||||
{
|
|
||||||
var client = new MqttFactory().CreateMqttClient();
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
|
||||||
{
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(MqttCommunicationException))]
|
|
||||||
public async Task Connect_To_Invalid_Server_Wrong_Protocol()
|
|
||||||
{
|
|
||||||
var client = new MqttFactory().CreateMqttClient();
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task ConnectTimeout_Throws_Exception()
|
|
||||||
{
|
|
||||||
var factory = new MqttFactory();
|
|
||||||
using (var client = factory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var disconnectHandlerCalled = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
client.DisconnectedAsync += args =>
|
|
||||||
{
|
|
||||||
disconnectHandlerCalled = true;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
|
||||||
|
|
||||||
Assert.Fail("Must fail!");
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Assert.IsNotNull(exception);
|
|
||||||
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
|
|
||||||
}
|
|
||||||
|
|
||||||
await LongTestDelay(); // disconnected handler is called async
|
|
||||||
Assert.IsTrue(disconnectHandlerCalled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Disconnect_Clean()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
ClientDisconnectedEventArgs eventArgs = null;
|
|
||||||
server.ClientDisconnectedAsync += args =>
|
|
||||||
{
|
|
||||||
eventArgs = args;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
var disconnectOptions = testEnvironment.Factory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
|
||||||
|
|
||||||
// Perform a clean disconnect.
|
|
||||||
await client.DisconnectAsync(disconnectOptions);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
Assert.IsNotNull(eventArgs);
|
|
||||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Disconnect_Clean_With_Custom_Reason()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
ClientDisconnectedEventArgs eventArgs = null;
|
|
||||||
server.ClientDisconnectedAsync += args =>
|
|
||||||
{
|
|
||||||
eventArgs = args;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
var disconnectOptions = testEnvironment.Factory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
|
||||||
|
|
||||||
// Perform a clean disconnect.
|
|
||||||
await client.DisconnectAsync(disconnectOptions);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
Assert.IsNotNull(eventArgs);
|
|
||||||
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Disconnect_Clean_With_User_Properties()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
var server = await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
ClientDisconnectedEventArgs eventArgs = null;
|
|
||||||
server.ClientDisconnectedAsync += args =>
|
|
||||||
{
|
|
||||||
eventArgs = args;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
var disconnectOptions = testEnvironment.Factory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build();
|
|
||||||
|
|
||||||
// Perform a clean disconnect.
|
|
||||||
await client.DisconnectAsync(disconnectOptions);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
Assert.IsNotNull(eventArgs);
|
|
||||||
Assert.IsNotNull(eventArgs.UserProperties);
|
|
||||||
Assert.AreEqual(1, eventArgs.UserProperties.Count);
|
|
||||||
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
|
|
||||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Diagnostics
|
namespace MQTTnet.Tests.Diagnostics
|
||||||
{
|
{
|
||||||
@@ -30,10 +30,10 @@ namespace MQTTnet.Tests.Diagnostics
|
|||||||
|
|
||||||
var packets = new List<string>();
|
var packets = new List<string>();
|
||||||
|
|
||||||
mqttClient.InspectPacketAsync += eventArgs =>
|
mqttClient.InspectPackage += eventArgs =>
|
||||||
{
|
{
|
||||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Helpers
|
namespace MQTTnet.Tests.Extensions
|
||||||
{
|
{
|
||||||
public static class MqttPacketWriterExtensions
|
public static class MqttPacketWriterExtensions
|
||||||
{
|
{
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Extensions.WebSocket4Net;
|
|
||||||
using MQTTnet.Tests.Helpers;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Extensions
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public sealed class WebSocket4Net_Tests
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(MqttCommunicationException))]
|
|
||||||
public async Task Connect_Failed_With_Invalid_Server()
|
|
||||||
{
|
|
||||||
var factory = new MqttFactory().UseWebSocket4Net();
|
|
||||||
|
|
||||||
using (var client = factory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var options = new MqttClientOptionsBuilder().WithWebSocketServer("ws://a.b/mqtt").WithTimeout(TimeSpan.FromSeconds(2)).Build();
|
|
||||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Use_Correct_Adapter()
|
|
||||||
{
|
|
||||||
// Should not throw exception
|
|
||||||
var factory = new MqttFactory().UseWebSocket4Net();
|
|
||||||
using (var client = factory.CreateMqttClient())
|
|
||||||
{
|
|
||||||
var adapterFactory = client.GetFieldValue("_adapterFactory");
|
|
||||||
|
|
||||||
Assert.IsInstanceOfType(adapterFactory, typeof(WebSocket4NetMqttClientAdapterFactory));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Use_WebSocket4Net()
|
|
||||||
{
|
|
||||||
// Should not throw exception
|
|
||||||
new MqttFactory().UseWebSocket4Net();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Factory
|
namespace MQTTnet.Tests.Factory
|
||||||
@@ -56,7 +56,7 @@ namespace MQTTnet.Tests.Factory
|
|||||||
managedClient.ConnectingFailedAsync += e =>
|
managedClient.ConnectingFailedAsync += e =>
|
||||||
{
|
{
|
||||||
tcs.TrySetResult(null);
|
tcs.TrySetResult(null);
|
||||||
return CompletedTask.Instance;
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Formatter
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public sealed class MqttBufferReader_Tests
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
|
||||||
public void Fire_Exception_If_Not_Enough_Data()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 0, 1, 2 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
reader.SetBuffer(buffer, 0, 3);
|
|
||||||
|
|
||||||
// 1 byte is missing.
|
|
||||||
reader.ReadFourByteInteger();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
|
||||||
public void Fire_Exception_If_Not_Enough_Data_With_Longer_Buffer()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
reader.SetBuffer(buffer, 0, 3);
|
|
||||||
|
|
||||||
// 1 byte is missing.
|
|
||||||
reader.ReadFourByteInteger();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Is_EndOfStream_Without_Buffer()
|
|
||||||
{
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
Assert.IsTrue(reader.EndOfStream);
|
|
||||||
Assert.AreEqual(0, reader.BytesLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Read_Remaining_Data_From_Larger_Buffer()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
// The used buffer contains more data than used!
|
|
||||||
reader.SetBuffer(buffer, 0, 5);
|
|
||||||
|
|
||||||
// This should only read 5 bytes even if more data is in the buffer
|
|
||||||
// due to custom bounds.
|
|
||||||
var remainingData = reader.ReadRemainingData();
|
|
||||||
|
|
||||||
Assert.IsTrue(reader.EndOfStream);
|
|
||||||
Assert.AreEqual(0, reader.BytesLeft);
|
|
||||||
Assert.AreEqual(5, remainingData.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Read_Various_Positions_and_Offsets()
|
|
||||||
{
|
|
||||||
const int NumBufferElements = 1000;
|
|
||||||
const int NumTestSequences = 10000;
|
|
||||||
|
|
||||||
const string TestString = "The quick brown fox jumps over the lazy dog.";
|
|
||||||
|
|
||||||
var rndSeed = Environment.TickCount;
|
|
||||||
// rndSeed = ...; // assign random seed here if there is an error so that it can be repeated
|
|
||||||
var rnd = new Random(rndSeed);
|
|
||||||
// rndSeed is printed when an assert fails
|
|
||||||
|
|
||||||
var bufferElements = new List<ElementReference>();
|
|
||||||
|
|
||||||
byte[] elementBuffer;
|
|
||||||
|
|
||||||
// Fill a buffer with strings or numbers in big endian format
|
|
||||||
using (var ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
using (var bw = new BinaryWriter(ms))
|
|
||||||
{
|
|
||||||
var bufferOffset = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < NumBufferElements; i++)
|
|
||||||
{
|
|
||||||
var nextElementType = (ElementReference.BufferElementType)rnd.Next(ElementReference.NumBufferElementTypes);
|
|
||||||
|
|
||||||
byte[] elementBytes = null;
|
|
||||||
uint elementNumberValue = 0; // all element number values fit into uint
|
|
||||||
string elementStringValue = null;
|
|
||||||
var elementSize = 0;
|
|
||||||
|
|
||||||
var alreadyBigEndian = false;
|
|
||||||
switch (nextElementType)
|
|
||||||
{
|
|
||||||
case ElementReference.BufferElementType.Byte:
|
|
||||||
var byteValue = (byte)i;
|
|
||||||
elementNumberValue = byteValue;
|
|
||||||
elementSize = sizeof(byte);
|
|
||||||
elementBytes = new byte[1] { byteValue };
|
|
||||||
alreadyBigEndian = true; // nothing to swap
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.TwoByteInt:
|
|
||||||
var ushortValue = (ushort)i;
|
|
||||||
elementNumberValue = ushortValue;
|
|
||||||
elementSize = sizeof(ushort);
|
|
||||||
elementBytes = BitConverter.GetBytes(ushortValue);
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.FourByteInt:
|
|
||||||
var uintValue = (uint)i;
|
|
||||||
elementNumberValue = uintValue;
|
|
||||||
elementSize = sizeof(uint);
|
|
||||||
elementBytes = BitConverter.GetBytes(uintValue);
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.VariableSizeInt:
|
|
||||||
{
|
|
||||||
elementNumberValue = (uint)i;
|
|
||||||
var writer = new MqttBufferWriter(4, 4);
|
|
||||||
writer.WriteVariableByteInteger(elementNumberValue);
|
|
||||||
elementSize = writer.Length;
|
|
||||||
elementBytes = new byte[elementSize];
|
|
||||||
var buffer = writer.GetBuffer();
|
|
||||||
Array.Copy(buffer, elementBytes, elementSize);
|
|
||||||
alreadyBigEndian = true; // nothing to swap
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.String:
|
|
||||||
{
|
|
||||||
var stringLen = rnd.Next(TestString.Length);
|
|
||||||
elementStringValue = TestString.Substring(0, stringLen); // could be empty
|
|
||||||
var writer = new MqttBufferWriter(stringLen + 1, stringLen + 1);
|
|
||||||
writer.WriteString(elementStringValue);
|
|
||||||
elementSize = writer.Length;
|
|
||||||
elementBytes = new byte[elementSize];
|
|
||||||
var buffer = writer.GetBuffer();
|
|
||||||
Array.Copy(buffer, elementBytes, elementSize);
|
|
||||||
alreadyBigEndian = true; // nothing to swap
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BitConverter.IsLittleEndian && !alreadyBigEndian)
|
|
||||||
{
|
|
||||||
Array.Reverse(elementBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bw.Write(elementBytes);
|
|
||||||
bufferElements.Add(new ElementReference(nextElementType, elementSize, elementNumberValue, elementStringValue, bufferOffset));
|
|
||||||
bufferOffset += elementSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
elementBuffer = ms.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < NumTestSequences; i++)
|
|
||||||
{
|
|
||||||
var firstElementIndex = rnd.Next(NumBufferElements - 1);
|
|
||||||
// segmentElementIndex will be < NumBufferElements - 1, which means there is at least one byte left for segment element count
|
|
||||||
var elementCount = rnd.Next(NumBufferElements - firstElementIndex) + 1;
|
|
||||||
var lastElementIndex = firstElementIndex + elementCount - 1;
|
|
||||||
// Use element index to get buffer offsets
|
|
||||||
var lastElement = bufferElements[lastElementIndex];
|
|
||||||
var segmentStartPosition = bufferElements[firstElementIndex].BufferOffset;
|
|
||||||
var segmentEndPosition = lastElement.BufferOffset + lastElement.Size;
|
|
||||||
var segmentLength = segmentEndPosition - segmentStartPosition;
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
reader.SetBuffer(elementBuffer, segmentStartPosition, segmentLength);
|
|
||||||
|
|
||||||
// read all elements in the buffer segment; values should be as expected
|
|
||||||
for (var n = 0; n < elementCount; n++)
|
|
||||||
{
|
|
||||||
var element = bufferElements[firstElementIndex + n];
|
|
||||||
uint elementNumberValue = 0;
|
|
||||||
string elementStringValue = null;
|
|
||||||
|
|
||||||
var expectedPosition = element.BufferOffset - segmentStartPosition;
|
|
||||||
Assert.AreEqual(expectedPosition, reader.Position);
|
|
||||||
|
|
||||||
switch (element.Type)
|
|
||||||
{
|
|
||||||
case ElementReference.BufferElementType.Byte:
|
|
||||||
{
|
|
||||||
elementNumberValue = reader.ReadByte();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.TwoByteInt:
|
|
||||||
{
|
|
||||||
elementNumberValue = reader.ReadTwoByteInteger();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.FourByteInt:
|
|
||||||
{
|
|
||||||
elementNumberValue = reader.ReadFourByteInteger();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.VariableSizeInt:
|
|
||||||
{
|
|
||||||
elementNumberValue = reader.ReadVariableByteInteger();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ElementReference.BufferElementType.String:
|
|
||||||
{
|
|
||||||
elementStringValue = reader.ReadString();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementStringValue != null)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(elementStringValue, element.StringValue, $"'{elementStringValue}' not equal '{element.StringValue}' with random seed {rndSeed}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.AreEqual(elementNumberValue, element.NumberValue, $"{elementNumberValue} not equal {element.NumberValue} with random seed {rndSeed}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm end of stream
|
|
||||||
Assert.AreEqual(0, reader.BytesLeft, "BytesLeft not zero as expected with random seed " + rndSeed);
|
|
||||||
Assert.IsTrue(reader.EndOfStream, "Not end of stream as expected with random seed " + rndSeed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Report_Correct_Length_For_Full_Buffer()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
reader.SetBuffer(buffer, 0, 5);
|
|
||||||
|
|
||||||
Assert.IsFalse(reader.EndOfStream);
|
|
||||||
Assert.AreEqual(5, reader.BytesLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Report_Correct_Length_For_Partial_End_Buffer()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
// The used buffer contains more data than used!
|
|
||||||
reader.SetBuffer(buffer, 5, 5);
|
|
||||||
|
|
||||||
Assert.IsFalse(reader.EndOfStream);
|
|
||||||
Assert.AreEqual(5, reader.BytesLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Report_Correct_Length_For_Partial_Start_Buffer()
|
|
||||||
{
|
|
||||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
|
|
||||||
// The used buffer contains more data than used!
|
|
||||||
reader.SetBuffer(buffer, 0, 5);
|
|
||||||
|
|
||||||
Assert.IsFalse(reader.EndOfStream);
|
|
||||||
Assert.AreEqual(5, reader.BytesLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Helper class to build up a reference to elements of various types in a buffer
|
|
||||||
class ElementReference
|
|
||||||
{
|
|
||||||
public enum BufferElementType
|
|
||||||
{
|
|
||||||
Byte,
|
|
||||||
TwoByteInt,
|
|
||||||
FourByteInt,
|
|
||||||
VariableSizeInt,
|
|
||||||
String
|
|
||||||
}
|
|
||||||
|
|
||||||
public const int NumBufferElementTypes = 5;
|
|
||||||
|
|
||||||
public ElementReference(BufferElementType type, int size, uint numberValue, string stringValue, int bufferOffset)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
Size = size;
|
|
||||||
NumberValue = numberValue;
|
|
||||||
StringValue = stringValue;
|
|
||||||
BufferOffset = bufferOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int BufferOffset { get; }
|
|
||||||
public uint NumberValue { get; }
|
|
||||||
public int Size { get; }
|
|
||||||
public string StringValue { get; }
|
|
||||||
public BufferElementType Type { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,12 +13,13 @@ using MQTTnet.Adapter;
|
|||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Helpers;
|
using MQTTnet.Tests.Extensions;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Formatter
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V3_Binary_Tests
|
public sealed class MqttPacketSerialization_V3_Binary_Tests
|
||||||
@@ -111,7 +112,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
PacketIdentifier = 123,
|
PacketIdentifier = 123,
|
||||||
Dup = true,
|
Dup = true,
|
||||||
Retain = true,
|
Retain = true,
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
|
Payload = Encoding.ASCII.GetBytes("HELLO"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
Topic = "A/B/C"
|
Topic = "A/B/C"
|
||||||
};
|
};
|
||||||
@@ -318,9 +319,10 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
var publishPacket = new MqttPublishPacket
|
var publishPacket = new MqttPublishPacket
|
||||||
{
|
{
|
||||||
Topic = "abcdefghijklmnopqrstuvwxyz0123456789",
|
Topic = "abcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
PayloadSegment = new ArraySegment<byte>(payload)
|
Payload = payload
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var serializationHelper = new MqttPacketSerializationHelper();
|
var serializationHelper = new MqttPacketSerializationHelper();
|
||||||
|
|
||||||
var buffer = serializationHelper.Encode(publishPacket);
|
var buffer = serializationHelper.Encode(publishPacket);
|
||||||
@@ -328,17 +330,17 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
|
|
||||||
Assert.IsNotNull(publishPacketCopy);
|
Assert.IsNotNull(publishPacketCopy);
|
||||||
Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic);
|
Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic);
|
||||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy.PayloadSegment.ToArray());
|
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy.Payload);
|
||||||
|
|
||||||
// Now modify the payload and test again.
|
// Now modify the payload and test again.
|
||||||
publishPacket.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("MQTT"));
|
publishPacket.Payload = Encoding.UTF8.GetBytes("MQTT");
|
||||||
|
|
||||||
buffer = serializationHelper.Encode(publishPacket);
|
buffer = serializationHelper.Encode(publishPacket);
|
||||||
var publishPacketCopy2 = serializationHelper.Decode(buffer) as MqttPublishPacket;
|
var publishPacketCopy2 = serializationHelper.Decode(buffer) as MqttPublishPacket;
|
||||||
|
|
||||||
Assert.IsNotNull(publishPacketCopy2);
|
Assert.IsNotNull(publishPacketCopy2);
|
||||||
Assert.AreEqual(publishPacket.Topic, publishPacketCopy2.Topic);
|
Assert.AreEqual(publishPacket.Topic, publishPacketCopy2.Topic);
|
||||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy2.PayloadSegment.ToArray());
|
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy2.Payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -462,7 +464,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
PacketIdentifier = 123,
|
PacketIdentifier = 123,
|
||||||
Dup = true,
|
Dup = true,
|
||||||
Retain = true,
|
Retain = true,
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
|
Payload = Encoding.ASCII.GetBytes("HELLO"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
Topic = "A/B/C"
|
Topic = "A/B/C"
|
||||||
};
|
};
|
||||||
@@ -566,6 +568,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
using (var adapter = new MqttChannelAdapter(
|
using (var adapter = new MqttChannelAdapter(
|
||||||
channel,
|
channel,
|
||||||
new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)),
|
new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)),
|
||||||
|
null,
|
||||||
new MqttNetEventLogger()))
|
new MqttNetEventLogger()))
|
||||||
{
|
{
|
||||||
var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
@@ -581,12 +584,19 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
||||||
{
|
{
|
||||||
var channel = new MemoryMqttChannel(buffer);
|
var channel = new MemoryMqttChannel(buffer);
|
||||||
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetEventLogger());
|
||||||
|
|
||||||
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
return packetFormatterAdapter.ProtocolVersion;
|
return packetFormatterAdapter.ProtocolVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MqttBufferReader ReaderFactory(byte[] data)
|
||||||
|
{
|
||||||
|
var reader = new MqttBufferReader();
|
||||||
|
reader.SetBuffer(data, 0, data.Length);
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, MqttBufferWriter bufferWriter = null) where TPacket : MqttPacket
|
TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, MqttBufferWriter bufferWriter = null) where TPacket : MqttPacket
|
||||||
{
|
{
|
||||||
var writer = bufferWriter ?? WriterFactory();
|
var writer = bufferWriter ?? WriterFactory();
|
||||||
@@ -595,7 +605,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
|
|
||||||
using (var channel = new MemoryMqttChannel(buffer.Join().ToArray()))
|
using (var channel = new MemoryMqttChannel(buffer.Join().ToArray()))
|
||||||
{
|
{
|
||||||
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
||||||
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
@@ -12,7 +10,7 @@ using MQTTnet.Formatter;
|
|||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Formatter
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V3_Tests
|
public sealed class MqttPacketSerialization_V3_Tests
|
||||||
@@ -298,7 +296,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
PacketIdentifier = 123,
|
PacketIdentifier = 123,
|
||||||
Dup = true,
|
Dup = true,
|
||||||
Retain = true,
|
Retain = true,
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
|
Payload = Encoding.ASCII.GetBytes("Payload"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
Topic = "Topic",
|
Topic = "Topic",
|
||||||
ResponseTopic = "/Response",
|
ResponseTopic = "/Response",
|
||||||
@@ -322,7 +320,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
|
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload);
|
||||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||||
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
|
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
|
||||||
|
|||||||
@@ -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.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Formatter
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V5_Tests
|
public sealed class MqttPacketSerialization_V5_Tests
|
||||||
@@ -259,7 +257,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
PacketIdentifier = 123,
|
PacketIdentifier = 123,
|
||||||
Dup = true,
|
Dup = true,
|
||||||
Retain = true,
|
Retain = true,
|
||||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
|
Payload = Encoding.ASCII.GetBytes("Payload"),
|
||||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||||
Topic = "Topic",
|
Topic = "Topic",
|
||||||
ResponseTopic = "/Response",
|
ResponseTopic = "/Response",
|
||||||
@@ -283,7 +281,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
|
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload);
|
||||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||||
Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic);
|
Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user