mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 01:03:01 +08:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cf5537043 | ||
|
|
d2ce9d607a | ||
|
|
36736914bc | ||
|
|
7823d8342e | ||
|
|
431142471f | ||
|
|
a3227a444a | ||
|
|
f74af45fba | ||
|
|
e050e2d61e | ||
|
|
ef85ef676a | ||
|
|
e977546297 | ||
|
|
ae92b0ed33 | ||
|
|
7e686d9aa5 | ||
|
|
d23bbf7bf3 | ||
|
|
d4dad4860a | ||
|
|
c009cbef76 | ||
|
|
37e60c73ae | ||
|
|
3ab095a568 | ||
|
|
f5d9b65139 | ||
|
|
31e5b40459 | ||
|
|
8fb55d75fe | ||
|
|
420222511b | ||
|
|
c5cec60809 | ||
|
|
12dd815421 | ||
|
|
f36f49cb3e |
@@ -0,0 +1,2 @@
|
|||||||
|
* [Core] Fixed dead lock and race conditions in new _AsyncLock_ implementation (#1542).
|
||||||
|
* [Client] Fixed connection freeze when using Xamarin etc.
|
||||||
@@ -3,7 +3,7 @@ name: CI
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "4.1.0.${{github.run_number}}"
|
VERSION: "4.1.2.${{github.run_number}}"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -210,6 +210,7 @@
|
|||||||
</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,5 +241,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_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@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></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -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.
|
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.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -50,39 +50,6 @@ 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
|
||||||
|
|
||||||
## Supported frameworks
|
|
||||||
|
|
||||||
| 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+ |
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
This library is used in the following projects:
|
|
||||||
|
|
||||||
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
|
|
||||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
|
||||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
|
||||||
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
|
|
||||||
* MQTT 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
|
||||||
|
|
||||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public static class Client_Connection_Samples
|
|||||||
// The used public broker sometimes has invalid certificates. This sample accepts all
|
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||||
// certificates. This should not be used in live environments.
|
// certificates. This should not be used in live environments.
|
||||||
o.CertificateValidationHandler = _ => true;
|
o.CertificateValidationHandler = _ => true;
|
||||||
|
|
||||||
// The default value is determined by the OS. Set manually to force version.
|
// The default value is determined by the OS. Set manually to force version.
|
||||||
o.SslProtocol = SslProtocols.Tls12;
|
o.SslProtocol = SslProtocols.Tls12;
|
||||||
})
|
})
|
||||||
@@ -134,15 +134,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())
|
||||||
{
|
{
|
||||||
@@ -155,18 +157,16 @@ public static class Client_Connection_Samples
|
|||||||
response.DumpToConsole();
|
response.DumpToConsole();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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().UseWebSocket4Net();
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
@@ -206,7 +206,7 @@ public static class Client_Connection_Samples
|
|||||||
using (var timeout = new CancellationTokenSource(5000))
|
using (var timeout = new CancellationTokenSource(5000))
|
||||||
{
|
{
|
||||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
|
|
||||||
response.DumpToConsole();
|
response.DumpToConsole();
|
||||||
@@ -214,6 +214,49 @@ 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(MqttClientDisconnectReason.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()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -248,7 +291,7 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Ping_Server()
|
public static async Task Ping_Server()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -321,14 +364,12 @@ public static class Client_Connection_Samples
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required
|
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
|
||||||
// in the first place.
|
if (!await mqttClient.TryPingAsync())
|
||||||
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,6 +40,53 @@ 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,6 +7,8 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Samples.Helpers;
|
using MQTTnet.Samples.Helpers;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Client;
|
namespace MQTTnet.Samples.Client;
|
||||||
@@ -23,9 +25,7 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.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,7 +41,11 @@ 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(f => { f.WithTopic("mqttnet/samples/topic/2"); })
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/2");
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
@@ -53,6 +57,95 @@ 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()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -63,14 +156,16 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.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(f => { f.WithTopic("mqttnet/samples/topic/1"); })
|
.WithTopicFilter(
|
||||||
|
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);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
var formatter = PacketFormatterAdapter;
|
var formatter = PacketFormatterAdapter;
|
||||||
|
|
||||||
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var buffer = formatter.Encode(packet);
|
var buffer = formatter.Encode(packet);
|
||||||
var msg = buffer.Join().AsMemory();
|
var msg = buffer.Join().AsMemory();
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// 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))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,14 +12,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||||
<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>
|
||||||
|
|||||||
@@ -2,6 +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 BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
@@ -70,8 +71,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +80,11 @@ 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(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
Console.WriteLine("c = SubscribeBenchmark");
|
Console.WriteLine("c = SubscribeBenchmark");
|
||||||
Console.WriteLine("d = UnsubscribeBenchmark");
|
Console.WriteLine("d = UnsubscribeBenchmark");
|
||||||
Console.WriteLine("e = MessageDeliveryBenchmark");
|
Console.WriteLine("e = MessageDeliveryBenchmark");
|
||||||
|
Console.WriteLine("f = AsyncLockBenchmark");
|
||||||
|
|
||||||
var pressedKey = Console.ReadKey(true);
|
var pressedKey = Console.ReadKey(true);
|
||||||
switch (pressedKey.KeyChar)
|
switch (pressedKey.KeyChar)
|
||||||
@@ -75,6 +76,9 @@ namespace MQTTnet.Benchmarks
|
|||||||
case 'e':
|
case 'e':
|
||||||
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
BenchmarkRunner.Run<AsyncLockBenchmark>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
return Task.FromResult(count);
|
return Task.FromResult(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||||
{
|
{
|
||||||
@@ -296,7 +296,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UnsubscribeAsync(ICollection<string> topics)
|
public Task UnsubscribeAsync(ICollection<string> topics)
|
||||||
@@ -319,7 +319,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -693,7 +693,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||||
|
|
||||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.EnterAsync().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
|
||||||
@@ -724,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.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.EnterAsync().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));
|
||||||
|
|
||||||
|
|||||||
@@ -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.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messagesLock.EnterAsync().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.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var index = _messages.IndexOf(applicationMessage);
|
var index = _messages.IndexOf(applicationMessage);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.Rpc
|
namespace MQTTnet.Extensions.Rpc
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
||||||
{
|
{
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NET452
|
#if NET452
|
||||||
@@ -128,7 +128,7 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
// 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 PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ 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;
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocket.Close();
|
_webSocket.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
@@ -154,10 +155,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
return Task.FromResult(readBytes);
|
return Task.FromResult(readBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_webSocket.Send(buffer, offset, count);
|
_webSocket.Send(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// 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.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -40,7 +40,7 @@ namespace MQTTnet.TestApp
|
|||||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
client.ConnectedAsync += async e =>
|
client.ConnectedAsync += async e =>
|
||||||
|
|||||||
@@ -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.Implementations;
|
using MQTTnet.Internal;
|
||||||
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 PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
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 Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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
|
||||||
{
|
{
|
||||||
@@ -124,7 +125,7 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
return Implementations.PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Cleanup()
|
public async Task Cleanup()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ 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')
|
||||||
@@ -88,6 +89,10 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Security.Authentication;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -144,7 +144,7 @@ namespace MQTTnet.TestApp
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(options);
|
await client.ConnectAsync(options);
|
||||||
|
|||||||
@@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -69,13 +67,13 @@ namespace MQTTnet.TestApp
|
|||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.RetainedMessagesClearedAsync += e =>
|
mqttServer.RetainedMessagesClearedAsync += e =>
|
||||||
{
|
{
|
||||||
File.Delete(Filename);
|
File.Delete(Filename);
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||||
@@ -93,7 +91,7 @@ namespace MQTTnet.TestApp
|
|||||||
|
|
||||||
e.LoadedRetainedMessages = retainedMessages;
|
e.LoadedRetainedMessages = retainedMessages;
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
@@ -111,7 +109,7 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.ValidatingConnectionAsync += e =>
|
mqttServer.ValidatingConnectionAsync += e =>
|
||||||
@@ -124,7 +122,7 @@ namespace MQTTnet.TestApp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||||
@@ -140,7 +138,7 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
@@ -149,7 +147,7 @@ namespace MQTTnet.TestApp
|
|||||||
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
||||||
ConsoleColor.Magenta);
|
ConsoleColor.Magenta);
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
//options.ApplicationMessageInterceptor = c =>
|
//options.ApplicationMessageInterceptor = c =>
|
||||||
@@ -177,7 +175,7 @@ namespace MQTTnet.TestApp
|
|||||||
mqttServer.ClientConnectedAsync += e =>
|
mqttServer.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Console.Write("Client disconnected event fired.");
|
Console.Write("Client disconnected event fired.");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttServer.StartAsync();
|
await mqttServer.StartAsync();
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ 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.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.Client
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
@@ -39,7 +37,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
managedClient.ConnectingFailedAsync += args =>
|
managedClient.ConnectingFailedAsync += args =>
|
||||||
{
|
{
|
||||||
connectingFailedEventArgs = args;
|
connectingFailedEventArgs = args;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.StartAsync(
|
await managedClient.StartAsync(
|
||||||
@@ -117,7 +115,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivingClient.ApplicationMessageReceivedAsync += e =>
|
receivingClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receivingClient.SubscribeAsync("My/last/will");
|
await receivingClient.SubscribeAsync("My/last/will");
|
||||||
@@ -281,7 +279,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait a bit for the retained message to be available
|
// Wait a bit for the retained message to be available
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
await sendingClient.DisconnectAsync();
|
await sendingClient.DisconnectAsync();
|
||||||
|
|
||||||
@@ -290,28 +288,29 @@ namespace MQTTnet.Tests.Client
|
|||||||
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 PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.SubscribeAsync("topic");
|
await managedClient.SubscribeAsync("topic");
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||||
|
|
||||||
await managedClient.StopAsync();
|
await managedClient.StopAsync();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
// 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
|
||||||
@@ -319,7 +318,9 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
// 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 Task.Delay(500);
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(2, receivedManagedMessages.Count);
|
Assert.AreEqual(2, receivedManagedMessages.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +429,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
managedClient.ConnectedAsync += e =>
|
managedClient.ConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
connected.TrySetResult(true);
|
connected.TrySetResult(true);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return connected.Task;
|
return connected.Task;
|
||||||
@@ -457,7 +458,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
result.TrySetResult(receivedMessages);
|
result.TrySetResult(receivedMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return result.Task;
|
return result.Task;
|
||||||
@@ -486,7 +487,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
_messages = messages;
|
_messages = messages;
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,15 +14,17 @@ 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.Implementations;
|
using MQTTnet.Internal;
|
||||||
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.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class Client_Tests : BaseTestClass
|
public sealed class MqttClient_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||||
@@ -56,7 +58,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = InvokeInternal();
|
_ = InvokeInternal();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
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));
|
||||||
@@ -107,7 +109,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += args =>
|
client.DisconnectedAsync += args =>
|
||||||
{
|
{
|
||||||
disconnectHandlerCalled = true;
|
disconnectHandlerCalled = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
||||||
@@ -121,7 +123,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(100); // disconnected handler is called async
|
await LongTestDelay(); // disconnected handler is called async
|
||||||
Assert.IsTrue(disconnectHandlerCalled);
|
Assert.IsTrue(disconnectHandlerCalled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +138,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
ex = e.Exception;
|
ex = e.Exception;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -145,6 +147,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -167,7 +170,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
server.InterceptingPublishAsync += c =>
|
server.InterceptingPublishAsync += c =>
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.SendAsync(
|
await client.SendAsync(
|
||||||
@@ -208,7 +211,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
handlerFired = true;
|
handlerFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -239,10 +242,10 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
for (var i = 0; i < 98; i++)
|
for (var i = 0; i < 98; i++)
|
||||||
{
|
{
|
||||||
Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
|
Assert.IsFalse(clients[i].TryPingAsync().GetAwaiter().GetResult(), $"clients[{i}] is not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(clients[99].IsConnected);
|
Assert.IsTrue(clients[99].TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
Assert.AreEqual(1, clientStatus.Count);
|
Assert.AreEqual(1, clientStatus.Count);
|
||||||
Assert.AreEqual(1, sessionStatus.Count);
|
Assert.AreEqual(1, sessionStatus.Count);
|
||||||
@@ -252,7 +255,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receiveClient.ApplicationMessageReceivedAsync += e =>
|
receiveClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiveClient.SubscribeAsync("x");
|
await receiveClient.SubscribeAsync("x");
|
||||||
@@ -310,7 +313,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||||
@@ -332,7 +335,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,11 +346,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
|
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,7 +456,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedValues.Add(value);
|
receivedValues.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||||
@@ -531,7 +534,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client2.ApplicationMessageReceivedAsync += e =>
|
client2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client2.SubscribeAsync(client2Topic);
|
await client2.SubscribeAsync(client2Topic);
|
||||||
@@ -566,7 +569,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync("a");
|
await client1.SubscribeAsync("a");
|
||||||
@@ -591,17 +594,17 @@ namespace MQTTnet.Tests.Client
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(await client.TryPingAsync());
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(await client.TryPingAsync());
|
||||||
|
|
||||||
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(client.IsConnected);
|
Assert.IsTrue(await client.TryPingAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,6 +640,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
||||||
@@ -656,11 +660,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
for (var i = 0; i < 5; i++)
|
for (var i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
@@ -671,6 +675,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,7 +683,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
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(client.IsConnected);
|
await client.PingAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +728,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replies.Add(eventArgs.ApplicationMessage.Topic);
|
replies.Add(eventArgs.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += Handler2;
|
client2.ApplicationMessageReceivedAsync += Handler2;
|
||||||
@@ -761,7 +766,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += async e =>
|
client2.ApplicationMessageReceivedAsync += async e =>
|
||||||
@@ -812,7 +817,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
@@ -828,14 +833,14 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
@@ -851,11 +856,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -892,7 +897,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
||||||
@@ -917,14 +922,14 @@ namespace MQTTnet.Tests.Client
|
|||||||
client1.DisconnectedAsync += c =>
|
client1.DisconnectedAsync += c =>
|
||||||
{
|
{
|
||||||
disconnectedFired = true;
|
disconnectedFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageReceived = false;
|
var messageReceived = false;
|
||||||
client1.ApplicationMessageReceivedAsync += e =>
|
client1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
messageReceived = true;
|
messageReceived = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
||||||
@@ -941,7 +946,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
Assert.IsTrue(messageReceived);
|
Assert.IsTrue(messageReceived);
|
||||||
Assert.IsTrue(client1.IsConnected);
|
Assert.IsTrue(client1.TryPingAsync().GetAwaiter().GetResult());
|
||||||
Assert.IsFalse(disconnectedFired);
|
Assert.IsFalse(disconnectedFired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Diagnostics
|
namespace MQTTnet.Tests.Diagnostics
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Diagnostics
|
|||||||
mqttClient.InspectPackage += eventArgs =>
|
mqttClient.InspectPackage += eventArgs =>
|
||||||
{
|
{
|
||||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|||||||
@@ -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.Implementations;
|
using MQTTnet.Internal;
|
||||||
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 PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System;
|
|||||||
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.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Internal
|
namespace MQTTnet.Tests.Internal
|
||||||
@@ -16,6 +15,28 @@ namespace MQTTnet.Tests.Internal
|
|||||||
{
|
{
|
||||||
int _testEventAsyncCount;
|
int _testEventAsyncCount;
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Attach_and_Detach_Sync_And_Async_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEvent += OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(2, _testEventAsyncCount);
|
||||||
|
|
||||||
|
testClass.TestEvent -= OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(3, _testEventAsyncCount);
|
||||||
|
|
||||||
|
testClass.TestEventAsync -= OnTestEventAsync;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(3, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Attach_EventHandler()
|
public async Task Attach_EventHandler()
|
||||||
{
|
{
|
||||||
@@ -28,6 +49,19 @@ namespace MQTTnet.Tests.Internal
|
|||||||
Assert.AreEqual(1, _testEventAsyncCount);
|
Assert.AreEqual(1, _testEventAsyncCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Attach_Sync_And_Async_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEvent += OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
|
||||||
|
Assert.AreEqual(2, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Detach_EventHandler()
|
public async Task Detach_EventHandler()
|
||||||
{
|
{
|
||||||
@@ -41,22 +75,62 @@ namespace MQTTnet.Tests.Internal
|
|||||||
Assert.AreEqual(0, _testEventAsyncCount);
|
Assert.AreEqual(0, _testEventAsyncCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Has_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
|
||||||
|
Assert.AreEqual(true, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void No_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Remove_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEventAsync -= OnTestEventAsync;
|
||||||
|
|
||||||
|
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestEvent(EventArgs arg)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
Task OnTestEventAsync(EventArgs arg)
|
Task OnTestEventAsync(EventArgs arg)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _testEventAsyncCount);
|
Interlocked.Increment(ref _testEventAsyncCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class TestClass
|
sealed class TestClass
|
||||||
{
|
{
|
||||||
readonly AsyncEvent<EventArgs> _testEvent = new AsyncEvent<EventArgs>();
|
readonly AsyncEvent<EventArgs> _testEvent = new AsyncEvent<EventArgs>();
|
||||||
|
|
||||||
|
public event Action<EventArgs> TestEvent
|
||||||
|
{
|
||||||
|
add => _testEvent.AddHandler(value);
|
||||||
|
remove => _testEvent.RemoveHandler(value);
|
||||||
|
}
|
||||||
|
|
||||||
public event Func<EventArgs, Task> TestEventAsync
|
public event Func<EventArgs, Task> TestEventAsync
|
||||||
{
|
{
|
||||||
add => _testEvent.AddHandler(value);
|
add => _testEvent.AddHandler(value);
|
||||||
remove => _testEvent.RemoveHandler(value);
|
remove => _testEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasTestHandlers => _testEvent.HasHandlers;
|
||||||
|
|
||||||
public Task FireEventAsync(EventArgs eventArgs)
|
public Task FireEventAsync(EventArgs eventArgs)
|
||||||
{
|
{
|
||||||
return _testEvent.InvokeAsync(eventArgs);
|
return _testEvent.InvokeAsync(eventArgs);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// 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.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
@@ -11,8 +12,186 @@ using MQTTnet.Internal;
|
|||||||
namespace MQTTnet.Tests.Internal
|
namespace MQTTnet.Tests.Internal
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class AsyncLock_Tests
|
public sealed class AsyncLock_Tests
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Cancellation_Of_Awaiter()
|
||||||
|
{
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// This call will not yet "release" the lock due to missing _using_.
|
||||||
|
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
Debug.WriteLine("Prepared locked lock.");
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 1 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 2 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 3 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(4)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 4 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 5 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(6)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 6 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Debug.WriteLine("Delay before release...");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(3.1));
|
||||||
|
releaser.Dispose();
|
||||||
|
|
||||||
|
Debug.WriteLine("Wait for all tasks...");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(6.1));
|
||||||
|
|
||||||
|
Assert.AreEqual(3, counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Lock_Parallel_Tasks()
|
||||||
|
{
|
||||||
|
const int taskCount = 50;
|
||||||
|
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
var tasks = new Task[taskCount];
|
||||||
|
var globalI = 0;
|
||||||
|
for (var i = 0; i < taskCount; i++)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
tasks[i] = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync())
|
||||||
|
{
|
||||||
|
var localI = globalI;
|
||||||
|
await Task.Delay(5); // Increase the chance for wrong data.
|
||||||
|
localI++;
|
||||||
|
globalI = localI;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
Assert.AreEqual(taskCount, globalI);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Lock_10_Parallel_Tasks_With_Dispose_Doesnt_Lockup()
|
||||||
|
{
|
||||||
|
const int ThreadsCount = 10;
|
||||||
|
|
||||||
|
var threads = new Task[ThreadsCount];
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
var globalI = 0;
|
||||||
|
for (var i = 0; i < ThreadsCount; i++)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
threads[i] = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync())
|
||||||
|
{
|
||||||
|
var localI = globalI;
|
||||||
|
await Task.Delay(10); // Increase the chance for wrong data.
|
||||||
|
localI++;
|
||||||
|
globalI = localI;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ContinueWith(
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
if (globalI == 5)
|
||||||
|
{
|
||||||
|
@lock.Dispose();
|
||||||
|
@lock = new AsyncLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.Exception != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(x.Exception.GetBaseException().GetType().Name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(threads);
|
||||||
|
|
||||||
|
// Expect only 6 because the others are failing due to disposal (if (globalI == 5)).
|
||||||
|
Assert.AreEqual(6, globalI);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Lock_Serial_Calls()
|
public async Task Lock_Serial_Calls()
|
||||||
{
|
{
|
||||||
@@ -21,7 +200,7 @@ namespace MQTTnet.Tests.Internal
|
|||||||
var @lock = new AsyncLock();
|
var @lock = new AsyncLock();
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
{
|
{
|
||||||
using (await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
sum++;
|
sum++;
|
||||||
}
|
}
|
||||||
@@ -37,66 +216,76 @@ namespace MQTTnet.Tests.Internal
|
|||||||
var @lock = new AsyncLock();
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
// This call will never "release" the lock due to missing _using_.
|
// This call will never "release" the lock due to missing _using_.
|
||||||
await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
|
await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
{
|
{
|
||||||
await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[TestMethod]
|
|
||||||
//public async Task Test_Cancellation_With_Later_Access()
|
|
||||||
//{
|
|
||||||
// var @lock = new AsyncLock();
|
|
||||||
|
|
||||||
// var releaser = await @lock.WaitAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
|
||||||
// {
|
|
||||||
// await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (OperationCanceledException)
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// releaser.Dispose();
|
|
||||||
|
|
||||||
// using (await @lock.WaitAsync().ConfigureAwait(false))
|
|
||||||
// {
|
|
||||||
// // When the method finished, the thread got access.
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Lock_10_Parallel_Tasks()
|
public async Task Test_Cancellation_With_Later_Access()
|
||||||
{
|
{
|
||||||
const int ThreadsCount = 10;
|
var asyncLock = new AsyncLock();
|
||||||
|
|
||||||
var threads = new Task[ThreadsCount];
|
var releaser = await asyncLock.EnterAsync().ConfigureAwait(false);
|
||||||
var @lock = new AsyncLock();
|
|
||||||
var globalI = 0;
|
try
|
||||||
for (var i = 0; i < ThreadsCount; i++)
|
|
||||||
{
|
{
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
threads[i] = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
using (var releaser = await @lock.WaitAsync(CancellationToken.None))
|
await asyncLock.EnterAsync(timeout.Token).ConfigureAwait(false);
|
||||||
{
|
}
|
||||||
var localI = globalI;
|
|
||||||
await Task.Delay(10); // Increase the chance for wrong data.
|
Assert.Fail("Exception should be thrown!");
|
||||||
localI++;
|
}
|
||||||
globalI = localI;
|
catch (OperationCanceledException)
|
||||||
}
|
{
|
||||||
});
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.WaitAll(threads);
|
releaser.Dispose();
|
||||||
Assert.AreEqual(ThreadsCount, globalI);
|
|
||||||
|
using (await asyncLock.EnterAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
// When the method finished, the thread got access.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Use_After_Cancellation()
|
||||||
|
{
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// This call will not yet "release" the lock due to missing _using_.
|
||||||
|
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Expected exception!
|
||||||
|
}
|
||||||
|
|
||||||
|
releaser.Dispose();
|
||||||
|
|
||||||
|
// Regular usage after cancellation.
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net452;net461;netcoreapp3.1;net6.0</TargetFrameworks>
|
<TargetFrameworks>net452;net461;net48;netcoreapp3.1;net6.0</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Mockups;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.MQTTv5
|
namespace MQTTnet.Tests.MQTTv5
|
||||||
{
|
{
|
||||||
@@ -43,7 +42,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||||
@@ -245,7 +244,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using MQTTnet.Formatter;
|
|||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.MQTTv5
|
namespace MQTTnet.Tests.MQTTv5
|
||||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
@@ -171,11 +171,53 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
// Session should be present
|
// Session should be present
|
||||||
|
|
||||||
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
[TestMethod]
|
||||||
|
public async Task Disconnect_with_Reason()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
|
||||||
|
|
||||||
|
string testClientId = null;
|
||||||
|
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||||
|
{
|
||||||
|
testClientId = e.ClientId;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|
||||||
|
client.DisconnectedAsync += e =>
|
||||||
|
{
|
||||||
|
disconnectReason = e.Reason;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Test client should be connected now
|
||||||
|
|
||||||
|
Assert.IsTrue(testClientId != null);
|
||||||
|
|
||||||
|
// Have the server disconnect the client with AdministrativeAction reason
|
||||||
|
|
||||||
|
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.AdministrativeAction);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// The reason should be returned to the client in the DISCONNECT packet
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientDisconnectReason.AdministrativeAction, disconnectReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
||||||
{
|
{
|
||||||
return testEnvironment.Factory.CreateClientOptionsBuilder()
|
return testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
@@ -32,12 +34,12 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
@@ -45,9 +47,9 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
|
return _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
_receivedEventArgs.Add(eventArgs);
|
_receivedEventArgs.Add(eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using MQTTnet.Client;
|
|||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.Rpc;
|
using MQTTnet.Extensions.Rpc;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.LowLevelClient;
|
using MQTTnet.LowLevelClient;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -220,7 +220,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
@@ -261,7 +261,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return Server;
|
return Server;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using MQTTnet.Extensions.Rpc;
|
|||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
@@ -189,7 +190,7 @@ namespace MQTTnet.Tests
|
|||||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||||
{
|
{
|
||||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||||
await PlatformAbstractionLayer.CompletedTask;
|
await CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Diagnostics;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests
|
|||||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiverClient.SubscribeAsync("#");
|
await receiverClient.SubscribeAsync("#");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -47,20 +47,20 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||||
{
|
{
|
||||||
serverConnectedClientId = args.ClientId;
|
serverConnectedClientId = args.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||||
{
|
{
|
||||||
serverDisconnectedClientId = args.ClientId;
|
serverDisconnectedClientId = args.ClientId;
|
||||||
disconnectedMre.Set();
|
disconnectedMre.Set();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Arrange client
|
// Arrange client
|
||||||
@@ -68,7 +68,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ConnectedAsync += args =>
|
client.ConnectedAsync += args =>
|
||||||
{
|
{
|
||||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientConnectedAsync += e =>
|
server.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||||
@@ -81,7 +81,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientSubscribedTopicAsync += e =>
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -108,7 +108,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientUnsubscribedTopicAsync += e =>
|
server.ClientUnsubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -161,7 +161,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.StartedAsync += e =>
|
server.StartedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
@@ -183,7 +183,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.StoppedAsync += e =>
|
server.StoppedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -99,7 +98,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ValidatingConnectionAsync += e =>
|
server.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var connectingFailedException = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => testEnvironment.ConnectClient());
|
var connectingFailedException = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => testEnvironment.ConnectClient());
|
||||||
@@ -122,7 +121,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
|
e.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare some retained messages.
|
// Prepare some retained messages.
|
||||||
@@ -146,7 +145,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
buffer.Append(e.ApplicationMessage.Topic);
|
buffer.Append(e.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client2.SubscribeAsync("y");
|
await client2.SubscribeAsync("y");
|
||||||
@@ -172,13 +171,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientConnectedAsync += e =>
|
server.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref clientConnectedCalled);
|
Interlocked.Increment(ref clientConnectedCalled);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref clientDisconnectedCalled);
|
Interlocked.Increment(ref clientDisconnectedCalled);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||||
@@ -225,7 +224,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
// Clear retained message.
|
// Clear retained message.
|
||||||
await client.PublishAsync(
|
await client.PublishAsync(
|
||||||
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
.WithPayload(PlatformAbstractionLayer.EmptyByteArray)
|
.WithPayload(EmptyBuffer.Array)
|
||||||
.WithRetainFlag()
|
.WithRetainFlag()
|
||||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
.Build());
|
.Build());
|
||||||
@@ -276,7 +275,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
e.ApplicationMessage = new MqttApplicationMessage { Topic = "new_topic" };
|
e.ApplicationMessage = new MqttApplicationMessage { Topic = "new_topic" };
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
string receivedTopic = null;
|
string receivedTopic = null;
|
||||||
@@ -286,7 +285,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += a =>
|
c1.ApplicationMessageReceivedAsync += a =>
|
||||||
{
|
{
|
||||||
receivedTopic = a.ApplicationMessage.Topic;
|
receivedTopic = a.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("original_topic").Build());
|
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("original_topic").Build());
|
||||||
@@ -305,7 +304,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
|
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
@@ -316,7 +315,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c2.ApplicationMessageReceivedAsync += e =>
|
c2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(e.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(e.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
|
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
|
||||||
@@ -339,7 +338,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ApplicationMessageNotConsumedAsync += e =>
|
server.ApplicationMessageNotConsumedAsync += e =>
|
||||||
{
|
{
|
||||||
undeliverd = e.ApplicationMessage.Topic;
|
undeliverd = e.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -376,7 +375,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -400,7 +399,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
s.RetainedMessageChangedAsync += e =>
|
s.RetainedMessageChangedAsync += e =>
|
||||||
{
|
{
|
||||||
savedRetainedMessages = e.StoredRetainedMessages;
|
savedRetainedMessages = e.StoredRetainedMessages;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
@@ -440,7 +439,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedTopic = e.ApplicationMessage.Topic;
|
receivedTopic = e.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync("#");
|
await c1.SubscribeAsync("#");
|
||||||
@@ -487,7 +486,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).Build();
|
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).Build();
|
||||||
@@ -521,13 +520,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
c2.ApplicationMessageReceivedAsync += e =>
|
c2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c3.ApplicationMessageReceivedAsync += e =>
|
c3.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }).ConfigureAwait(false);
|
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }).ConfigureAwait(false);
|
||||||
@@ -581,7 +580,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("c");
|
events.Add("c");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
@@ -591,7 +590,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("d");
|
events.Add("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var clientOptionsBuilder = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString());
|
var clientOptionsBuilder = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString());
|
||||||
@@ -621,7 +620,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("r");
|
events.Add("r");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c2.SubscribeAsync("topic");
|
await c2.SubscribeAsync("topic");
|
||||||
@@ -669,7 +668,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ValidatingConnectionAsync += e =>
|
server.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
ConnectionValidationHandler(e);
|
ConnectionValidationHandler(e);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var events = new List<string>();
|
var events = new List<string>();
|
||||||
@@ -681,7 +680,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("c");
|
events.Add("c");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
@@ -691,7 +690,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("d");
|
events.Add("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithClientId("same_id");
|
var clientOptions = new MqttClientOptionsBuilder().WithClientId("same_id");
|
||||||
@@ -706,7 +705,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("x");
|
events.Add("x");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
@@ -716,7 +715,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("r");
|
events.Add("r");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c1.SubscribeAsync("topic").Wait();
|
c1.SubscribeAsync("topic").Wait();
|
||||||
@@ -785,7 +784,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client1.ApplicationMessageReceivedAsync += e =>
|
client1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedBody = e.ApplicationMessage.Payload;
|
receivedBody = e.ApplicationMessage.Payload;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync("string");
|
await client1.SubscribeAsync("string");
|
||||||
@@ -822,7 +821,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -850,7 +849,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.DisconnectedAsync += e =>
|
c1.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
disconnectCalled++;
|
disconnectCalled++;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
@@ -935,7 +934,62 @@ namespace MQTTnet.Tests.Server
|
|||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Disconnect_Client_with_Reason()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var disconnectPacketReceived = false;
|
||||||
|
|
||||||
|
string testClientId = null;
|
||||||
|
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||||
|
{
|
||||||
|
testClientId = e.ClientId;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
|
client.InspectPackage += e =>
|
||||||
|
{
|
||||||
|
if (e.Buffer.Length > 0)
|
||||||
|
{
|
||||||
|
if (e.Buffer[0] == (byte)MqttControlPacketType.Disconnect << 4)
|
||||||
|
{
|
||||||
|
disconnectPacketReceived = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ConnectAsync(
|
||||||
|
new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||||
|
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
|
||||||
|
.Build()
|
||||||
|
);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Test client should be connected now
|
||||||
|
|
||||||
|
Assert.IsTrue(testClientId != null);
|
||||||
|
|
||||||
|
// Have the server disconnect the client with SessionTakenOver reason; no DISCONNECT should be sent to MQTT 3.1.1 clients
|
||||||
|
|
||||||
|
await testEnvironment.Server.DisconnectClientAsync(testClientId, Protocol.MqttDisconnectReasonCode.SessionTakenOver);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// MQTT 3.1.1 client should not receive a disconnect packet
|
||||||
|
|
||||||
|
Assert.IsFalse(disconnectPacketReceived, "Disconnect packet received when none is expected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionValidationHandler(ValidatingConnectionEventArgs eventArgs)
|
void ConnectionValidationHandler(ValidatingConnectionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (_connected.ContainsKey(eventArgs.ClientId))
|
if (_connected.ContainsKey(eventArgs.ClientId))
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiverClient.SubscribeAsync("t/+");
|
await receiverClient.SubscribeAsync("t/+");
|
||||||
@@ -74,7 +74,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
@@ -137,7 +137,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||||
{
|
{
|
||||||
eventArgs = args;
|
eventArgs = args;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client1 = await testEnvironment.ConnectClient();
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
@@ -53,7 +53,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||||
{
|
{
|
||||||
eventArgs = args;
|
eventArgs = args;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client1 = await testEnvironment.ConnectClient();
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
@@ -88,7 +88,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
eventArgs.Add(args);
|
eventArgs.Add(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client1 = await testEnvironment.ConnectClient();
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||||
@@ -47,7 +47,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var validClient = testEnvironment.CreateClient())
|
using (var validClient = testEnvironment.CreateClient())
|
||||||
@@ -157,7 +157,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
var client = testEnvironment.CreateClient();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using MQTTnet.Adapter;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -28,7 +29,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||||
e.ServerReference = "new_server";
|
e.ServerReference = "new_server";
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -29,7 +28,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||||
|
|
||||||
const string ClientId = "Client1";
|
const string clientId = "Client1";
|
||||||
|
|
||||||
// Create client with clean session and long session expiry interval
|
// Create client with clean session and long session expiry interval
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||||
.WithCleanSession() // start and end with clean session
|
.WithCleanSession() // start and end with clean session
|
||||||
.WithClientId(ClientId)
|
.WithClientId(clientId)
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
// Disconnect; empty session should be removed from server
|
// Disconnect; empty session should be removed from server
|
||||||
@@ -57,7 +56,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||||
.WithCleanSession(false) // see if there is a session
|
.WithCleanSession(false) // see if there is a session
|
||||||
.WithClientId(ClientId)
|
.WithClientId(clientId)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.SessionDeletedAsync += e =>
|
server.SessionDeletedAsync += e =>
|
||||||
{
|
{
|
||||||
deletedEventFired = true;
|
deletedEventFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -111,7 +110,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.SessionItems["can_subscribe_x"] = true;
|
e.SessionItems["can_subscribe_x"] = true;
|
||||||
e.SessionItems["default_payload"] = "Hello World";
|
e.SessionItems["default_payload"] = "Hello World";
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await testEnvironment.ConnectClient();
|
await testEnvironment.ConnectClient();
|
||||||
@@ -123,8 +122,12 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET452
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Handle_Parallel_Connection_Attempts()
|
[DataRow(MqttProtocolVersion.V311)]
|
||||||
|
[DataRow(MqttProtocolVersion.V500)]
|
||||||
|
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
|
||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
@@ -132,7 +135,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
|
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(1)).WithProtocolVersion(protocolVersion);
|
||||||
|
|
||||||
var hasReceive = false;
|
var hasReceive = false;
|
||||||
|
|
||||||
@@ -146,11 +149,14 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
// Try to connect 50 clients at the same time.
|
// Try to connect 50 clients at the same time.
|
||||||
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||||
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
|
||||||
|
|
||||||
Assert.AreEqual(1, connectedClients.Count);
|
|
||||||
|
|
||||||
await Task.Delay(5000);
|
await Task.Delay(5000);
|
||||||
|
|
||||||
|
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, connectedClients.Count);
|
||||||
|
|
||||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||||
@@ -161,26 +167,8 @@ namespace MQTTnet.Tests.Server
|
|||||||
Assert.AreEqual(true, hasReceive);
|
Assert.AreEqual(true, hasReceive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
[TestMethod]
|
|
||||||
public async Task Manage_Session_MaxParallel()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
testEnvironment.IgnoreClientLogErrors = true;
|
|
||||||
var serverOptions = new MqttServerOptionsBuilder();
|
|
||||||
await testEnvironment.StartServer(serverOptions);
|
|
||||||
|
|
||||||
var options = new MqttClientOptionsBuilder().WithClientId("1");
|
|
||||||
|
|
||||||
var clients = await Task.WhenAll(Enumerable.Range(0, 10).Select(i => TryConnect(testEnvironment, options)));
|
|
||||||
|
|
||||||
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
|
||||||
|
|
||||||
Assert.AreEqual(1, connectedClients.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||||
@@ -200,7 +188,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c.AutoAcknowledge = false;
|
c.AutoAcknowledge = false;
|
||||||
++count;
|
++count;
|
||||||
Console.WriteLine("process");
|
Console.WriteLine("process");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await subscriber.SubscribeAsync("#", qos);
|
await subscriber.SubscribeAsync("#", qos);
|
||||||
@@ -237,7 +225,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client1.DisconnectedAsync += c =>
|
client1.DisconnectedAsync += c =>
|
||||||
{
|
{
|
||||||
disconnectReason = c.Reason;
|
disconnectReason = c.Reason;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client2 = await testEnvironment.ConnectClient(options);
|
var client2 = await testEnvironment.ConnectClient(options);
|
||||||
@@ -263,7 +251,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.SessionItems["can_subscribe_x"] = true;
|
e.SessionItems["can_subscribe_x"] = true;
|
||||||
e.SessionItems["default_payload"] = "Hello World";
|
e.SessionItems["default_payload"] = "Hello World";
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.InterceptingSubscriptionAsync += e =>
|
server.InterceptingSubscriptionAsync += e =>
|
||||||
@@ -276,13 +264,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
string receivedPayload = null;
|
string receivedPayload = null;
|
||||||
@@ -291,7 +279,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var subscribeResult = await client.SubscribeAsync("x");
|
var subscribeResult = await client.SubscribeAsync("x");
|
||||||
@@ -341,7 +329,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
@@ -349,10 +337,10 @@ namespace MQTTnet.Tests.Server
|
|||||||
// C2 has the last will defined.
|
// C2 has the last will defined.
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
await testEnvironment.ConnectClient(clientOptions);
|
||||||
|
|
||||||
// C3 will do the connection takeover.
|
// C3 will do the connection takeover.
|
||||||
var c3 = await testEnvironment.ConnectClient(clientOptions);
|
await testEnvironment.ConnectClient(clientOptions);
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
|
|
||||||
@@ -360,41 +348,29 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sendClient = await testEnvironment.ConnectClient(options);
|
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
|
||||||
|
|
||||||
sendClient.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
onReceive();
|
onReceive();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var subscribeTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||||
{
|
{
|
||||||
await sendClient.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, subscribeTimeout.Token).ConfigureAwait(false);
|
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendClient;
|
return client;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<IMqttClient> TryConnect(TestEnvironment testEnvironment, MqttClientOptionsBuilder options)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await testEnvironment.ConnectClient(options);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -56,7 +56,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientSubscribedTopicAsync += e =>
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -81,7 +81,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
// Set the topic to "a" regards what the client wants to subscribe.
|
// Set the topic to "a" regards what the client wants to subscribe.
|
||||||
e.TopicFilter.Topic = "a";
|
e.TopicFilter.Topic = "a";
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var topicAReceived = false;
|
var topicAReceived = false;
|
||||||
@@ -99,7 +99,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
topicBReceived = true;
|
topicBReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.SubscribeAsync("b");
|
await client.SubscribeAsync("b");
|
||||||
@@ -146,7 +146,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 500; i++)
|
for (var i = 0; i < 500; i++)
|
||||||
@@ -189,7 +189,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||||
@@ -229,7 +229,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
||||||
@@ -267,7 +267,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
||||||
@@ -301,7 +301,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||||
@@ -316,7 +316,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientSubscribedTopicAsync += e =>
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||||
@@ -331,7 +331,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientUnsubscribedTopicAsync += e =>
|
server.ClientUnsubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.UnsubscribeAsync("a");
|
await c1.UnsubscribeAsync("a");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync("#");
|
await c1.SubscribeAsync("#");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
@@ -53,7 +53,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace MQTTnet.Adapter
|
|||||||
{
|
{
|
||||||
const uint ErrorOperationAborted = 0x800703E3;
|
const uint ErrorOperationAborted = 0x800703E3;
|
||||||
const int ReadBufferSize = 4096;
|
const int ReadBufferSize = 4096;
|
||||||
|
|
||||||
readonly IMqttChannel _channel;
|
readonly IMqttChannel _channel;
|
||||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||||
readonly MqttNetSourceLogger _logger;
|
readonly MqttNetSourceLogger _logger;
|
||||||
@@ -73,7 +73,26 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
/*
|
||||||
|
* We have to implement a small workaround here to support connecting in Xamarin
|
||||||
|
* with a disabled WiFi network. If the WiFi is disabled the connect method will
|
||||||
|
* block forever. Even a cancellation token is not supported properly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var connectTask = _channel.ConnectAsync(cancellationToken);
|
||||||
|
|
||||||
|
var timeout = new TaskCompletionSource<object>();
|
||||||
|
using (cancellationToken.Register(() => timeout.TrySetResult(null)))
|
||||||
|
{
|
||||||
|
await Task.WhenAny(connectTask, timeout.Task).ConfigureAwait(false);
|
||||||
|
if (timeout.Task.IsCompleted && !connectTask.IsCompleted)
|
||||||
|
{
|
||||||
|
throw new OperationCanceledException("MQTT connect cancelled.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the exception from the connect task gets thrown.
|
||||||
|
await connectTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -176,7 +195,7 @@ namespace MQTTnet.Adapter
|
|||||||
// This lock makes sure that multiple threads can send packets at the same time.
|
// This lock makes sure that multiple threads can send packets at the same time.
|
||||||
// This is required when a disconnect is sent from another thread while the
|
// This is required when a disconnect is sent from another thread while the
|
||||||
// worker thread is still sending publish packets etc.
|
// worker thread is still sending publish packets etc.
|
||||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
using (await _syncRoot.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@@ -188,11 +207,14 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
||||||
|
|
||||||
await _channel.WriteAsync(packetBuffer.Packet.Array, packetBuffer.Packet.Offset, packetBuffer.Packet.Count, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (packetBuffer.Payload.Count > 0)
|
if (packetBuffer.Payload.Count > 0)
|
||||||
{
|
{
|
||||||
await _channel.WriteAsync(packetBuffer.Payload.Array, packetBuffer.Payload.Offset, packetBuffer.Payload.Count, cancellationToken).ConfigureAwait(false);
|
await _channel.WriteAsync(packetBuffer.Packet, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
await _channel.WriteAsync(packetBuffer.Payload, true, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _channel.WriteAsync(packetBuffer.Packet, true, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
|
Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
|
||||||
@@ -341,7 +363,7 @@ namespace MQTTnet.Adapter
|
|||||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||||
if (fixedHeader.RemainingLength == 0)
|
if (fixedHeader.RemainingLength == 0)
|
||||||
{
|
{
|
||||||
return new ReceivedMqttPacket(fixedHeader.Flags, PlatformAbstractionLayer.EmptyByteArraySegment, 2);
|
return new ReceivedMqttPacket(fixedHeader.Flags, EmptyBuffer.ArraySegment, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodyLength = fixedHeader.RemainingLength;
|
var bodyLength = fixedHeader.RemainingLength;
|
||||||
|
|||||||
@@ -12,13 +12,17 @@ namespace MQTTnet.Channel
|
|||||||
public interface IMqttChannel : IDisposable
|
public interface IMqttChannel : IDisposable
|
||||||
{
|
{
|
||||||
string Endpoint { get; }
|
string Endpoint { get; }
|
||||||
|
|
||||||
bool IsSecureConnection { get; }
|
bool IsSecureConnection { get; }
|
||||||
|
|
||||||
X509Certificate2 ClientCertificate { get; }
|
X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
Task ConnectAsync(CancellationToken cancellationToken);
|
Task ConnectAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task DisconnectAsync(CancellationToken cancellationToken);
|
Task DisconnectAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||||
Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
|
||||||
|
Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,32 +7,32 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public interface IMqttClient : IDisposable
|
public interface IMqttClient : IDisposable
|
||||||
{
|
{
|
||||||
event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync;
|
|
||||||
|
|
||||||
event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync;
|
|
||||||
|
|
||||||
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
|
||||||
|
|
||||||
event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync;
|
event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync;
|
||||||
|
|
||||||
|
event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync;
|
||||||
|
|
||||||
|
event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync;
|
||||||
|
|
||||||
|
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
||||||
|
|
||||||
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
||||||
|
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
|
||||||
MqttClientOptions Options { get; }
|
MqttClientOptions Options { get; }
|
||||||
|
|
||||||
Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default);
|
Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken = default);
|
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task PingAsync(CancellationToken cancellationToken = default);
|
Task PingAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default);
|
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+430
-400
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,26 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public static class MqttClientExtensions
|
public static class MqttClientExtensions
|
||||||
{
|
{
|
||||||
|
public static async Task<bool> TryPingAsync(this IMqttClient client, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.PingAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectReason reason = MqttClientDisconnectReason.NormalDisconnection, string reasonString = null)
|
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectReason reason = MqttClientDisconnectReason.NormalDisconnection, string reasonString = null)
|
||||||
{
|
{
|
||||||
if (client == null)
|
if (client == null)
|
||||||
|
|||||||
@@ -13,80 +13,83 @@ namespace MQTTnet.Client
|
|||||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
readonly Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> _acknowledgeHandler;
|
readonly Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> _acknowledgeHandler;
|
||||||
|
|
||||||
int _isAcknowledged;
|
int _isAcknowledged;
|
||||||
|
|
||||||
public MqttApplicationMessageReceivedEventArgs(
|
public MqttApplicationMessageReceivedEventArgs(
|
||||||
string clientId,
|
string clientId,
|
||||||
MqttApplicationMessage applicationMessage,
|
MqttApplicationMessage applicationMessage,
|
||||||
MqttPublishPacket publishPacket,
|
MqttPublishPacket publishPacket,
|
||||||
Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> acknowledgeHandler)
|
Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> acknowledgeHandler)
|
||||||
{
|
{
|
||||||
ClientId = clientId;
|
ClientId = clientId;
|
||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
PublishPacket = publishPacket;
|
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
||||||
PacketIdentifier = publishPacket.PacketIdentifier;
|
|
||||||
_acknowledgeHandler = acknowledgeHandler;
|
_acknowledgeHandler = acknowledgeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal MqttPublishPacket PublishPacket { get; set; }
|
public MqttApplicationMessage ApplicationMessage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the client identifier.
|
/// Gets or sets whether the library should send MQTT ACK packets automatically if required.
|
||||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
/// </summary>
|
||||||
|
public bool AutoAcknowledge { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the client identifier.
|
||||||
|
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ClientId { get; }
|
public string ClientId { get; }
|
||||||
|
|
||||||
public MqttApplicationMessage ApplicationMessage { get; }
|
|
||||||
|
|
||||||
public bool ProcessingFailed { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the reason code which will be sent to the server.
|
/// Gets or sets whether this message was handled.
|
||||||
/// </summary>
|
/// This value can be used in user code for custom control flow.
|
||||||
public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the user properties which will be sent to the server in the ACK packet etc.
|
|
||||||
/// </summary>
|
|
||||||
public List<MqttUserProperty> ResponseUserProperties { get; } = new List<MqttUserProperty>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the reason string which will be sent to the server in the ACK packet.
|
|
||||||
/// </summary>
|
|
||||||
public string ResponseReasonString { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether this message was handled.
|
|
||||||
/// This value can be used in user code for custom control flow.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsHandled { get; set; }
|
public bool IsHandled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether the library should send MQTT ACK packets automatically if required.
|
/// Gets the identifier of the MQTT packet
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoAcknowledge { get; set; } = true;
|
public ushort PacketIdentifier => PublishPacket.PacketIdentifier;
|
||||||
|
|
||||||
public object Tag { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the indentifier of MQTT packet
|
/// Indicates if the processing of this PUBLISH packet has failed.
|
||||||
|
/// If the processing has failed the client will not send an ACK packet etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort PacketIdentifier { get; }
|
public bool ProcessingFailed { get; set; }
|
||||||
|
|
||||||
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
/// <summary>
|
||||||
|
/// Gets or sets the reason code which will be sent to the server.
|
||||||
|
/// </summary>
|
||||||
|
public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the reason string which will be sent to the server in the ACK packet.
|
||||||
|
/// </summary>
|
||||||
|
public string ResponseReasonString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the user properties which will be sent to the server in the ACK packet etc.
|
||||||
|
/// </summary>
|
||||||
|
public List<MqttUserProperty> ResponseUserProperties { get; } = new List<MqttUserProperty>();
|
||||||
|
|
||||||
|
public object Tag { get; set; }
|
||||||
|
|
||||||
|
internal MqttPublishPacket PublishPacket { get; set; }
|
||||||
|
|
||||||
|
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_acknowledgeHandler == null)
|
if (_acknowledgeHandler == null)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("Deferred acknowledgement of application message is not yet supported in MQTTnet server.");
|
throw new NotSupportedException("Deferred acknowledgement of application message is not yet supported in MQTTnet server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Interlocked.CompareExchange(ref _isAcknowledged, 1, 0) == 0)
|
if (Interlocked.CompareExchange(ref _isAcknowledged, 1, 0) == 0)
|
||||||
{
|
{
|
||||||
return _acknowledgeHandler(this, cancellationToken);
|
return _acknowledgeHandler(this, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("The application message is already acknowledged.");
|
throw new InvalidOperationException("The application message is already acknowledged.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,13 +6,13 @@ using System;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Formatter
|
namespace MQTTnet.Formatter
|
||||||
{
|
{
|
||||||
public sealed class MqttBufferReader
|
public sealed class MqttBufferReader
|
||||||
{
|
{
|
||||||
byte[] _buffer = PlatformAbstractionLayer.EmptyByteArray;
|
byte[] _buffer = EmptyBuffer.Array;
|
||||||
int _initialPosition;
|
int _initialPosition;
|
||||||
int _length;
|
int _length;
|
||||||
int _position;
|
int _position;
|
||||||
@@ -29,7 +29,7 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
return PlatformAbstractionLayer.EmptyByteArray;
|
return EmptyBuffer.Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateReceiveBuffer(length);
|
ValidateReceiveBuffer(length);
|
||||||
@@ -66,7 +66,7 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
if (bufferLength == 0)
|
if (bufferLength == 0)
|
||||||
{
|
{
|
||||||
return PlatformAbstractionLayer.EmptyByteArray;
|
return EmptyBuffer.Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = new byte[bufferLength];
|
var buffer = new byte[bufferLength];
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Formatter
|
namespace MQTTnet.Formatter
|
||||||
{
|
{
|
||||||
public readonly struct MqttPacketBuffer
|
public readonly struct MqttPacketBuffer
|
||||||
{
|
{
|
||||||
static readonly ArraySegment<byte> EmptyPayload = PlatformAbstractionLayer.EmptyByteArraySegment;
|
static readonly ArraySegment<byte> EmptyPayload = EmptyBuffer.ArraySegment;
|
||||||
|
|
||||||
public MqttPacketBuffer(ArraySegment<byte> packet, ArraySegment<byte> payload)
|
public MqttPacketBuffer(ArraySegment<byte> packet, ArraySegment<byte> payload)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,14 +15,20 @@ namespace MQTTnet.Implementations
|
|||||||
public sealed class CrossPlatformSocket : IDisposable
|
public sealed class CrossPlatformSocket : IDisposable
|
||||||
{
|
{
|
||||||
readonly Socket _socket;
|
readonly Socket _socket;
|
||||||
|
|
||||||
|
#if !NET5_0_OR_GREATER
|
||||||
readonly Action _socketDisposeAction;
|
readonly Action _socketDisposeAction;
|
||||||
|
#endif
|
||||||
|
|
||||||
NetworkStream _networkStream;
|
NetworkStream _networkStream;
|
||||||
|
|
||||||
public CrossPlatformSocket(AddressFamily addressFamily)
|
public CrossPlatformSocket(AddressFamily addressFamily)
|
||||||
{
|
{
|
||||||
_socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
|
_socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
||||||
|
#if !NET5_0_OR_GREATER
|
||||||
_socketDisposeAction = _socket.Dispose;
|
_socketDisposeAction = _socket.Dispose;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public CrossPlatformSocket()
|
public CrossPlatformSocket()
|
||||||
@@ -31,17 +37,76 @@ namespace MQTTnet.Implementations
|
|||||||
// will make use of dual mode in the .net framework.
|
// will make use of dual mode in the .net framework.
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
||||||
|
#if !NET5_0_OR_GREATER
|
||||||
_socketDisposeAction = _socket.Dispose;
|
_socketDisposeAction = _socket.Dispose;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public CrossPlatformSocket(Socket socket)
|
CrossPlatformSocket(Socket socket)
|
||||||
{
|
{
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||||
_networkStream = new NetworkStream(socket, true);
|
_networkStream = new NetworkStream(socket, true);
|
||||||
|
|
||||||
|
#if !NET5_0_OR_GREATER
|
||||||
_socketDisposeAction = _socket.Dispose;
|
_socketDisposeAction = _socket.Dispose;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DualMode
|
||||||
|
{
|
||||||
|
get => _socket.DualMode;
|
||||||
|
set => _socket.DualMode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsConnected => _socket.Connected;
|
||||||
|
|
||||||
|
public bool KeepAlive
|
||||||
|
{
|
||||||
|
get => _socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive) as int? == 1;
|
||||||
|
set => _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TcpKeepAliveInterval
|
||||||
|
{
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
get => _socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval) as int? ?? 0;
|
||||||
|
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, value);
|
||||||
|
#else
|
||||||
|
get { throw new NotSupportedException("TcpKeepAliveInterval requires at least net5.0."); }
|
||||||
|
set { throw new NotSupportedException("TcpKeepAliveInterval requires at least net5.0."); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TcpKeepAliveRetryCount
|
||||||
|
{
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
get => _socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount) as int? ?? 0;
|
||||||
|
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, value);
|
||||||
|
#else
|
||||||
|
get { throw new NotSupportedException("TcpKeepAliveRetryCount requires at least net5.0."); }
|
||||||
|
set { throw new NotSupportedException("TcpKeepAliveRetryCount requires at least net5.0."); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TcpKeepAliveTime
|
||||||
|
{
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
get => _socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime) as int? ?? 0;
|
||||||
|
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, value);
|
||||||
|
#else
|
||||||
|
get { throw new NotSupportedException("TcpKeepAliveTime requires at least net5.0."); }
|
||||||
|
set { throw new NotSupportedException("TcpKeepAliveTime requires at least net5.0."); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public LingerOption LingerState
|
||||||
|
{
|
||||||
|
get => _socket.LingerState;
|
||||||
|
set => _socket.LingerState = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint => _socket.LocalEndPoint;
|
||||||
|
|
||||||
public bool NoDelay
|
public bool NoDelay
|
||||||
{
|
{
|
||||||
// We cannot use the _NoDelay_ property from the socket because there is an issue in .NET 4.5.2, 4.6.
|
// We cannot use the _NoDelay_ property from the socket because there is an issue in .NET 4.5.2, 4.6.
|
||||||
@@ -51,24 +116,20 @@ namespace MQTTnet.Implementations
|
|||||||
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
|
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LingerOption LingerState
|
|
||||||
{
|
|
||||||
get => _socket.LingerState;
|
|
||||||
set => _socket.LingerState = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DualMode
|
|
||||||
{
|
|
||||||
get => _socket.DualMode;
|
|
||||||
set => _socket.DualMode = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ReceiveBufferSize
|
public int ReceiveBufferSize
|
||||||
{
|
{
|
||||||
get => _socket.ReceiveBufferSize;
|
get => _socket.ReceiveBufferSize;
|
||||||
set => _socket.ReceiveBufferSize = value;
|
set => _socket.ReceiveBufferSize = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EndPoint RemoteEndPoint => _socket.RemoteEndPoint;
|
||||||
|
|
||||||
|
public bool ReuseAddress
|
||||||
|
{
|
||||||
|
get => _socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) as int? != 0;
|
||||||
|
set => _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
public int SendBufferSize
|
public int SendBufferSize
|
||||||
{
|
{
|
||||||
get => _socket.SendBufferSize;
|
get => _socket.SendBufferSize;
|
||||||
@@ -81,16 +142,6 @@ namespace MQTTnet.Implementations
|
|||||||
set => _socket.SendTimeout = value;
|
set => _socket.SendTimeout = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndPoint RemoteEndPoint => _socket.RemoteEndPoint;
|
|
||||||
|
|
||||||
public bool ReuseAddress
|
|
||||||
{
|
|
||||||
get => (int)_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 0;
|
|
||||||
set => _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsConnected => _socket.Connected;
|
|
||||||
|
|
||||||
public async Task<CrossPlatformSocket> AcceptAsync()
|
public async Task<CrossPlatformSocket> AcceptAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -109,8 +160,6 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndPoint LocalEndPoint => _socket.LocalEndPoint;
|
|
||||||
|
|
||||||
public void Bind(EndPoint localEndPoint)
|
public void Bind(EndPoint localEndPoint)
|
||||||
{
|
{
|
||||||
if (localEndPoint is null)
|
if (localEndPoint is null)
|
||||||
@@ -121,11 +170,6 @@ namespace MQTTnet.Implementations
|
|||||||
_socket.Bind(localEndPoint);
|
_socket.Bind(localEndPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Listen(int connectionBacklog)
|
|
||||||
{
|
|
||||||
_socket.Listen(connectionBacklog);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
|
public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (host is null)
|
if (host is null)
|
||||||
@@ -137,11 +181,17 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_networkStream?.Dispose();
|
|
||||||
|
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
|
|
||||||
|
if (_networkStream != null)
|
||||||
|
{
|
||||||
|
await _networkStream.DisposeAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
await _socket.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false);
|
await _socket.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
|
_networkStream?.Dispose();
|
||||||
|
|
||||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||||
using (cancellationToken.Register(_socketDisposeAction))
|
using (cancellationToken.Register(_socketDisposeAction))
|
||||||
{
|
{
|
||||||
@@ -175,20 +225,26 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
_networkStream?.Dispose();
|
||||||
|
_socket?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkStream GetStream()
|
||||||
|
{
|
||||||
|
var networkStream = _networkStream;
|
||||||
|
if (networkStream == null)
|
||||||
{
|
{
|
||||||
#if NET452 || NET461
|
throw new IOException("The socket is not connected.");
|
||||||
await Task.Factory.FromAsync(SocketWrapper.BeginSend, _socket.EndSend, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
|
|
||||||
#else
|
|
||||||
await _socket.SendAsync(buffer, socketFlags).ConfigureAwait(false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return networkStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Listen(int connectionBacklog)
|
||||||
|
{
|
||||||
|
_socket.Listen(connectionBacklog);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> ReceiveAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
public async Task<int> ReceiveAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||||
@@ -208,28 +264,27 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkStream GetStream()
|
public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||||
{
|
{
|
||||||
var networkStream = _networkStream;
|
try
|
||||||
if (networkStream == null)
|
|
||||||
{
|
{
|
||||||
throw new IOException("The socket is not connected.");
|
#if NET452 || NET461
|
||||||
|
await Task.Factory.FromAsync(SocketWrapper.BeginSend, _socket.EndSend, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
|
await _socket.SendAsync(buffer, socketFlags).ConfigureAwait(false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
|
||||||
}
|
}
|
||||||
|
|
||||||
return networkStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_networkStream?.Dispose();
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NET452 || NET461
|
#if NET452 || NET461
|
||||||
sealed class SocketWrapper
|
sealed class SocketWrapper
|
||||||
{
|
{
|
||||||
readonly Socket _socket;
|
|
||||||
readonly ArraySegment<byte> _buffer;
|
readonly ArraySegment<byte> _buffer;
|
||||||
|
readonly Socket _socket;
|
||||||
readonly SocketFlags _socketFlags;
|
readonly SocketFlags _socketFlags;
|
||||||
|
|
||||||
public SocketWrapper(Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags)
|
public SocketWrapper(Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||||
@@ -239,16 +294,28 @@ namespace MQTTnet.Implementations
|
|||||||
_socketFlags = socketFlags;
|
_socketFlags = socketFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IAsyncResult BeginSend(AsyncCallback callback, object state)
|
|
||||||
{
|
|
||||||
var socketWrapper = (SocketWrapper)state;
|
|
||||||
return socketWrapper._socket.BeginSend(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncResult BeginReceive(AsyncCallback callback, object state)
|
public static IAsyncResult BeginReceive(AsyncCallback callback, object state)
|
||||||
{
|
{
|
||||||
var socketWrapper = (SocketWrapper)state;
|
var socketWrapper = (SocketWrapper)state;
|
||||||
return socketWrapper._socket.BeginReceive(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
|
return socketWrapper._socket.BeginReceive(
|
||||||
|
socketWrapper._buffer.Array,
|
||||||
|
socketWrapper._buffer.Offset,
|
||||||
|
socketWrapper._buffer.Count,
|
||||||
|
socketWrapper._socketFlags,
|
||||||
|
callback,
|
||||||
|
state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IAsyncResult BeginSend(AsyncCallback callback, object state)
|
||||||
|
{
|
||||||
|
var socketWrapper = (SocketWrapper)state;
|
||||||
|
return socketWrapper._socket.BeginSend(
|
||||||
|
socketWrapper._buffer.Array,
|
||||||
|
socketWrapper._buffer.Offset,
|
||||||
|
socketWrapper._buffer.Count,
|
||||||
|
socketWrapper._socketFlags,
|
||||||
|
callback,
|
||||||
|
state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -108,12 +108,12 @@ namespace MQTTnet.Implementations
|
|||||||
return _readStream.ReadAsync(buffer, offset, count, cancellationToken);
|
return _readStream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// In the write method only the internal buffer will be filled. So here is no
|
// In the write method only the internal buffer will be filled. So here is no
|
||||||
// async/await required. The real network transmit is done when calling the
|
// async/await required. The real network transmit is done when calling the
|
||||||
// Flush method.
|
// Flush method.
|
||||||
_writeStream.Write(buffer, offset, count);
|
_writeStream.Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
return _writeStream.FlushAsync(cancellationToken);
|
return _writeStream.FlushAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.Runtime.ExceptionServices;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
@@ -154,7 +155,7 @@ namespace MQTTnet.Implementations
|
|||||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -225,7 +226,7 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public async Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -239,12 +240,12 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
||||||
await stream.WriteAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
|
await stream.WriteAsync(buffer.AsMemory(), cancellationToken).ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||||
using (cancellationToken.Register(_disposeAction))
|
using (cancellationToken.Register(_disposeAction))
|
||||||
{
|
{
|
||||||
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System.Net.Sockets;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
@@ -56,13 +57,13 @@ namespace MQTTnet.Implementations
|
|||||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
Cleanup();
|
Cleanup();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -124,7 +125,7 @@ namespace MQTTnet.Implementations
|
|||||||
var clientHandler = ClientHandler;
|
var clientHandler = ClientHandler;
|
||||||
if (clientHandler == null)
|
if (clientHandler == null)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return clientHandler(channelAdapter);
|
return clientHandler(channelAdapter);
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Implementations
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).RunInBackground(_logger);
|
_ = Task.Factory.StartNew(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
@@ -35,12 +35,12 @@ namespace MQTTnet.Implementations
|
|||||||
ClientCertificate = clientCertificate;
|
ClientCertificate = clientCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
public string Endpoint { get; }
|
public string Endpoint { get; }
|
||||||
|
|
||||||
public bool IsSecureConnection { get; private set; }
|
public bool IsSecureConnection { get; private set; }
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var uri = _options.Uri;
|
var uri = _options.Uri;
|
||||||
@@ -89,14 +89,26 @@ namespace MQTTnet.Implementations
|
|||||||
Cleanup();
|
Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
|
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
|
||||||
return response.Count;
|
return response.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public async Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
// MQTT Control Packets MUST be sent in WebSocket binary data frames. If any other type of data frame is received the recipient MUST close the Network Connection [MQTT-6.0.0-1].
|
||||||
|
// A single WebSocket data frame can contain multiple or partial MQTT Control Packets. The receiver MUST NOT assume that MQTT Control Packets are aligned on WebSocket frame boundaries [MQTT-6.0.0-2].
|
||||||
|
await _webSocket.SendAsync(buffer, WebSocketMessageType.Binary, isEndOfPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
// The lock is required because the client will throw an exception if _SendAsync_ is
|
// The lock is required because the client will throw an exception if _SendAsync_ is
|
||||||
// called from multiple threads at the same time. But this issue only happens with several
|
// called from multiple threads at the same time. But this issue only happens with several
|
||||||
// framework versions.
|
// framework versions.
|
||||||
@@ -105,88 +117,11 @@ namespace MQTTnet.Implementations
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
using (await _sendLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken).ConfigureAwait(false);
|
await _webSocket.SendAsync(buffer, WebSocketMessageType.Binary, isEndOfPacket, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetupClientWebSocket(ClientWebSocket clientWebSocket)
|
|
||||||
{
|
|
||||||
if (_options.ProxyOptions != null)
|
|
||||||
{
|
|
||||||
clientWebSocket.Options.Proxy = CreateProxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.RequestHeaders != null)
|
|
||||||
{
|
|
||||||
foreach (var requestHeader in _options.RequestHeaders)
|
|
||||||
{
|
|
||||||
clientWebSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.SubProtocols != null)
|
|
||||||
{
|
|
||||||
foreach (var subProtocol in _options.SubProtocols)
|
|
||||||
{
|
|
||||||
clientWebSocket.Options.AddSubProtocol(subProtocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.CookieContainer != null)
|
|
||||||
{
|
|
||||||
clientWebSocket.Options.Cookies = _options.CookieContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
|
|
||||||
{
|
|
||||||
clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
|
|
||||||
foreach (var certificate in _options.TlsOptions.Certificates)
|
|
||||||
{
|
|
||||||
#if WINDOWS_UWP
|
|
||||||
clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
|
|
||||||
#else
|
|
||||||
clientWebSocket.Options.ClientCertificates.Add(certificate);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
|
|
||||||
if (certificateValidationHandler != null)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD1_3
|
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard1.3'.");
|
|
||||||
#elif NETSTANDARD2_0
|
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard2.0'.");
|
|
||||||
#elif WINDOWS_UWP
|
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'uap10.0'.");
|
|
||||||
#elif NET452
|
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
|
|
||||||
#elif NET461
|
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
|
|
||||||
#else
|
|
||||||
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
|
||||||
{
|
|
||||||
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
|
|
||||||
var context = new MqttClientCertificateValidationEventArgs
|
|
||||||
{
|
|
||||||
Certificate = certificate,
|
|
||||||
Chain = chain,
|
|
||||||
SslPolicyErrors = sslPolicyErrors,
|
|
||||||
ClientOptions = _options
|
|
||||||
};
|
|
||||||
|
|
||||||
return certificateValidationHandler(context);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cleanup()
|
void Cleanup()
|
||||||
@@ -230,5 +165,77 @@ namespace MQTTnet.Implementations
|
|||||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupClientWebSocket(ClientWebSocket clientWebSocket)
|
||||||
|
{
|
||||||
|
if (_options.ProxyOptions != null)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.Proxy = CreateProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.RequestHeaders != null)
|
||||||
|
{
|
||||||
|
foreach (var requestHeader in _options.RequestHeaders)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.SubProtocols != null)
|
||||||
|
{
|
||||||
|
foreach (var subProtocol in _options.SubProtocols)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.AddSubProtocol(subProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.CookieContainer != null)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.Cookies = _options.CookieContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
|
||||||
|
foreach (var certificate in _options.TlsOptions.Certificates)
|
||||||
|
{
|
||||||
|
#if WINDOWS_UWP
|
||||||
|
clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
|
||||||
|
#else
|
||||||
|
clientWebSocket.Options.ClientCertificates.Add(certificate);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
|
||||||
|
if (certificateValidationHandler != null)
|
||||||
|
{
|
||||||
|
#if NETSTANDARD1_3
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard1.3'.");
|
||||||
|
#elif NETSTANDARD2_0
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard2.0'.");
|
||||||
|
#elif WINDOWS_UWP
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'uap10.0'.");
|
||||||
|
#elif NET452
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
|
||||||
|
#elif NET461
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
|
||||||
|
#else
|
||||||
|
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||||
|
{
|
||||||
|
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
|
||||||
|
var context = new MqttClientCertificateValidationEventArgs
|
||||||
|
{
|
||||||
|
Certificate = certificate,
|
||||||
|
Chain = chain,
|
||||||
|
SslPolicyErrors = sslPolicyErrors,
|
||||||
|
ClientOptions = _options
|
||||||
|
};
|
||||||
|
|
||||||
|
return certificateValidationHandler(context);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public static class PlatformAbstractionLayer
|
|
||||||
{
|
|
||||||
#if NET452
|
|
||||||
public static Task CompletedTask => Task.FromResult(0);
|
|
||||||
|
|
||||||
public static byte[] EmptyByteArray { get; } = new byte[0];
|
|
||||||
#else
|
|
||||||
public static Task CompletedTask => Task.CompletedTask;
|
|
||||||
|
|
||||||
public static byte[] EmptyByteArray { get; } = Array.Empty<byte>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public static ArraySegment<byte> EmptyByteArraySegment { get; } = new ArraySegment<byte>(EmptyByteArray);
|
|
||||||
|
|
||||||
public static void Sleep(TimeSpan timeout)
|
|
||||||
{
|
|
||||||
#if !NETSTANDARD1_3 && !WINDOWS_UWP
|
|
||||||
try
|
|
||||||
{
|
|
||||||
System.Threading.Thread.Sleep(timeout);
|
|
||||||
}
|
|
||||||
catch (ThreadAbortException)
|
|
||||||
{
|
|
||||||
// The ThreadAbortException is not actively catched in this project.
|
|
||||||
// So we use a one which is similar and will be catched properly.
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Task.Delay(timeout).Wait();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,16 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
readonly List<AsyncEventInvocator<TEventArgs>> _handlers = new List<AsyncEventInvocator<TEventArgs>>();
|
readonly List<AsyncEventInvocator<TEventArgs>> _handlers = new List<AsyncEventInvocator<TEventArgs>>();
|
||||||
|
|
||||||
public bool HasHandlers => _handlers.Count > 0;
|
ICollection<AsyncEventInvocator<TEventArgs>> _handlersForInvoke;
|
||||||
|
|
||||||
|
public AsyncEvent()
|
||||||
|
{
|
||||||
|
_handlersForInvoke = _handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track the existence of handlers in a separate field so that checking it all the time will not
|
||||||
|
// require locking the actual list (_handlers).
|
||||||
|
public bool HasHandlers { get; private set; }
|
||||||
|
|
||||||
public void AddHandler(Func<TEventArgs, Task> handler)
|
public void AddHandler(Func<TEventArgs, Task> handler)
|
||||||
{
|
{
|
||||||
@@ -22,7 +31,13 @@ namespace MQTTnet.Internal
|
|||||||
throw new ArgumentNullException(nameof(handler));
|
throw new ArgumentNullException(nameof(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(null, handler));
|
lock (_handlers)
|
||||||
|
{
|
||||||
|
_handlers.Add(new AsyncEventInvocator<TEventArgs>(null, handler));
|
||||||
|
|
||||||
|
HasHandlers = true;
|
||||||
|
_handlersForInvoke = new List<AsyncEventInvocator<TEventArgs>>(_handlers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddHandler(Action<TEventArgs> handler)
|
public void AddHandler(Action<TEventArgs> handler)
|
||||||
@@ -32,17 +47,32 @@ namespace MQTTnet.Internal
|
|||||||
throw new ArgumentNullException(nameof(handler));
|
throw new ArgumentNullException(nameof(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(handler, null));
|
lock (_handlers)
|
||||||
|
{
|
||||||
|
_handlers.Add(new AsyncEventInvocator<TEventArgs>(handler, null));
|
||||||
|
|
||||||
|
HasHandlers = true;
|
||||||
|
_handlersForInvoke = new List<AsyncEventInvocator<TEventArgs>>(_handlers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InvokeAsync(TEventArgs eventArgs)
|
public async Task InvokeAsync(TEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
foreach (var handler in _handlers)
|
if (!HasHandlers)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding or removing handlers will produce a new list instance all the time.
|
||||||
|
// So locking here is not required since only the reference to an immutable list
|
||||||
|
// of handlers is used.
|
||||||
|
var handlers = _handlersForInvoke;
|
||||||
|
foreach (var handler in handlers)
|
||||||
{
|
{
|
||||||
await handler.InvokeAsync(eventArgs).ConfigureAwait(false);
|
await handler.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveHandler(Func<TEventArgs, Task> handler)
|
public void RemoveHandler(Func<TEventArgs, Task> handler)
|
||||||
{
|
{
|
||||||
if (handler == null)
|
if (handler == null)
|
||||||
@@ -50,7 +80,13 @@ namespace MQTTnet.Internal
|
|||||||
throw new ArgumentNullException(nameof(handler));
|
throw new ArgumentNullException(nameof(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
lock (_handlers)
|
||||||
|
{
|
||||||
|
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||||
|
|
||||||
|
HasHandlers = _handlers.Count > 0;
|
||||||
|
_handlersForInvoke = new List<AsyncEventInvocator<TEventArgs>>(_handlers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveHandler(Action<TEventArgs> handler)
|
public void RemoveHandler(Action<TEventArgs> handler)
|
||||||
@@ -60,7 +96,13 @@ namespace MQTTnet.Internal
|
|||||||
throw new ArgumentNullException(nameof(handler));
|
throw new ArgumentNullException(nameof(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
lock (_handlers)
|
||||||
|
{
|
||||||
|
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||||
|
|
||||||
|
HasHandlers = _handlers.Count > 0;
|
||||||
|
_handlersForInvoke = new List<AsyncEventInvocator<TEventArgs>>(_handlers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TryInvokeAsync(TEventArgs eventArgs, MqttNetSourceLogger logger)
|
public async Task TryInvokeAsync(TEventArgs eventArgs, MqttNetSourceLogger logger)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Internal
|
|||||||
if (_handler != null)
|
if (_handler != null)
|
||||||
{
|
{
|
||||||
_handler.Invoke(eventArgs);
|
_handler.Invoke(eventArgs);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_asyncHandler != null)
|
if (_asyncHandler != null)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
// 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 System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -10,77 +12,152 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
public sealed class AsyncLock : IDisposable
|
public sealed class AsyncLock : IDisposable
|
||||||
{
|
{
|
||||||
|
readonly Task<IDisposable> _completedTask;
|
||||||
|
readonly IDisposable _releaser;
|
||||||
readonly object _syncRoot = new object();
|
readonly object _syncRoot = new object();
|
||||||
readonly Task<IDisposable> _releaser;
|
readonly Queue<AsyncLockWaiter> _waiters = new Queue<AsyncLockWaiter>(64);
|
||||||
|
|
||||||
SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
volatile bool _isDisposed;
|
||||||
|
bool _isLocked;
|
||||||
|
|
||||||
public AsyncLock()
|
public AsyncLock()
|
||||||
{
|
{
|
||||||
_releaser = Task.FromResult((IDisposable)new Releaser(this));
|
_releaser = new Releaser(this);
|
||||||
}
|
_completedTask = Task.FromResult(_releaser);
|
||||||
|
|
||||||
public Task<IDisposable> WaitAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Task task;
|
|
||||||
|
|
||||||
// This lock is required to avoid ObjectDisposedExceptions.
|
|
||||||
// These are fired when this lock gets disposed (and thus the semaphore)
|
|
||||||
// and a worker thread tries to call this method at the same time.
|
|
||||||
// Another way would be catching all ObjectDisposedExceptions but this situation happens
|
|
||||||
// quite often when clients are disconnecting.
|
|
||||||
lock (_syncRoot)
|
|
||||||
{
|
|
||||||
task = _semaphore?.WaitAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task == null)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException("The AsyncLock is disposed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.Status == TaskStatus.RanToCompletion)
|
|
||||||
{
|
|
||||||
return _releaser;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the _WaitAsync_ method and return the releaser afterwards.
|
|
||||||
return task.ContinueWith(
|
|
||||||
(_, state) => (IDisposable)state,
|
|
||||||
_releaser.Result,
|
|
||||||
cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
lock (_syncRoot)
|
lock (_syncRoot)
|
||||||
{
|
{
|
||||||
_semaphore?.Dispose();
|
_isDisposed = true;
|
||||||
_semaphore = null;
|
|
||||||
|
while (_waiters.Any())
|
||||||
|
{
|
||||||
|
_waiters.Dequeue().Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Release()
|
public Task<IDisposable> EnterAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(AsyncLock));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
if (!_isLocked)
|
||||||
|
{
|
||||||
|
_isLocked = true;
|
||||||
|
return _completedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
var waiter = new AsyncLockWaiter(cancellationToken);
|
||||||
|
_waiters.Enqueue(waiter);
|
||||||
|
|
||||||
|
return waiter.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release()
|
||||||
{
|
{
|
||||||
lock (_syncRoot)
|
lock (_syncRoot)
|
||||||
{
|
{
|
||||||
_semaphore?.Release();
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
// All waiters have been canceled with a ObjectDisposedException.
|
||||||
|
// So there is nothing left to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume that there is no waiter left first.
|
||||||
|
_isLocked = false;
|
||||||
|
|
||||||
|
// Try to find the next waiter which can be approved.
|
||||||
|
// Some of them might be canceled already so it is not
|
||||||
|
// guaranteed that the very next waiter is the correct one.
|
||||||
|
while (_waiters.Any())
|
||||||
|
{
|
||||||
|
var waiter = _waiters.Dequeue();
|
||||||
|
var isApproved = waiter.Approve(_releaser);
|
||||||
|
waiter.Dispose();
|
||||||
|
|
||||||
|
if (isApproved)
|
||||||
|
{
|
||||||
|
_isLocked = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Releaser : IDisposable
|
sealed class AsyncLockWaiter : IDisposable
|
||||||
{
|
{
|
||||||
readonly AsyncLock _lock;
|
readonly CancellationTokenRegistration _cancellationRegistration;
|
||||||
|
readonly bool _hasCancellationRegistration;
|
||||||
|
readonly AsyncTaskCompletionSource<IDisposable> _promise = new AsyncTaskCompletionSource<IDisposable>();
|
||||||
|
|
||||||
internal Releaser(AsyncLock @lock)
|
public AsyncLockWaiter(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_lock = @lock;
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
_cancellationRegistration = cancellationToken.Register(Cancel);
|
||||||
|
_hasCancellationRegistration = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IDisposable> Task => _promise.Task;
|
||||||
|
|
||||||
|
public bool Approve(IDisposable scope)
|
||||||
|
{
|
||||||
|
if (scope == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_promise.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _promise.TrySetResult(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_lock.Release();
|
if (_hasCancellationRegistration)
|
||||||
|
{
|
||||||
|
_cancellationRegistration.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_promise.TrySetException(new ObjectDisposedException(nameof(AsyncLockWaiter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cancel()
|
||||||
|
{
|
||||||
|
_promise.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly struct Releaser : IDisposable
|
||||||
|
{
|
||||||
|
readonly AsyncLock _asyncLock;
|
||||||
|
|
||||||
|
public Releaser(AsyncLock asyncLock)
|
||||||
|
{
|
||||||
|
_asyncLock = asyncLock ?? throw new ArgumentNullException(nameof(asyncLock));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_asyncLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
// 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.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MQTTnet.Internal
|
namespace MQTTnet.Internal
|
||||||
@@ -22,7 +23,7 @@ namespace MQTTnet.Internal
|
|||||||
|
|
||||||
public Task<TResult> Task => _taskCompletionSource.Task;
|
public Task<TResult> Task => _taskCompletionSource.Task;
|
||||||
|
|
||||||
public bool TrySetCanceled()
|
public void TrySetCanceled()
|
||||||
{
|
{
|
||||||
#if NET452
|
#if NET452
|
||||||
// To prevent deadlocks it is required to call the _TrySetCanceled_ method
|
// To prevent deadlocks it is required to call the _TrySetCanceled_ method
|
||||||
@@ -31,19 +32,14 @@ namespace MQTTnet.Internal
|
|||||||
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
||||||
// NET452 does not support RunContinuationsAsynchronously
|
// NET452 does not support RunContinuationsAsynchronously
|
||||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetCanceled());
|
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetCanceled());
|
||||||
return true;
|
SpinWait.SpinUntil(() => _taskCompletionSource.Task.IsCompleted);
|
||||||
#else
|
#else
|
||||||
return _taskCompletionSource.TrySetCanceled();
|
_taskCompletionSource.TrySetCanceled();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TrySetException(Exception exception)
|
public void TrySetException(Exception exception)
|
||||||
{
|
{
|
||||||
if (exception == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(exception));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NET452
|
#if NET452
|
||||||
// To prevent deadlocks it is required to call the _TrySetException_ method
|
// To prevent deadlocks it is required to call the _TrySetException_ method
|
||||||
// from a new thread because the awaiting code will not(!) be executed in
|
// from a new thread because the awaiting code will not(!) be executed in
|
||||||
@@ -51,9 +47,9 @@ namespace MQTTnet.Internal
|
|||||||
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
||||||
// NET452 does not support RunContinuationsAsynchronously
|
// NET452 does not support RunContinuationsAsynchronously
|
||||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetException(exception));
|
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetException(exception));
|
||||||
return true;
|
SpinWait.SpinUntil(() => _taskCompletionSource.Task.IsCompleted);
|
||||||
#else
|
#else
|
||||||
return _taskCompletionSource.TrySetException(exception);
|
_taskCompletionSource.TrySetException(exception);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +61,14 @@ namespace MQTTnet.Internal
|
|||||||
// a new thread automatically (due to await). Furthermore _this_ thread will
|
// a new thread automatically (due to await). Furthermore _this_ thread will
|
||||||
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
// do it. But _this_ thread is also reading incoming packets -> deadlock.
|
||||||
// NET452 does not support RunContinuationsAsynchronously
|
// NET452 does not support RunContinuationsAsynchronously
|
||||||
|
if (_taskCompletionSource.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetResult(result));
|
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetResult(result));
|
||||||
|
SpinWait.SpinUntil(() => _taskCompletionSource.Task.IsCompleted);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return _taskCompletionSource.TrySetResult(result);
|
return _taskCompletionSource.TrySetResult(result);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// 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.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Internal
|
||||||
|
{
|
||||||
|
public static class CompletedTask
|
||||||
|
{
|
||||||
|
#if NET452
|
||||||
|
public static readonly Task Instance = Task.FromResult(true);
|
||||||
|
#else
|
||||||
|
public static readonly Task Instance = Task.CompletedTask;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 System;
|
||||||
|
|
||||||
|
namespace MQTTnet.Internal
|
||||||
|
{
|
||||||
|
public static class EmptyBuffer
|
||||||
|
{
|
||||||
|
#if NET452
|
||||||
|
public static readonly byte[] Array = new byte[0];
|
||||||
|
#else
|
||||||
|
public static readonly byte[] Array = System.Array.Empty<byte>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static readonly ArraySegment<byte> ArraySegment = new ArraySegment<byte>(Array, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
<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>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
|
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
<RootNamespace>MQTTnet</RootNamespace>
|
<RootNamespace>MQTTnet</RootNamespace>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Text;
|
|||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ namespace MQTTnet.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MaximumPacketSize => _connectPacket.MaximumPacketSize;
|
public uint MaximumPacketSize => _connectPacket.MaximumPacketSize;
|
||||||
|
|
||||||
public string Password => Encoding.UTF8.GetString(RawPassword ?? PlatformAbstractionLayer.EmptyByteArray);
|
public string Password => Encoding.UTF8.GetString(RawPassword ?? EmptyBuffer.Array);
|
||||||
|
|
||||||
public MqttProtocolVersion ProtocolVersion => ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
|
public MqttProtocolVersion ProtocolVersion => ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Task.Run(() => SendPacketsLoop(cancellationToken), cancellationToken).RunInBackground(_logger);
|
_ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
|
||||||
|
|
||||||
IsRunning = true;
|
IsRunning = true;
|
||||||
|
|
||||||
@@ -147,13 +147,20 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
|
||||||
if (reason == MqttDisconnectReasonCode.SessionTakenOver || reason == MqttDisconnectReasonCode.KeepAliveTimeout)
|
// Sending DISCONNECT packets from the server to the client is only supported when using MQTTv5+.
|
||||||
|
if (ChannelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
|
||||||
{
|
{
|
||||||
// Is is very important to send the DISCONNECT packet here BEFORE cancelling the
|
// The Client or Server MAY send a DISCONNECT packet before closing the Network Connection.
|
||||||
// token because the entire connection is closed (disposed) as soon as the cancellation
|
// This library does not sent a DISCONNECT packet for a normal disconnection. Maybe adding
|
||||||
// token is cancelled. To there is no chance that the DISCONNECT packet will ever arrive
|
// a configuration option is requested in the future.
|
||||||
// at the client!
|
if (reason != MqttDisconnectReasonCode.NormalDisconnection)
|
||||||
await TrySendDisconnectPacket(reason).ConfigureAwait(false);
|
{
|
||||||
|
// Is is very important to send the DISCONNECT packet here BEFORE cancelling the
|
||||||
|
// token because the entire connection is closed (disposed) as soon as the cancellation
|
||||||
|
// token is cancelled. To there is no chance that the DISCONNECT packet will ever arrive
|
||||||
|
// at the client!
|
||||||
|
await TrySendDisconnectPacket(reason).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StopInternal();
|
StopInternal();
|
||||||
@@ -174,7 +181,7 @@ namespace MQTTnet.Server
|
|||||||
return _eventContainer.ClientAcknowledgedPublishPacketEvent.TryInvokeAsync(eventArgs, _logger);
|
return _eventContainer.ClientAcknowledgedPublishPacketEvent.TryInvokeAsync(eventArgs, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task HandleIncomingPubAckPacket(MqttPubAckPacket pubAckPacket)
|
Task HandleIncomingPubAckPacket(MqttPubAckPacket pubAckPacket)
|
||||||
@@ -186,7 +193,7 @@ namespace MQTTnet.Server
|
|||||||
return ClientAcknowledgedPublishPacket(acknowledgedPublishPacket, pubAckPacket);
|
return ClientAcknowledgedPublishPacket(acknowledgedPublishPacket, pubAckPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task HandleIncomingPubCompPacket(MqttPubCompPacket pubCompPacket)
|
Task HandleIncomingPubCompPacket(MqttPubCompPacket pubCompPacket)
|
||||||
@@ -198,7 +205,7 @@ namespace MQTTnet.Server
|
|||||||
return ClientAcknowledgedPublishPacket(acknowledgedPublishPacket, pubCompPacket);
|
return ClientAcknowledgedPublishPacket(acknowledgedPublishPacket, pubCompPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task HandleIncomingPublishPacket(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
async Task HandleIncomingPublishPacket(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||||
@@ -384,7 +391,20 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for cancellation again because receive packet might block some time.
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TCP connection of this client may be still open but the client has already been taken over by
|
||||||
|
// a new TCP connection. So we must exit here to make sure to no longer process any message.
|
||||||
|
if (IsTakenOver || !IsRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var processPacket = true;
|
var processPacket = true;
|
||||||
|
|
||||||
if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers)
|
if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers)
|
||||||
@@ -486,6 +506,11 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsTakenOver || !IsRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SendPacketAsync(packetBusItem.Packet, cancellationToken).ConfigureAwait(false);
|
await SendPacketAsync(packetBusItem.Packet, cancellationToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace MQTTnet.Server
|
|||||||
readonly Dictionary<string, MqttClient> _clients = new Dictionary<string, MqttClient>(4096);
|
readonly Dictionary<string, MqttClient> _clients = new Dictionary<string, MqttClient>(4096);
|
||||||
|
|
||||||
readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
|
readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
|
||||||
|
|
||||||
readonly MqttServerEventContainer _eventContainer;
|
readonly MqttServerEventContainer _eventContainer;
|
||||||
readonly MqttNetSourceLogger _logger;
|
readonly MqttNetSourceLogger _logger;
|
||||||
readonly MqttServerOptions _options;
|
readonly MqttServerOptions _options;
|
||||||
@@ -75,6 +76,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public async Task DeleteSessionAsync(string clientId)
|
public async Task DeleteSessionAsync(string clientId)
|
||||||
{
|
{
|
||||||
|
_logger.Verbose("Deleting session for client '{0}'.", clientId);
|
||||||
|
|
||||||
MqttClient connection;
|
MqttClient connection;
|
||||||
|
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
@@ -193,7 +196,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_createConnectionSyncRoot?.Dispose();
|
_createConnectionSyncRoot.Dispose();
|
||||||
|
|
||||||
lock (_sessionsManagementLock)
|
lock (_sessionsManagementLock)
|
||||||
{
|
{
|
||||||
@@ -303,6 +306,9 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
await client.RunAsync().ConfigureAwait(false);
|
await client.RunAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -438,13 +444,18 @@ namespace MQTTnet.Server
|
|||||||
return GetClientSession(clientId).SubscriptionsManager.Unsubscribe(fakeUnsubscribePacket, CancellationToken.None);
|
return GetClientSession(clientId).SubscriptionsManager.Unsubscribe(fakeUnsubscribePacket, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MqttClient CreateClient(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session)
|
||||||
|
{
|
||||||
|
return new MqttClient(connectPacket, channelAdapter, session, _options, _eventContainer, this, _rootLogger);
|
||||||
|
}
|
||||||
|
|
||||||
async Task<MqttClient> CreateClientConnection(
|
async Task<MqttClient> CreateClientConnection(
|
||||||
MqttConnectPacket connectPacket,
|
MqttConnectPacket connectPacket,
|
||||||
MqttConnAckPacket connAckPacket,
|
MqttConnAckPacket connAckPacket,
|
||||||
IMqttChannelAdapter channelAdapter,
|
IMqttChannelAdapter channelAdapter,
|
||||||
ValidatingConnectionEventArgs validatingConnectionEventArgs)
|
ValidatingConnectionEventArgs validatingConnectionEventArgs)
|
||||||
{
|
{
|
||||||
MqttClient connection;
|
MqttClient client;
|
||||||
|
|
||||||
bool sessionShouldPersist;
|
bool sessionShouldPersist;
|
||||||
|
|
||||||
@@ -471,7 +482,7 @@ namespace MQTTnet.Server
|
|||||||
sessionShouldPersist = !connectPacket.CleanSession;
|
sessionShouldPersist = !connectPacket.CleanSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _createConnectionSyncRoot.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
MqttSession session;
|
MqttSession session;
|
||||||
lock (_sessionsManagementLock)
|
lock (_sessionsManagementLock)
|
||||||
@@ -484,7 +495,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
if (connectPacket.CleanSession)
|
if (connectPacket.CleanSession)
|
||||||
{
|
{
|
||||||
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.Verbose("Deleting existing session of client '{0}' due to clean start.", connectPacket.ClientId);
|
||||||
session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist);
|
session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -507,40 +518,40 @@ namespace MQTTnet.Server
|
|||||||
await _eventContainer.PreparingSessionEvent.InvokeAsync(preparingSessionEventArgs).ConfigureAwait(false);
|
await _eventContainer.PreparingSessionEvent.InvokeAsync(preparingSessionEventArgs).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClient existing;
|
MqttClient existingClient;
|
||||||
|
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
{
|
{
|
||||||
_clients.TryGetValue(connectPacket.ClientId, out existing);
|
_clients.TryGetValue(connectPacket.ClientId, out existingClient);
|
||||||
connection = CreateConnection(connectPacket, channelAdapter, session);
|
client = CreateClient(connectPacket, channelAdapter, session);
|
||||||
|
|
||||||
_clients[connectPacket.ClientId] = connection;
|
_clients[connectPacket.ClientId] = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existing != null)
|
if (existingClient != null)
|
||||||
{
|
{
|
||||||
existing.IsTakenOver = true;
|
existingClient.IsTakenOver = true;
|
||||||
await existing.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false);
|
await existingClient.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false);
|
||||||
|
|
||||||
if (_eventContainer.ClientConnectedEvent.HasHandlers)
|
if (_eventContainer.ClientConnectedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
var eventArgs = new ClientDisconnectedEventArgs(existing.Id, MqttClientDisconnectType.Takeover, existing.Endpoint, existing.Session.Items);
|
var eventArgs = new ClientDisconnectedEventArgs(
|
||||||
|
existingClient.Id,
|
||||||
|
MqttClientDisconnectType.Takeover,
|
||||||
|
existingClient.Endpoint,
|
||||||
|
existingClient.Session.Items);
|
||||||
|
|
||||||
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return connection;
|
return client;
|
||||||
}
|
|
||||||
|
|
||||||
MqttClient CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session)
|
|
||||||
{
|
|
||||||
return new MqttClient(connectPacket, channelAdapter, session, _options, _eventContainer, this, _rootLogger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttSession CreateSession(string clientId, IDictionary sessionItems, bool isPersistent)
|
MqttSession CreateSession(string clientId, IDictionary sessionItems, bool isPersistent)
|
||||||
{
|
{
|
||||||
_logger.Verbose("Created a new session for client '{0}'.", clientId);
|
_logger.Verbose("Created new session for client '{0}'.", clientId);
|
||||||
|
|
||||||
return new MqttSession(clientId, isPersistent, sessionItems, _options, _eventContainer, _retainedMessagesManager, this);
|
return new MqttSession(clientId, isPersistent, sessionItems, _options, _eventContainer, _retainedMessagesManager, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
@@ -85,7 +83,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? PlatformAbstractionLayer.EmptyByteArray))
|
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? EmptyBuffer.Array))
|
||||||
{
|
{
|
||||||
_messages[applicationMessage.Topic] = applicationMessage;
|
_messages[applicationMessage.Topic] = applicationMessage;
|
||||||
saveIsRequired = true;
|
saveIsRequired = true;
|
||||||
@@ -103,7 +101,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
if (saveIsRequired)
|
if (saveIsRequired)
|
||||||
{
|
{
|
||||||
using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _storageAccessLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var eventArgs = new RetainedMessageChangedEventArgs(clientId, applicationMessage, messagesForSave);
|
var eventArgs = new RetainedMessageChangedEventArgs(clientId, applicationMessage, messagesForSave);
|
||||||
await _eventContainer.RetainedMessageChangedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
await _eventContainer.RetainedMessageChangedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
@@ -132,7 +130,7 @@ namespace MQTTnet.Server
|
|||||||
_messages.Clear();
|
_messages.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _storageAccessLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
|
await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -14,16 +13,20 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
public sealed class MqttServerKeepAliveMonitor
|
public sealed class MqttServerKeepAliveMonitor
|
||||||
{
|
{
|
||||||
|
readonly MqttNetSourceLogger _logger;
|
||||||
readonly MqttServerOptions _options;
|
readonly MqttServerOptions _options;
|
||||||
readonly MqttClientSessionsManager _sessionsManager;
|
readonly MqttClientSessionsManager _sessionsManager;
|
||||||
readonly MqttNetSourceLogger _logger;
|
|
||||||
|
|
||||||
public MqttServerKeepAliveMonitor(MqttServerOptions options, MqttClientSessionsManager sessionsManager, IMqttNetLogger logger)
|
public MqttServerKeepAliveMonitor(MqttServerOptions options, MqttClientSessionsManager sessionsManager, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
||||||
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
_logger = logger.WithSource(nameof(MqttServerKeepAliveMonitor));
|
_logger = logger.WithSource(nameof(MqttServerKeepAliveMonitor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +48,7 @@ namespace MQTTnet.Server
|
|||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
TryProcessClients();
|
TryProcessClients();
|
||||||
PlatformAbstractionLayer.Sleep(_options.KeepAliveMonitorInterval);
|
Sleep(_options.KeepAliveMonitorInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -61,13 +64,22 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TryProcessClients()
|
static void Sleep(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
#if !NETSTANDARD1_3 && !WINDOWS_UWP
|
||||||
foreach (var client in _sessionsManager.GetClients())
|
try
|
||||||
{
|
{
|
||||||
TryProcessClient(client, now);
|
Thread.Sleep(timeout);
|
||||||
}
|
}
|
||||||
|
catch (ThreadAbortException)
|
||||||
|
{
|
||||||
|
// The ThreadAbortException is not actively catched in this project.
|
||||||
|
// So we use a one which is similar and will be catched properly.
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Task.Delay(timeout).Wait();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TryProcessClient(MqttClient connection, DateTime now)
|
void TryProcessClient(MqttClient connection, DateTime now)
|
||||||
@@ -119,5 +131,14 @@ namespace MQTTnet.Server
|
|||||||
_logger.Error(exception, "Client {0}: Unhandled exception while checking keep alive timeouts.", connection.Id);
|
_logger.Error(exception, "Client {0}: Unhandled exception while checking keep alive timeouts.", connection.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TryProcessClients()
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
foreach (var client in _sessionsManager.GetClients())
|
||||||
|
{
|
||||||
|
TryProcessClient(client, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
@@ -172,7 +171,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
ThrowIfNotStarted();
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _retainedMessagesManager?.ClearMessages() ?? PlatformAbstractionLayer.CompletedTask;
|
return _retainedMessagesManager?.ClearMessages() ?? CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectClientAsync(string id, MqttDisconnectReasonCode reasonCode)
|
public Task DisconnectClientAsync(string id, MqttDisconnectReasonCode reasonCode)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
@@ -34,8 +33,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
var publishPacketFactory = new MqttPublishPacketFactory();
|
var publishPacketFactory = new MqttPublishPacketFactory();
|
||||||
_session.EnqueueDataPacket(new MqttPacketBusItem(publishPacketFactory.Create(applicationMessage)));
|
_session.EnqueueDataPacket(new MqttPacketBusItem(publishPacketFactory.Create(applicationMessage)));
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
public Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||||
|
|||||||
Reference in New Issue
Block a user