mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 17:00:03 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0c5f20c5a |
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
VERSION: "4.1.3.${{github.run_number}}"
|
||||
VERSION: "4.1.0.${{github.run_number}}"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -210,7 +210,6 @@
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Access Order="Public Internal Protected Private" />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
@@ -241,7 +240,5 @@ 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_002EMigrateThisQualifierSettings/@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/=PUBCOMP/@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>
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
# MQTTnet
|
||||
|
||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework version and CPU architecture.
|
||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -50,6 +50,39 @@ 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
|
||||
* 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
|
||||
|
||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Security.Authentication;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Extensions.WebSocket4Net;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Samples.Helpers;
|
||||
using MQTTnet.Samples.Shared;
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
@@ -119,7 +119,7 @@ public static class Client_Connection_Samples
|
||||
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||
// certificates. This should not be used in live environments.
|
||||
o.CertificateValidationHandler = _ => true;
|
||||
|
||||
|
||||
// The default value is determined by the OS. Set manually to force version.
|
||||
o.SslProtocol = SslProtocols.Tls12;
|
||||
})
|
||||
@@ -134,17 +134,15 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Connect_Client_Using_WebSocket4Net()
|
||||
public static async Task Connect_Client_Using_WebSockets()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory().UseWebSocket4Net();
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
@@ -157,16 +155,18 @@ public static class Client_Connection_Samples
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
var mqttFactory = new MqttFactory().UseWebSocket4Net();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
@@ -206,7 +206,7 @@ public static class Client_Connection_Samples
|
||||
using (var timeout = new CancellationTokenSource(5000))
|
||||
{
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
response.DumpToConsole();
|
||||
@@ -214,49 +214,6 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Disconnect_Clean()
|
||||
{
|
||||
/*
|
||||
* This sample disconnects from the server with sending a DISCONNECT packet.
|
||||
* This way of disconnecting is treated as a clean disconnect which will not
|
||||
* trigger sending the last will etc.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
|
||||
// Using a reason code requires MQTT version 5.0.0!
|
||||
await mqttClient.DisconnectAsync(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()
|
||||
{
|
||||
/*
|
||||
@@ -291,7 +248,7 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async Task Ping_Server()
|
||||
{
|
||||
/*
|
||||
@@ -364,12 +321,14 @@ public static class Client_Connection_Samples
|
||||
{
|
||||
try
|
||||
{
|
||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
|
||||
if (!await mqttClient.TryPingAsync())
|
||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required
|
||||
// in the first place.
|
||||
if (!mqttClient.IsConnected)
|
||||
{
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Subscribe to topics when session is clean etc.
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,53 +40,6 @@ public static class Client_Publish_Samples
|
||||
|
||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
|
||||
await mqttClient.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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Samples.Helpers;
|
||||
using MQTTnet.Samples.Shared;
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
@@ -25,7 +23,9 @@ public static class Client_Subscribe_Samples
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
// Setup message handling before connecting so that queued messages
|
||||
// are also handled properly. When there is no event handler attached all
|
||||
@@ -41,11 +41,7 @@ public static class Client_Subscribe_Samples
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/2");
|
||||
})
|
||||
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
|
||||
.Build();
|
||||
|
||||
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
@@ -57,95 +53,6 @@ public static class Client_Subscribe_Samples
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Send_Responses()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to a topic and sends a response to the broker. This requires at least QoS level 1 to work!
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
|
||||
{
|
||||
// Do some work with the message...
|
||||
|
||||
// Now respond to the broker with a reason code other than success.
|
||||
args.ReasonCode = MqttApplicationMessageReceivedReasonCode.ImplementationSpecificError;
|
||||
args.ResponseReasonString = "That did not work!";
|
||||
|
||||
// User properties require MQTT v5!
|
||||
args.ResponseUserProperties.Add(new MqttUserProperty("My", "Data"));
|
||||
|
||||
// Now the broker will resend the message again.
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/1");
|
||||
})
|
||||
.Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topic.");
|
||||
|
||||
// The response contains additional data sent by the server after subscribing.
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Subscribe_Multiple_Topics()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to several topics in a single request.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Create the subscribe options including several topics with different options.
|
||||
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/1");
|
||||
})
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/2").WithNoLocal();
|
||||
})
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/3").WithRetainHandling(MqttRetainHandling.SendAtSubscribe);
|
||||
})
|
||||
.Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topics.");
|
||||
|
||||
// The response contains additional data sent by the server after subscribing.
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Subscribe_Topic()
|
||||
{
|
||||
/*
|
||||
@@ -156,16 +63,14 @@ public static class Client_Subscribe_Samples
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic("mqttnet/samples/topic/1");
|
||||
})
|
||||
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/1"); })
|
||||
.Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
using System.Text;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Samples.Shared;
|
||||
|
||||
namespace MQTTnet.Samples.Diagnostics;
|
||||
|
||||
@@ -22,9 +23,7 @@ public static class Logger_Samples
|
||||
|
||||
var mqttFactory = new MqttFactory(new MyLogger());
|
||||
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
@@ -32,13 +31,31 @@ public static class Logger_Samples
|
||||
|
||||
Console.WriteLine("MQTT client is connected.");
|
||||
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
||||
.Build();
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Log_To_Console()
|
||||
{
|
||||
/*
|
||||
* This sample covers the creation of a custom logger which is used to log to the console.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory(new ConsoleLogger());
|
||||
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client is connected. Press any key to exit.");
|
||||
Console.ReadKey(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Use_Event_Logger()
|
||||
{
|
||||
/*
|
||||
@@ -64,9 +81,7 @@ public static class Logger_Samples
|
||||
|
||||
var mqttFactory = new MqttFactory(mqttEventLogger);
|
||||
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
@@ -74,8 +89,7 @@ public static class Logger_Samples
|
||||
|
||||
Console.WriteLine("MQTT client is connected.");
|
||||
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
||||
.Build();
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
@@ -83,7 +97,7 @@ public static class Logger_Samples
|
||||
|
||||
sealed class MyLogger : IMqttNetLogger
|
||||
{
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
public bool IsEnabled { get; } = true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ Console.WriteLine("Welcome to MQTTnet samples!");
|
||||
Console.WriteLine();
|
||||
|
||||
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
||||
sampleClasses.RemoveAll(c => c.Namespace.Contains(".Shared"));
|
||||
|
||||
var index = 0;
|
||||
foreach (var sampleClass in sampleClasses)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable EmptyConstructor
|
||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_Intercepting_Samples
|
||||
{
|
||||
public static async Task Intercept_Application_Messages()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which manipulate all processed application messages.
|
||||
* Please see _Server_Simple_Samples_ for more details on how to start a server.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
mqttServer.InterceptingPublishAsync += args =>
|
||||
{
|
||||
// Here we only change the topic of the received application message.
|
||||
// but also changing the payload etc. is required. Changing the QoS after
|
||||
// transmitting is not supported and makes no sense at all.
|
||||
args.ApplicationMessage.Topic += "/manipulated";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Samples.Shared;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
@@ -170,50 +170,4 @@ public static class Server_Simple_Samples
|
||||
await server.StartAsync();
|
||||
return server;
|
||||
}
|
||||
|
||||
class ConsoleLogger : IMqttNetLogger
|
||||
{
|
||||
readonly object _consoleSyncRoot = new();
|
||||
|
||||
public bool IsEnabled => true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
||||
{
|
||||
var foregroundColor = ConsoleColor.White;
|
||||
switch (logLevel)
|
||||
{
|
||||
case MqttNetLogLevel.Verbose:
|
||||
foregroundColor = ConsoleColor.White;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Info:
|
||||
foregroundColor = ConsoleColor.Green;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Warning:
|
||||
foregroundColor = ConsoleColor.DarkYellow;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Error:
|
||||
foregroundColor = ConsoleColor.Red;
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
message = string.Format(message, parameters);
|
||||
}
|
||||
|
||||
lock (_consoleSyncRoot)
|
||||
{
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.WriteLine(message);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Samples.Shared;
|
||||
|
||||
public sealed class ConsoleLogger : IMqttNetLogger
|
||||
{
|
||||
readonly object _consoleSyncRoot = new();
|
||||
|
||||
public bool IsEnabled => true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
||||
{
|
||||
var foregroundColor = ConsoleColor.White;
|
||||
switch (logLevel)
|
||||
{
|
||||
case MqttNetLogLevel.Verbose:
|
||||
foregroundColor = ConsoleColor.White;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Info:
|
||||
foregroundColor = ConsoleColor.Green;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Warning:
|
||||
foregroundColor = ConsoleColor.DarkYellow;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Error:
|
||||
foregroundColor = ConsoleColor.Red;
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
message = string.Format(message, parameters);
|
||||
}
|
||||
|
||||
lock (_consoleSyncRoot)
|
||||
{
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.WriteLine(message);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MQTTnet.Samples.Helpers;
|
||||
namespace MQTTnet.Samples.Shared;
|
||||
|
||||
internal static class ObjectExtensions
|
||||
{
|
||||
@@ -0,0 +1,26 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Pipelines;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
public class ConnectionContextMockup : ConnectionContext
|
||||
{
|
||||
public override string ConnectionId { get; set; }
|
||||
|
||||
public override IFeatureCollection Features { get; }
|
||||
|
||||
public override IDictionary<object, object> Items { get; set; }
|
||||
public override IDuplexPipe Transport { get; set; }
|
||||
|
||||
public ConnectionContextMockup()
|
||||
{
|
||||
//Transport = new DefaultConnectionContext
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,20 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
public sealed class ConnectionHandlerMockup : IMqttServerAdapter
|
||||
public class ConnectionHandlerMockup: IMqttServerAdapter
|
||||
{
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new TaskCompletionSource<MqttConnectionContext>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
@@ -46,5 +42,9 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.IO.Pipelines;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
public sealed class DuplexPipeMockup : IDuplexPipe
|
||||
public class DuplexPipeMockup : IDuplexPipe
|
||||
{
|
||||
public DuplexPipeMockup()
|
||||
{
|
||||
@@ -20,8 +20,9 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
|
||||
PipeWriter IDuplexPipe.Output => Send.Writer;
|
||||
|
||||
public Pipe Receive { get; }
|
||||
|
||||
public Pipe Send { get; }
|
||||
public Pipe Receive { get; set; }
|
||||
|
||||
public Pipe Send { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Buffers;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
public sealed class LimitedMemoryPool : MemoryPool<byte>
|
||||
public class LimitedMemoryPool : MemoryPool<byte>
|
||||
{
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
@@ -7,9 +7,9 @@ using System.Buffers;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
{
|
||||
public sealed class MemoryOwner : IMemoryOwner<byte>
|
||||
public class MemoryOwner : IMemoryOwner<byte>
|
||||
{
|
||||
readonly byte[] _raw;
|
||||
private readonly byte[] _raw;
|
||||
|
||||
public MemoryOwner(int size)
|
||||
{
|
||||
@@ -27,11 +27,11 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||
Memory = _raw;
|
||||
}
|
||||
|
||||
public Memory<byte> Memory { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_raw);
|
||||
}
|
||||
|
||||
public Memory<byte> Memory { get; }
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,22 @@
|
||||
// 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.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Tests.Mockups;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Extensions;
|
||||
|
||||
@@ -25,6 +26,20 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
[TestClass]
|
||||
public class MqttConnectionContextTest
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
var pipe = new DuplexPipeMockup();
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
pipe.Receive.Writer.Complete();
|
||||
|
||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestCorruptedConnectPacket()
|
||||
{
|
||||
@@ -34,43 +49,13 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
|
||||
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0]));
|
||||
|
||||
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
|
||||
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
||||
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestEndpoint()
|
||||
{
|
||||
var mockup = new ConnectionHandlerMockup();
|
||||
|
||||
|
||||
using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
||||
.UseStartup<Startup>()
|
||||
.ConfigureServices(
|
||||
(hostContext, services) =>
|
||||
{
|
||||
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
||||
services.AddSingleton<IMqttServerAdapter>(mockup);
|
||||
})
|
||||
.Build())
|
||||
|
||||
using (var client = new MqttFactory().CreateMqttClient())
|
||||
{
|
||||
host.Start();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
|
||||
|
||||
var ctx = await mockup.Context.Task;
|
||||
#if NETCOREAPP3_1
|
||||
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
||||
Assert.IsNotNull(ep);
|
||||
#endif
|
||||
Assert.IsNotNull(ctx);
|
||||
}
|
||||
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
||||
@@ -93,7 +78,7 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
|
||||
// await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestLargePacket()
|
||||
{
|
||||
@@ -109,25 +94,43 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
var pipe = new DuplexPipeMockup();
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
pipe.Receive.Writer.Complete();
|
||||
|
||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
class Startup
|
||||
private class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestEndpoint()
|
||||
{
|
||||
var mockup = new ConnectionHandlerMockup();
|
||||
|
||||
|
||||
using (var host = new WebHostBuilder()
|
||||
.UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
||||
.UseStartup<Startup>()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
||||
services.AddSingleton<IMqttServerAdapter>(mockup);
|
||||
})
|
||||
.Build())
|
||||
|
||||
using (var client = new MqttFactory().CreateMqttClient())
|
||||
{
|
||||
host.Start();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.Build(), CancellationToken.None);
|
||||
|
||||
var ctx = await mockup.Context.Task;
|
||||
#if NETCOREAPP3_1
|
||||
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
||||
Assert.IsNotNull(ep);
|
||||
#endif
|
||||
Assert.IsNotNull(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using MQTTnet.Packets;
|
||||
namespace MQTTnet.AspNetCore.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class ReaderExtensionsTest
|
||||
public class ReaderExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestTryDeserialize()
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
var formatter = PacketFormatterAdapter;
|
||||
|
||||
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var buffer = formatter.Encode(packet);
|
||||
var msg = buffer.Join().AsMemory();
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class AsyncLockBenchmark : BaseBenchmark
|
||||
{
|
||||
[Benchmark]
|
||||
public async Task Synchronize_100_Tasks()
|
||||
{
|
||||
const int tasksCount = 100;
|
||||
|
||||
var tasks = new Task[tasksCount];
|
||||
var asyncLock = new AsyncLock();
|
||||
var globalI = 0;
|
||||
|
||||
for (var i = 0; i < tasksCount; i++)
|
||||
{
|
||||
tasks[i] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (await asyncLock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
var localI = globalI;
|
||||
await Task.Delay(5).ConfigureAwait(false); // Increase the chance for wrong data.
|
||||
localI++;
|
||||
globalI = localI;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
if (globalI != tasksCount)
|
||||
{
|
||||
throw new Exception($"Code is broken ({globalI})!");
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task Wait_100_000_Times()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
|
||||
using (var cancellationToken = new CancellationTokenSource())
|
||||
{
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0'">
|
||||
<PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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 BenchmarkDotNet.Attributes;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Implementations;
|
||||
@@ -71,8 +70,8 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
while (read < expected)
|
||||
{
|
||||
var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||
read += readResult;
|
||||
var readresult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||
read += readresult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +79,9 @@ namespace MQTTnet.Benchmarks
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var buffer = new ArraySegment<byte>(new byte[size]);
|
||||
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
|
||||
await _serverChannel.WriteAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace MQTTnet.Benchmarks
|
||||
Console.WriteLine("c = SubscribeBenchmark");
|
||||
Console.WriteLine("d = UnsubscribeBenchmark");
|
||||
Console.WriteLine("e = MessageDeliveryBenchmark");
|
||||
Console.WriteLine("f = AsyncLockBenchmark");
|
||||
|
||||
var pressedKey = Console.ReadKey(true);
|
||||
switch (pressedKey.KeyChar)
|
||||
@@ -76,9 +75,6 @@ namespace MQTTnet.Benchmarks
|
||||
case 'e':
|
||||
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
||||
break;
|
||||
case 'f':
|
||||
BenchmarkRunner.Run<AsyncLockBenchmark>();
|
||||
break;
|
||||
}
|
||||
|
||||
Console.ReadLine();
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace MQTTnet.Benchmarks
|
||||
return Task.FromResult(count);
|
||||
}
|
||||
|
||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
Task StartAsync(ManagedMqttClientOptions options);
|
||||
|
||||
Task StopAsync(bool cleanDisconnect = true);
|
||||
Task StopAsync();
|
||||
|
||||
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
CancellationTokenSource _publishingCancellationToken;
|
||||
|
||||
ManagedMqttClientStorageManager _storageManager;
|
||||
bool _isCleanDisconnect;
|
||||
|
||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
@@ -161,14 +160,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
throw new InvalidOperationException("call StartAsync before publishing messages");
|
||||
}
|
||||
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage);
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
||||
|
||||
ManagedMqttApplicationMessage removedMessage = null;
|
||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false))
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||
{
|
||||
@@ -256,12 +255,10 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_logger.Info("Started");
|
||||
}
|
||||
|
||||
public async Task StopAsync(bool cleanDisconnect = true)
|
||||
public async Task StopAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_isCleanDisconnect = cleanDisconnect;
|
||||
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
@@ -299,7 +296,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task UnsubscribeAsync(ICollection<string> topics)
|
||||
@@ -322,7 +319,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -385,12 +382,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isCleanDisconnect)
|
||||
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
||||
{
|
||||
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
||||
{
|
||||
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -699,7 +693,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||
|
||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
// While publishing this message, this.PublishAsync could have booted this
|
||||
// message off the queue to make room for another (when using a cap
|
||||
@@ -730,7 +724,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
//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
|
||||
//with prior behavior in that way.
|
||||
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
_messages.Add(applicationMessage);
|
||||
await SaveAsync().ConfigureAwait(false);
|
||||
@@ -45,7 +45,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
var index = _messages.IndexOf(applicationMessage);
|
||||
if (index == -1)
|
||||
|
||||
@@ -9,7 +9,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
@@ -116,7 +116,7 @@ namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
#if NET452
|
||||
@@ -128,7 +128,7 @@ namespace MQTTnet.Extensions.Rpc
|
||||
// Set this message to handled to that other code can avoid execution etc.
|
||||
eventArgs.IsHandled = true;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using SuperSocket.ClientEngine;
|
||||
using WebSocket4Net;
|
||||
|
||||
@@ -125,7 +124,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
_webSocket.Close();
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
@@ -155,10 +154,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
|
||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Send(buffer.Array, buffer.Offset, buffer.Count);
|
||||
return CompletedTask.Instance;
|
||||
_webSocket.Send(buffer, offset, count);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
{
|
||||
public sealed class AsyncLockTest
|
||||
{
|
||||
public async Task Run()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
|
||||
using (var cancellationToken = new CancellationTokenSource())
|
||||
{
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
@@ -40,7 +40,7 @@ namespace MQTTnet.TestApp
|
||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||
Console.WriteLine();
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
client.ConnectedAsync += async e =>
|
||||
|
||||
@@ -9,7 +9,7 @@ using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
@@ -42,7 +42,7 @@ namespace MQTTnet.TestApp
|
||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await managedClient.StartAsync(options);
|
||||
@@ -85,7 +85,7 @@ namespace MQTTnet.TestApp
|
||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||
{
|
||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
{
|
||||
@@ -125,7 +124,7 @@ namespace MQTTnet.TestApp
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
return CompletedTask.Instance;
|
||||
return Implementations.PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Cleanup()
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace MQTTnet.TestApp
|
||||
Console.WriteLine("c = Start QoS 0 benchmark");
|
||||
Console.WriteLine("d = Start server with logging");
|
||||
Console.WriteLine("e = Start Message Throughput Test");
|
||||
Console.WriteLine("f = Start AsyncLock Test");
|
||||
|
||||
var pressedKey = Console.ReadKey(true);
|
||||
if (pressedKey.KeyChar == '1')
|
||||
@@ -89,10 +88,6 @@ namespace MQTTnet.TestApp
|
||||
{
|
||||
Task.Run(new MessageThroughputTest().Run);
|
||||
}
|
||||
else if (pressedKey.KeyChar == 'f')
|
||||
{
|
||||
Task.Run(new AsyncLockTest().Run);
|
||||
}
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
@@ -144,7 +144,7 @@ namespace MQTTnet.TestApp
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(options);
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using Newtonsoft.Json;
|
||||
@@ -67,13 +69,13 @@ namespace MQTTnet.TestApp
|
||||
}
|
||||
|
||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
|
||||
mqttServer.RetainedMessagesClearedAsync += e =>
|
||||
{
|
||||
File.Delete(Filename);
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
|
||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||
@@ -91,7 +93,7 @@ namespace MQTTnet.TestApp
|
||||
|
||||
e.LoadedRetainedMessages = retainedMessages;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
@@ -109,7 +111,7 @@ namespace MQTTnet.TestApp
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
mqttServer.ValidatingConnectionAsync += e =>
|
||||
@@ -122,7 +124,7 @@ namespace MQTTnet.TestApp
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||
@@ -138,7 +140,7 @@ namespace MQTTnet.TestApp
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
@@ -147,7 +149,7 @@ namespace MQTTnet.TestApp
|
||||
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
||||
ConsoleColor.Magenta);
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
//options.ApplicationMessageInterceptor = c =>
|
||||
@@ -175,7 +177,7 @@ namespace MQTTnet.TestApp
|
||||
mqttServer.ClientConnectedAsync += e =>
|
||||
{
|
||||
Console.Write("Client disconnected event fired.");
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace MQTTnet.Tests.Client
|
||||
|
||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Wrong_Host()
|
||||
@@ -80,8 +80,6 @@ namespace MQTTnet.Tests.Client
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
testEnvironment.ServerPort = 8364;
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
@@ -118,8 +116,6 @@ namespace MQTTnet.Tests.Client
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
||||
@@ -127,7 +123,7 @@ namespace MQTTnet.Tests.Client
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
|
||||
|
||||
|
||||
await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
|
||||
|
||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||
@@ -152,7 +148,7 @@ namespace MQTTnet.Tests.Client
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,76 +11,18 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
using MqttClient = MQTTnet.Client.MqttClient;
|
||||
|
||||
namespace MQTTnet.Tests.Client
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var senderClient = await testEnvironment.ConnectClient();
|
||||
|
||||
// Prepare managed client.
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||
await managedClient.SubscribeAsync("#");
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(managedClient);
|
||||
|
||||
var managedClientOptions = new ManagedMqttClientOptions
|
||||
{
|
||||
ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithClientId(nameof(Receive_While_Not_Cleanly_Disconnected) + "_managed")
|
||||
.WithCleanSession(false)
|
||||
.Build()
|
||||
};
|
||||
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
|
||||
// Send test data.
|
||||
await senderClient.PublishStringAsync("topic1");
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
|
||||
// Stop the managed client but keep session at server (not clean disconnect required).
|
||||
await managedClient.StopAsync(false);
|
||||
await LongTestDelay();
|
||||
|
||||
// Send new messages in the meantime.
|
||||
await senderClient.PublishStringAsync("topic2", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await LongTestDelay();
|
||||
|
||||
// Start the managed client, it should receive the new message.
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(2);
|
||||
|
||||
// Stop and start again, no new message should be received.
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
await managedClient.StopAsync(false);
|
||||
await LongTestDelay();
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
}
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(2);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_To_Invalid_Server()
|
||||
{
|
||||
@@ -93,10 +35,11 @@ namespace MQTTnet.Tests.Client
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
||||
|
||||
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
||||
|
||||
managedClient.ConnectingFailedAsync += args =>
|
||||
{
|
||||
connectingFailedEventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await managedClient.StartAsync(
|
||||
@@ -174,7 +117,7 @@ namespace MQTTnet.Tests.Client
|
||||
receivingClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await receivingClient.SubscribeAsync("My/last/will");
|
||||
@@ -338,7 +281,7 @@ namespace MQTTnet.Tests.Client
|
||||
});
|
||||
|
||||
// Wait a bit for the retained message to be available
|
||||
await LongTestDelay();
|
||||
await Task.Delay(1000);
|
||||
|
||||
await sendingClient.DisconnectAsync();
|
||||
|
||||
@@ -347,29 +290,28 @@ namespace MQTTnet.Tests.Client
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
||||
|
||||
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
||||
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(testEnvironment.CreateClient());
|
||||
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedManagedMessages.Add(e.ApplicationMessage);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await managedClient.SubscribeAsync("topic");
|
||||
|
||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||
|
||||
await LongTestDelay();
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||
|
||||
await managedClient.StopAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
await Task.Delay(500);
|
||||
|
||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||
|
||||
await LongTestDelay();
|
||||
await Task.Delay(1000);
|
||||
|
||||
// After reconnect and then some delay, the retained message must not be received,
|
||||
// showing that the subscriptions were cleared
|
||||
@@ -377,9 +319,7 @@ namespace MQTTnet.Tests.Client
|
||||
|
||||
// Make sure that it gets received after subscribing again.
|
||||
await managedClient.SubscribeAsync("topic");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(2, receivedManagedMessages.Count);
|
||||
}
|
||||
}
|
||||
@@ -488,7 +428,7 @@ namespace MQTTnet.Tests.Client
|
||||
managedClient.ConnectedAsync += e =>
|
||||
{
|
||||
connected.TrySetResult(true);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
return connected.Task;
|
||||
@@ -517,7 +457,7 @@ namespace MQTTnet.Tests.Client
|
||||
result.TrySetResult(receivedMessages);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
return result.Task;
|
||||
@@ -546,7 +486,7 @@ namespace MQTTnet.Tests.Client
|
||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||
{
|
||||
_messages = messages;
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,17 +14,15 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace MQTTnet.Tests.Client
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttClient_Tests : BaseTestClass
|
||||
public sealed class Client_Tests : BaseTestClass
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
@@ -58,7 +56,7 @@ namespace MQTTnet.Tests.Client
|
||||
}
|
||||
|
||||
_ = InvokeInternal();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
||||
@@ -109,7 +107,7 @@ namespace MQTTnet.Tests.Client
|
||||
client.DisconnectedAsync += args =>
|
||||
{
|
||||
disconnectHandlerCalled = true;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
||||
@@ -123,7 +121,7 @@ namespace MQTTnet.Tests.Client
|
||||
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
||||
}
|
||||
|
||||
await LongTestDelay(); // disconnected handler is called async
|
||||
await Task.Delay(100); // disconnected handler is called async
|
||||
Assert.IsTrue(disconnectHandlerCalled);
|
||||
}
|
||||
}
|
||||
@@ -138,7 +136,7 @@ namespace MQTTnet.Tests.Client
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
ex = e.Exception;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
try
|
||||
@@ -147,7 +145,6 @@ namespace MQTTnet.Tests.Client
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
await Task.Delay(500);
|
||||
@@ -170,7 +167,7 @@ namespace MQTTnet.Tests.Client
|
||||
server.InterceptingPublishAsync += c =>
|
||||
{
|
||||
i++;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.SendAsync(
|
||||
@@ -211,7 +208,7 @@ namespace MQTTnet.Tests.Client
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
handlerFired = true;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
@@ -242,10 +239,10 @@ namespace MQTTnet.Tests.Client
|
||||
|
||||
for (var i = 0; i < 98; i++)
|
||||
{
|
||||
Assert.IsFalse(clients[i].TryPingAsync().GetAwaiter().GetResult(), $"clients[{i}] is not connected");
|
||||
Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
|
||||
}
|
||||
|
||||
Assert.IsTrue(clients[99].TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(clients[99].IsConnected);
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(1, sessionStatus.Count);
|
||||
@@ -255,7 +252,7 @@ namespace MQTTnet.Tests.Client
|
||||
receiveClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await receiveClient.SubscribeAsync("x");
|
||||
@@ -313,7 +310,7 @@ namespace MQTTnet.Tests.Client
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||
@@ -335,7 +332,7 @@ namespace MQTTnet.Tests.Client
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,11 +343,11 @@ namespace MQTTnet.Tests.Client
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +453,7 @@ namespace MQTTnet.Tests.Client
|
||||
receivedValues.Add(value);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||
@@ -534,7 +531,7 @@ namespace MQTTnet.Tests.Client
|
||||
client2.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client2.SubscribeAsync(client2Topic);
|
||||
@@ -569,7 +566,7 @@ namespace MQTTnet.Tests.Client
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync("a");
|
||||
@@ -594,17 +591,17 @@ namespace MQTTnet.Tests.Client
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.IsTrue(await client.TryPingAsync());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
|
||||
await server.StopAsync();
|
||||
await Task.Delay(500);
|
||||
Assert.IsFalse(await client.TryPingAsync());
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
|
||||
await server.StartAsync();
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
Assert.IsTrue(await client.TryPingAsync());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,7 +637,6 @@ namespace MQTTnet.Tests.Client
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
||||
@@ -660,11 +656,11 @@ namespace MQTTnet.Tests.Client
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
|
||||
await server.StopAsync();
|
||||
await Task.Delay(500);
|
||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
@@ -675,7 +671,6 @@ namespace MQTTnet.Tests.Client
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +678,7 @@ namespace MQTTnet.Tests.Client
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
await client.PingAsync();
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,7 +723,7 @@ namespace MQTTnet.Tests.Client
|
||||
replies.Add(eventArgs.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += Handler2;
|
||||
@@ -766,7 +761,7 @@ namespace MQTTnet.Tests.Client
|
||||
replyReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += async e =>
|
||||
@@ -817,7 +812,7 @@ namespace MQTTnet.Tests.Client
|
||||
replyReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
@@ -833,14 +828,14 @@ namespace MQTTnet.Tests.Client
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
Assert.IsTrue(e.ClientWasConnected);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.DisconnectAsync();
|
||||
@@ -856,11 +851,11 @@ namespace MQTTnet.Tests.Client
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
Assert.IsTrue(e.ClientWasConnected);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
@@ -897,7 +892,7 @@ namespace MQTTnet.Tests.Client
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
||||
@@ -922,14 +917,14 @@ namespace MQTTnet.Tests.Client
|
||||
client1.DisconnectedAsync += c =>
|
||||
{
|
||||
disconnectedFired = true;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var messageReceived = false;
|
||||
client1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
messageReceived = true;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
||||
@@ -946,7 +941,7 @@ namespace MQTTnet.Tests.Client
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(messageReceived);
|
||||
Assert.IsTrue(client1.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsTrue(client1.IsConnected);
|
||||
Assert.IsFalse(disconnectedFired);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests.Diagnostics
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Diagnostics
|
||||
mqttClient.InspectPackage += eventArgs =>
|
||||
{
|
||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Factory
|
||||
@@ -56,7 +56,7 @@ namespace MQTTnet.Tests.Factory
|
||||
managedClient.ConnectingFailedAsync += e =>
|
||||
{
|
||||
tcs.TrySetResult(null);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
||||
|
||||
@@ -13,12 +13,13 @@ using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Extensions;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V3_Binary_Tests
|
||||
|
||||
@@ -10,7 +10,7 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V3_Tests
|
||||
|
||||
@@ -9,7 +9,7 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V5_Tests
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
@@ -15,28 +16,6 @@ namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
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]
|
||||
public async Task Attach_EventHandler()
|
||||
{
|
||||
@@ -49,19 +28,6 @@ namespace MQTTnet.Tests.Internal
|
||||
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]
|
||||
public async Task Detach_EventHandler()
|
||||
{
|
||||
@@ -75,62 +41,22 @@ namespace MQTTnet.Tests.Internal
|
||||
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)
|
||||
{
|
||||
Interlocked.Increment(ref _testEventAsyncCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
sealed class TestClass
|
||||
{
|
||||
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
|
||||
{
|
||||
add => _testEvent.AddHandler(value);
|
||||
remove => _testEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool HasTestHandlers => _testEvent.HasHandlers;
|
||||
|
||||
public Task FireEventAsync(EventArgs eventArgs)
|
||||
{
|
||||
return _testEvent.InvokeAsync(eventArgs);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -12,186 +11,8 @@ using MQTTnet.Internal;
|
||||
namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class AsyncLock_Tests
|
||||
public 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]
|
||||
public async Task Lock_Serial_Calls()
|
||||
{
|
||||
@@ -200,7 +21,7 @@ namespace MQTTnet.Tests.Internal
|
||||
var @lock = new AsyncLock();
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
using (await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
@@ -216,76 +37,66 @@ namespace MQTTnet.Tests.Internal
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will never "release" the lock due to missing _using_.
|
||||
await @lock.EnterAsync().ConfigureAwait(false);
|
||||
await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Test_Cancellation_With_Later_Access()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
//[TestMethod]
|
||||
//public async Task Test_Cancellation_With_Later_Access()
|
||||
//{
|
||||
// var @lock = new AsyncLock();
|
||||
|
||||
var releaser = await asyncLock.EnterAsync().ConfigureAwait(false);
|
||||
// var releaser = await @lock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await asyncLock.EnterAsync(timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
// try
|
||||
// {
|
||||
// using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
// {
|
||||
// await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// }
|
||||
|
||||
// releaser.Dispose();
|
||||
|
||||
Assert.Fail("Exception should be thrown!");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
releaser.Dispose();
|
||||
|
||||
using (await asyncLock.EnterAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
// When the method finished, the thread got access.
|
||||
}
|
||||
}
|
||||
// using (await @lock.WaitAsync().ConfigureAwait(false))
|
||||
// {
|
||||
// // When the method finished, the thread got access.
|
||||
// }
|
||||
//}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_After_Cancellation()
|
||||
public void Lock_10_Parallel_Tasks()
|
||||
{
|
||||
const int ThreadsCount = 10;
|
||||
|
||||
var threads = new Task[ThreadsCount];
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will not yet "release" the lock due to missing _using_.
|
||||
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
var globalI = 0;
|
||||
for (var i = 0; i < ThreadsCount; i++)
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
#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 () =>
|
||||
{
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Expected exception!
|
||||
using (var releaser = await @lock.WaitAsync(CancellationToken.None))
|
||||
{
|
||||
var localI = globalI;
|
||||
await Task.Delay(10); // 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
|
||||
}
|
||||
|
||||
releaser.Dispose();
|
||||
|
||||
// Regular usage after cancellation.
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
Task.WaitAll(threads);
|
||||
Assert.AreEqual(ThreadsCount, globalI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +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.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class AsyncSignal_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Cancel_If_No_Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||
{
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public async Task Dispose_Properly()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(999)))
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(2000, CancellationToken.None);
|
||||
asyncSignal.Dispose();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reset_Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// WaitAsync should fail because no signal is available.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)))
|
||||
{
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
}
|
||||
|
||||
Assert.Fail("This must fail because the signal is not yet set.");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
asyncSignal.Set();
|
||||
|
||||
// WaitAsync should return directly because the signal is available.
|
||||
await asyncSignal.WaitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(1000, CancellationToken.None);
|
||||
asyncSignal.Set();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 900);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public async Task Fail_For_Two_Waiters()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// This thread will wait properly because it is the first waiter.
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await asyncSignal.WaitAsync();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Now the current thread must fail because there is already a waiter.
|
||||
await asyncSignal.WaitAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBusItem_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Fire_Completed_Event()
|
||||
{
|
||||
var eventFired = false;
|
||||
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item.Completed += (_, __) =>
|
||||
{
|
||||
eventFired = true;
|
||||
};
|
||||
|
||||
item.Complete();
|
||||
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Wait_Packet_Bus_Item_After_Already_Canceled()
|
||||
{
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
|
||||
// Finish the item before the actual
|
||||
item.Cancel();
|
||||
|
||||
await item.WaitAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,131 +13,9 @@ namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBus_Tests
|
||||
{
|
||||
{
|
||||
[TestMethod]
|
||||
public void Alternate_Priorities()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
|
||||
Assert.AreEqual(9, bus.TotalItemsCount);
|
||||
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Await_Single_Packet()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
var delivered = false;
|
||||
|
||||
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
|
||||
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item3.Completed += (_, __) =>
|
||||
{
|
||||
delivered = true;
|
||||
};
|
||||
|
||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
// The third packet has the event attached.
|
||||
Assert.IsTrue(delivered);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Export_Packets_Without_Dequeue()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
|
||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||
Assert.AreEqual(3, exportedPackets.Count);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fill_From_Different_Task()
|
||||
{
|
||||
const int MessageCount = 500;
|
||||
|
||||
var delayRandom = new Random();
|
||||
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
_ = Task.Run(
|
||||
() =>
|
||||
{
|
||||
for (var i = 0; i < MessageCount; i++)
|
||||
{
|
||||
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
||||
|
||||
Thread.Sleep(delayRandom.Next(0, 10));
|
||||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < MessageCount; i++)
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public async Task Wait_With_Empty_Bus()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
@@ -147,5 +25,127 @@ namespace MQTTnet.Tests.Internal
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fill_From_Different_Task()
|
||||
{
|
||||
const int MessageCount = 500;
|
||||
|
||||
var delayRandom = new Random();
|
||||
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
_ = Task.Run(
|
||||
() =>
|
||||
{
|
||||
for (int i = 0; i < MessageCount; i++)
|
||||
{
|
||||
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
||||
|
||||
Thread.Sleep(delayRandom.Next(0, 10));
|
||||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < MessageCount; i++)
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Alternate_Priorities()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
|
||||
Assert.AreEqual(9, bus.TotalItemsCount);
|
||||
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Export_Packets_Without_Dequeue()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
|
||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||
Assert.AreEqual(3, exportedPackets.Count);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Await_Single_Packet()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
var delivered = false;
|
||||
|
||||
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
|
||||
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item3.Delivered += (_, __) =>
|
||||
{
|
||||
delivered = true;
|
||||
};
|
||||
|
||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
// The third packet has the event attached.
|
||||
Assert.IsTrue(delivered);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net452;net461;net48;netcoreapp3.1;net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net452;net461;netcoreapp3.1;net6.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
|
||||
@@ -8,9 +8,10 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.MQTTv5
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
@@ -154,7 +155,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +245,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
@@ -8,7 +8,7 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.MQTTv5
|
||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
@@ -171,53 +171,11 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
// Session should be present
|
||||
|
||||
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[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)
|
||||
MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
||||
{
|
||||
return testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
// 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.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
{
|
||||
@@ -34,12 +32,12 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
@@ -47,9 +45,9 @@ namespace MQTTnet.Tests.Mockups
|
||||
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
|
||||
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
{
|
||||
@@ -19,12 +17,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
|
||||
{
|
||||
if (mqttClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mqttClient));
|
||||
}
|
||||
|
||||
mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync;
|
||||
mqttClient.ApplicationMessageReceivedAsync += MqttClientOnApplicationMessageReceivedAsync;
|
||||
}
|
||||
|
||||
public List<MqttApplicationMessageReceivedEventArgs> ReceivedEventArgs
|
||||
@@ -46,32 +39,14 @@ namespace MQTTnet.Tests.Mockups
|
||||
}
|
||||
}
|
||||
|
||||
public string GeneratePayloadSequence()
|
||||
{
|
||||
var sequence = new StringBuilder();
|
||||
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
foreach (var receivedEventArg in _receivedEventArgs)
|
||||
{
|
||||
var payload = receivedEventArg.ApplicationMessage.ConvertPayloadToString();
|
||||
|
||||
// An empty payload is not part of the sequence!
|
||||
sequence.Append(payload);
|
||||
}
|
||||
}
|
||||
|
||||
return sequence.ToString();
|
||||
}
|
||||
|
||||
Task OnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
Task MqttClientOnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
_receivedEventArgs.Add(eventArgs);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,9 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Extensions.Rpc;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.LowLevelClient;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
@@ -110,7 +109,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
configureOptions.Invoke(optionsBuilder);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
@@ -138,7 +137,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
||||
@@ -162,7 +161,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
}
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
@@ -200,16 +199,6 @@ namespace MQTTnet.Tests.Mockups
|
||||
return new TestApplicationMessageReceivedHandler(mqttClient);
|
||||
}
|
||||
|
||||
public TestApplicationMessageReceivedHandler CreateApplicationMessageHandler(IManagedMqttClient managedClient)
|
||||
{
|
||||
if (managedClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(managedClient));
|
||||
}
|
||||
|
||||
return new TestApplicationMessageReceivedHandler(managedClient.InternalClient);
|
||||
}
|
||||
|
||||
public IMqttClient CreateClient()
|
||||
{
|
||||
lock (_clients)
|
||||
@@ -231,7 +220,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
return client;
|
||||
@@ -272,7 +261,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
return Server;
|
||||
@@ -336,7 +325,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync();
|
||||
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
@@ -355,7 +344,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync();
|
||||
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
|
||||
@@ -13,7 +13,6 @@ using MQTTnet.Extensions.Rpc;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
@@ -190,7 +189,7 @@ namespace MQTTnet.Tests
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||
await CompletedTask.Instance;
|
||||
await PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("#");
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -47,20 +47,20 @@ namespace MQTTnet.Tests.Server
|
||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||
{
|
||||
serverConnectedClientId = args.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
serverDisconnectedClientId = args.ClientId;
|
||||
disconnectedMre.Set();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
// Arrange client
|
||||
@@ -68,7 +68,7 @@ namespace MQTTnet.Tests.Server
|
||||
client.ConnectedAsync += args =>
|
||||
{
|
||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientConnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
@@ -81,7 +81,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -108,7 +108,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -161,7 +161,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.StartedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
@@ -183,7 +183,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.StoppedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
@@ -11,7 +11,8 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
@@ -98,7 +99,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var connectingFailedException = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => testEnvironment.ConnectClient());
|
||||
@@ -121,7 +122,7 @@ namespace MQTTnet.Tests.Server
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
// Prepare some retained messages.
|
||||
@@ -145,7 +146,7 @@ namespace MQTTnet.Tests.Server
|
||||
buffer.Append(e.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client2.SubscribeAsync("y");
|
||||
@@ -171,13 +172,13 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientConnectedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref clientConnectedCalled);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref clientDisconnectedCalled);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||
@@ -224,7 +225,7 @@ namespace MQTTnet.Tests.Server
|
||||
// Clear retained message.
|
||||
await client.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||
.WithPayload(EmptyBuffer.Array)
|
||||
.WithPayload(PlatformAbstractionLayer.EmptyByteArray)
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
@@ -275,7 +276,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
e.ApplicationMessage = new MqttApplicationMessage { Topic = "new_topic" };
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
string receivedTopic = null;
|
||||
@@ -285,7 +286,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += a =>
|
||||
{
|
||||
receivedTopic = a.ApplicationMessage.Topic;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("original_topic").Build());
|
||||
@@ -304,7 +305,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
@@ -315,7 +316,7 @@ namespace MQTTnet.Tests.Server
|
||||
c2.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(e.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
|
||||
@@ -338,7 +339,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ApplicationMessageNotConsumedAsync += e =>
|
||||
{
|
||||
undeliverd = e.ApplicationMessage.Topic;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -375,7 +376,7 @@ namespace MQTTnet.Tests.Server
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await Task.Delay(500);
|
||||
@@ -399,7 +400,7 @@ namespace MQTTnet.Tests.Server
|
||||
s.RetainedMessageChangedAsync += e =>
|
||||
{
|
||||
savedRetainedMessages = e.StoredRetainedMessages;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
@@ -439,7 +440,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedTopic = e.ApplicationMessage.Topic;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync("#");
|
||||
@@ -486,7 +487,7 @@ namespace MQTTnet.Tests.Server
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).Build();
|
||||
@@ -520,13 +521,13 @@ namespace MQTTnet.Tests.Server
|
||||
c2.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
c3.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }).ConfigureAwait(false);
|
||||
@@ -580,7 +581,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("c");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
@@ -590,7 +591,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("d");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var clientOptionsBuilder = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString());
|
||||
@@ -620,7 +621,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("r");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c2.SubscribeAsync("topic");
|
||||
@@ -668,7 +669,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
ConnectionValidationHandler(e);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var events = new List<string>();
|
||||
@@ -680,7 +681,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("c");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
@@ -690,7 +691,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("d");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithClientId("same_id");
|
||||
@@ -705,7 +706,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("x");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
@@ -715,7 +716,7 @@ namespace MQTTnet.Tests.Server
|
||||
events.Add("r");
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
c1.SubscribeAsync("topic").Wait();
|
||||
@@ -784,7 +785,7 @@ namespace MQTTnet.Tests.Server
|
||||
client1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedBody = e.ApplicationMessage.Payload;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync("string");
|
||||
@@ -821,7 +822,7 @@ namespace MQTTnet.Tests.Server
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await Task.Delay(500);
|
||||
@@ -849,7 +850,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.DisconnectedAsync += e =>
|
||||
{
|
||||
disconnectCalled++;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await Task.Delay(100);
|
||||
@@ -934,62 +935,7 @@ namespace MQTTnet.Tests.Server
|
||||
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)
|
||||
{
|
||||
if (_connected.ContainsKey(eventArgs.ClientId))
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("t/+");
|
||||
@@ -55,7 +55,7 @@ namespace MQTTnet.Tests.Server
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(120));
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(60));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
@@ -137,7 +137,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
|
||||
@@ -130,7 +130,9 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out _, out _);
|
||||
ulong topicHashMask; // not needed
|
||||
bool hasWildcard; // not needed
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out topicHashMask, out hasWildcard);
|
||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Publish_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Return_Success_When_Subscribed()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Publishing_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Publishing()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -15,57 +15,6 @@ namespace MQTTnet.Tests.Server
|
||||
[TestClass]
|
||||
public sealed class QoS_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Preserve_Message_Order_For_Queued_Messages()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
// Create a session which will contain the messages.
|
||||
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
dummyClient.Dispose();
|
||||
|
||||
// Now inject messages which are appended to the queue of the client.
|
||||
await server.InjectApplicationMessage("T", "0", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "2", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "1", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "4", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "3", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "6", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "5", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "8", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "7", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "9", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Create a new client for the existing message.
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
var messages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
var payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
|
||||
// Disconnect and reconnect to make sure that the server will not send the messages twice.
|
||||
await client.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
await client.ReconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Event_On_Client_Acknowledges_QoS_0()
|
||||
{
|
||||
@@ -77,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
@@ -104,7 +53,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
@@ -139,7 +88,7 @@ namespace MQTTnet.Tests.Server
|
||||
eventArgs.Add(args);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
@@ -150,16 +99,25 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, eventArgs.Count);
|
||||
Assert.AreEqual(2, eventArgs.Count);
|
||||
|
||||
var firstEvent = eventArgs[0];
|
||||
|
||||
Assert.IsNotNull(firstEvent);
|
||||
Assert.IsNotNull(firstEvent.PublishPacket);
|
||||
Assert.IsNotNull(firstEvent.AcknowledgePacket);
|
||||
Assert.IsTrue(firstEvent.IsCompleted);
|
||||
Assert.IsFalse(firstEvent.IsCompleted);
|
||||
|
||||
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
|
||||
|
||||
var secondEvent = eventArgs[1];
|
||||
|
||||
Assert.IsNotNull(secondEvent);
|
||||
Assert.IsNotNull(secondEvent.PublishPacket);
|
||||
Assert.IsNotNull(secondEvent.AcknowledgePacket);
|
||||
Assert.IsTrue(secondEvent.IsCompleted);
|
||||
|
||||
Assert.AreEqual("A", secondEvent.PublishPacket.Topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,66 +65,6 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Downgrade_QoS_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 2!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 1 so a downgrade is required.
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Upgrade_QoS_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 1!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_No_Retained_Message_After_Subscribe()
|
||||
{
|
||||
@@ -184,7 +124,7 @@ namespace MQTTnet.Tests.Server
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithRetainFlag()
|
||||
.Build());
|
||||
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
// Subscribe using a new client.
|
||||
|
||||
@@ -8,7 +8,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Server
|
||||
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||
@@ -47,7 +47,7 @@ namespace MQTTnet.Tests.Server
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
using (var validClient = testEnvironment.CreateClient())
|
||||
@@ -157,7 +157,7 @@ namespace MQTTnet.Tests.Server
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
@@ -8,7 +8,6 @@ using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -29,7 +28,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||
e.ServerReference = "new_server";
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
try
|
||||
|
||||
@@ -10,8 +10,9 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -28,7 +29,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string clientId = "Client1";
|
||||
const string ClientId = "Client1";
|
||||
|
||||
// Create client with clean session and long session expiry interval
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace MQTTnet.Tests.Server
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession() // start and end with clean session
|
||||
.WithClientId(clientId)
|
||||
.WithClientId(ClientId)
|
||||
.Build());
|
||||
|
||||
// Disconnect; empty session should be removed from server
|
||||
@@ -56,7 +57,7 @@ namespace MQTTnet.Tests.Server
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession(false) // see if there is a session
|
||||
.WithClientId(clientId)
|
||||
.WithClientId(ClientId)
|
||||
.Build();
|
||||
|
||||
|
||||
@@ -82,7 +83,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.SessionDeletedAsync += e =>
|
||||
{
|
||||
deletedEventFired = true;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -110,7 +111,7 @@ namespace MQTTnet.Tests.Server
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await testEnvironment.ConnectClient();
|
||||
@@ -122,12 +123,8 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET452
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(MqttProtocolVersion.V311)]
|
||||
[DataRow(MqttProtocolVersion.V500)]
|
||||
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
|
||||
public async Task Handle_Parallel_Connection_Attempts()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
@@ -135,7 +132,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(1)).WithProtocolVersion(protocolVersion);
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
|
||||
|
||||
var hasReceive = false;
|
||||
|
||||
@@ -149,26 +146,41 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
// 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 connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
|
||||
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||
await sendClient.PublishStringAsync("aaa", "1");
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
await Task.Delay(3000);
|
||||
|
||||
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]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
@@ -188,7 +200,7 @@ namespace MQTTnet.Tests.Server
|
||||
c.AutoAcknowledge = false;
|
||||
++count;
|
||||
Console.WriteLine("process");
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await subscriber.SubscribeAsync("#", qos);
|
||||
@@ -225,7 +237,7 @@ namespace MQTTnet.Tests.Server
|
||||
client1.DisconnectedAsync += c =>
|
||||
{
|
||||
disconnectReason = c.Reason;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient(options);
|
||||
@@ -251,7 +263,7 @@ namespace MQTTnet.Tests.Server
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
@@ -264,13 +276,13 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
string receivedPayload = null;
|
||||
@@ -279,7 +291,7 @@ namespace MQTTnet.Tests.Server
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("x");
|
||||
@@ -329,7 +341,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
@@ -337,10 +349,10 @@ namespace MQTTnet.Tests.Server
|
||||
// C2 has the last will defined.
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
||||
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
// C3 will do the connection takeover.
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
var c3 = await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
@@ -348,29 +360,41 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||
async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
|
||||
var sendClient = await testEnvironment.ConnectClient(options);
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
sendClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
onReceive();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
|
||||
using (var subscribeTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||
await sendClient.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, subscribeTimeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return client;
|
||||
return sendClient;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
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 MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Server
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -46,22 +46,6 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Subscribing()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Message_After_Subscription()
|
||||
{
|
||||
@@ -72,7 +56,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
@@ -97,7 +81,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
// Set the topic to "a" regards what the client wants to subscribe.
|
||||
e.TopicFilter.Topic = "a";
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var topicAReceived = false;
|
||||
@@ -115,7 +99,7 @@ namespace MQTTnet.Tests.Server
|
||||
topicBReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.SubscribeAsync("b");
|
||||
@@ -162,7 +146,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 500; i++)
|
||||
@@ -205,7 +189,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||
@@ -245,7 +229,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
||||
@@ -283,7 +267,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
||||
@@ -317,7 +301,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||
@@ -332,7 +316,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
@@ -347,7 +331,7 @@ namespace MQTTnet.Tests.Server
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.UnsubscribeAsync("a");
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
@@ -13,301 +13,49 @@ using MQTTnet.Tests.Mockups;
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class SubscriptionTopicHashTests
|
||||
public class Subscription_TopicHash_Tests
|
||||
{
|
||||
MqttSession _clientSession;
|
||||
|
||||
/// <summary>
|
||||
/// Even fairly regularly named topics as generated by the topic generator should result in shallow hash buckets
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[TestMethod]
|
||||
public void Check_Hash_Bucket_Depth()
|
||||
{
|
||||
const int numPublishers = 5000;
|
||||
const int numTopicsPerPublisher = 10;
|
||||
|
||||
TopicGenerator.Generate(
|
||||
numPublishers,
|
||||
numTopicsPerPublisher,
|
||||
out var topicsByPublisher,
|
||||
out _,
|
||||
out _);
|
||||
|
||||
// There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
|
||||
// Hash bucket depths should remain low.
|
||||
var bucketDepths = new Dictionary<ulong, int>();
|
||||
var maxBucketDepth = 0;
|
||||
ulong maxBucketDepthHash = 0;
|
||||
|
||||
var topicsByHash = new Dictionary<ulong, List<string>>();
|
||||
|
||||
foreach (var t in topicsByPublisher)
|
||||
{
|
||||
var topics = t.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
|
||||
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
||||
++currentValue;
|
||||
bucketDepths[topicHash] = currentValue;
|
||||
|
||||
if (currentValue > maxBucketDepth)
|
||||
{
|
||||
maxBucketDepth = currentValue;
|
||||
maxBucketDepthHash = topicHash;
|
||||
}
|
||||
|
||||
if (!topicsByHash.TryGetValue(topicHash, out var topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByHash.Add(topicHash, topicList);
|
||||
}
|
||||
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
|
||||
var maxDepthTopics = topicsByHash[maxBucketDepthHash];
|
||||
|
||||
Console.Write("Max bucket depth is " + maxBucketDepth);
|
||||
|
||||
// for the test case the bucket depth should be less than 100
|
||||
Assert.IsTrue(maxBucketDepth < 100, "Unexpected high topic hash bucket depth");
|
||||
}
|
||||
enum TopicHashSelector { NoWildcard, SingleWildcard, MultiWildcard };
|
||||
|
||||
MQTTnet.Server.MqttSession _clientSession;
|
||||
|
||||
[TestMethod]
|
||||
public void Check_Selected_Topic_Hashes()
|
||||
public void Match_Hash_Test_SingleWildCard()
|
||||
{
|
||||
CheckTopicHash("client1/building1/level1/sensor1", 0x655D4AF100000000, 0xFFFFFFFFFFFFFFFF);
|
||||
CheckTopicHash("client1/building1/+/sensor1", 0x655D00F100000000, 0xFFFF00FFFFFFFFFF);
|
||||
CheckTopicHash("client1/+/level1/+", 0x65004A0000000000, 0xFF00FF00FFFFFFFF);
|
||||
CheckTopicHash("client1/building1/level1/#", 0x655D4A0000000000, 0xFFFFFF0000000000);
|
||||
CheckTopicHash("client1/+/level1/#", 0x65004A0000000000, 0xFF00FF0000000000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectMultiLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
var l0 = "pub0";
|
||||
var l1 = "topic1";
|
||||
var l2 = "+";
|
||||
var l3 = "prop1";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectSingleLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_MultiWildcard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int numLevels = 8;
|
||||
var levelNames = new string[numLevels];
|
||||
for (var i = 0; i < numLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append("/");
|
||||
}
|
||||
|
||||
string levelName;
|
||||
if (i == numLevels - 1)
|
||||
{
|
||||
// last one is #
|
||||
levelName = "#";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
|
||||
var topic = sb.ToString();
|
||||
|
||||
// UInt64 is limited to 8 levels
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
var count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
||||
Assert.AreEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
// The mask should have ff except for last level
|
||||
// The mask should have zeroes where the wildcard and ff at the end
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name exceeding 8 levels
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_NoWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int numLevels = 9;
|
||||
var levelNames = new string[numLevels];
|
||||
for (var i = 0; i < numLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append("/");
|
||||
}
|
||||
|
||||
var levelName = "level" + i;
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
|
||||
var topic = sb.ToString();
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
var count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
++count;
|
||||
}
|
||||
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_SingleWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int numLevels = 8;
|
||||
var levelNames = new string[numLevels];
|
||||
for (var i = 0; i < numLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append("/");
|
||||
}
|
||||
|
||||
string levelName;
|
||||
if (i == numLevels - 1)
|
||||
{
|
||||
// last one is +
|
||||
levelName = "+";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
|
||||
var topic = sb.ToString();
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
var count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
// wildcard position
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[2], 0, "mask 2 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -317,9 +65,13 @@ namespace MQTTnet.Tests.Server
|
||||
var l1 = "topic1";
|
||||
var l2 = "sub1";
|
||||
var l3 = "#";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
@@ -352,9 +104,13 @@ namespace MQTTnet.Tests.Server
|
||||
var l1 = "topic1";
|
||||
var l2 = "sub1";
|
||||
var l3 = "prop1";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
||||
|
||||
@@ -381,13 +137,225 @@ namespace MQTTnet.Tests.Server
|
||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public async Task Match_Hash_Test_Search_MultiWildcard()
|
||||
public void Match_Hash_Test_LongTopic_MultiWildcard()
|
||||
{
|
||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.MultiWildcard);
|
||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Multi Wildcard");
|
||||
// Should match all topics
|
||||
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 8;
|
||||
var levelNames = new string[NumLevels];
|
||||
for (var i = 0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
string levelName;
|
||||
if (i == NumLevels - 1)
|
||||
{
|
||||
// last one is #
|
||||
levelName = "#";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
// UInt64 is limited to 8 levels
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff except for last level
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_SingleWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 8;
|
||||
var levelNames = new string[NumLevels];
|
||||
for (var i = 0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
string levelName;
|
||||
if (i == NumLevels - 1)
|
||||
{
|
||||
// last one is +
|
||||
levelName = "+";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
// wildcard position
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name exceeding 8 levels
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_NoWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 9;
|
||||
var levelNames = new string[NumLevels];
|
||||
for(var i=0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
var levelName = "level" + i;
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach(var h in hashBytes)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectSingleLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectMultiLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -439,125 +407,134 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_SingleWildCard()
|
||||
public async Task Match_Hash_Test_Search_MultiWildcard()
|
||||
{
|
||||
var l0 = "pub0";
|
||||
var l1 = "topic1";
|
||||
var l2 = "+";
|
||||
var l3 = "prop1";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.MultiWildcard);
|
||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Multi Wildcard");
|
||||
// Should match all topics
|
||||
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
||||
}
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
/// <summary>
|
||||
/// Even fairly regularly named topics as generated by the topic generator should result in shallow hash buckets
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[TestMethod]
|
||||
public void Check_Hash_Bucket_Depth()
|
||||
{
|
||||
Dictionary<string, List<string>> topicsByPublisher;
|
||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
const int NumPublishers = 5000;
|
||||
const int NumTopicsPerPublisher = 10;
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
||||
Assert.AreEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
||||
|
||||
// The mask should have zeroes where the wildcard and ff at the end
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[2], 0, "mask 2 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||
// There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
|
||||
// Hash bucket depths should remain low.
|
||||
var bucketDepths = new Dictionary<UInt64, int>();
|
||||
int maxBucketDepth = 0;
|
||||
UInt64 maxBucketDepthHash = 0;
|
||||
|
||||
var topicsByHash = new Dictionary<UInt64, List<string>>();
|
||||
|
||||
foreach (var t in topicsByPublisher)
|
||||
{
|
||||
var topics = t.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
UInt64 topicHash;
|
||||
UInt64 hashMask;
|
||||
bool hasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
||||
++currentValue;
|
||||
bucketDepths[topicHash] = currentValue;
|
||||
|
||||
if (currentValue > maxBucketDepth)
|
||||
{
|
||||
maxBucketDepth = currentValue;
|
||||
maxBucketDepthHash = topicHash;
|
||||
}
|
||||
|
||||
if (!topicsByHash.TryGetValue(topicHash, out var topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByHash.Add(topicHash, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
|
||||
var maxDepthTopics = topicsByHash[maxBucketDepthHash];
|
||||
|
||||
Console.Write("Max bucket depth is " + maxBucketDepth);
|
||||
|
||||
// for the test case the bucket depth should be less than 100
|
||||
Assert.IsTrue(maxBucketDepth < 100, "Unexpected high topic hash bucket depth");
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Check_Selected_Topic_Hashes()
|
||||
{
|
||||
CheckTopicHash("client1/building1/level1/sensor1", 0x655D4AF100000000, 0xFFFFFFFFFFFFFFFF);
|
||||
CheckTopicHash("client1/building1/+/sensor1", 0x655D00F100000000, 0xFFFF00FFFFFFFFFF);
|
||||
CheckTopicHash("client1/+/level1/+", 0x65004A0000000000, 0xFF00FF00FFFFFFFF);
|
||||
CheckTopicHash("client1/building1/level1/#", 0x655D4A0000000000, 0xFFFFFF0000000000);
|
||||
CheckTopicHash("client1/+/level1/#", 0x65004A0000000000, 0xFF00FF0000000000);
|
||||
}
|
||||
|
||||
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
ulong topicHash;
|
||||
ulong hashMask;
|
||||
bool hasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Topic: " + topic);
|
||||
Console.WriteLine($"Hash: {topicHash:X8}");
|
||||
Console.WriteLine($"Hash Mask: {hashMask:X8}");
|
||||
Console.WriteLine(string.Format("Hash: {0:X8}", topicHash));
|
||||
Console.WriteLine(string.Format("Hash Mask: {0:X8}", hashMask));
|
||||
|
||||
Assert.AreEqual(expectedHash, topicHash, "Topic hash not as expected. Has the hash function changed?");
|
||||
Assert.AreEqual(expectedHashMask, hashMask, "Topic hash mask not as expected");
|
||||
}
|
||||
|
||||
int CheckTopicSubscriptions(MqttSession clientSession, List<string> topicsToFind, string subject)
|
||||
{
|
||||
var matchCount = 0;
|
||||
|
||||
{
|
||||
var resultCount = 0;
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
var countUp = 0;
|
||||
var countDown = topicsToFind.Count - 1;
|
||||
for (; countUp < topicsToFind.Count; ++countUp, --countDown)
|
||||
{
|
||||
var topicToFind = topicsToFind[countDown];
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topicToFind, out var topicHash, out _, out _);
|
||||
|
||||
clientSession.TryCheckSubscriptions(topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient", out var result);
|
||||
if (result.IsSubscribed)
|
||||
{
|
||||
++matchCount;
|
||||
}
|
||||
|
||||
++resultCount;
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write("Match count: " + matchCount + "; ");
|
||||
|
||||
Console.WriteLine(subject + " lookup milliseconds: " + stopWatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
byte[] GetBytes(ulong value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
// Ensure that highest byte comes first for comparison left to right
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
return bytes.Reverse().ToArray();
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
async Task<List<string>> PrepareTopicHashSubscriptions(TopicHashSelector selector)
|
||||
{
|
||||
const int numPublishers = 1;
|
||||
const int numTopicsPerPublisher = 10000;
|
||||
Dictionary<string, List<string>> topicsByPublisher;
|
||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
||||
|
||||
TopicGenerator.Generate(
|
||||
numPublishers,
|
||||
numTopicsPerPublisher,
|
||||
out var topicsByPublisher,
|
||||
out var singleWildcardTopicsByPublisher,
|
||||
out var multiWildcardTopicsByPublisher);
|
||||
const int NumPublishers = 1;
|
||||
const int NumTopicsPerPublisher = 10000;
|
||||
|
||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
||||
|
||||
var topics = topicsByPublisher.FirstOrDefault().Value;
|
||||
var singleWildcardTopics = singleWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||
var multiWildcardTopics = multiWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||
|
||||
const string clientId = "Client1";
|
||||
var logger = new TestLogger();
|
||||
var serverOptions = new MqttServerOptions();
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
const string ClientId = "Client1";
|
||||
var logger = new Mockups.TestLogger();
|
||||
var serverOptions = new MQTTnet.Server.MqttServerOptions();
|
||||
var eventContainer = new MQTTnet.Server.MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
||||
_clientSession = new MqttSession(clientId, false, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
||||
_clientSession = new MQTTnet.Server.MqttSession(
|
||||
ClientId,
|
||||
false,
|
||||
new Dictionary<object, object>(),
|
||||
serverOptions,
|
||||
eventContainer,
|
||||
retainedMessagesManager,
|
||||
sessionManager
|
||||
);
|
||||
|
||||
List<string> topicsToSubscribe;
|
||||
|
||||
@@ -573,26 +550,63 @@ namespace MQTTnet.Tests.Server
|
||||
topicsToSubscribe = topics;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var t in topicsToSubscribe)
|
||||
{
|
||||
var subPacket = new MqttSubscribePacket();
|
||||
var filter = new MqttTopicFilter
|
||||
{
|
||||
Topic = t
|
||||
};
|
||||
var subPacket = new Packets.MqttSubscribePacket();
|
||||
var filter = new Packets.MqttTopicFilter();
|
||||
filter.Topic = t;
|
||||
subPacket.TopicFilters.Add(filter);
|
||||
await _clientSession.Subscribe(subPacket, default);
|
||||
await _clientSession.SubscriptionsManager.Subscribe(subPacket, default(CancellationToken));
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
enum TopicHashSelector
|
||||
int CheckTopicSubscriptions(MQTTnet.Server.MqttSession clientSession, List<string> topicsToFind, string subject)
|
||||
{
|
||||
NoWildcard,
|
||||
SingleWildcard,
|
||||
MultiWildcard
|
||||
var matchCount = 0;
|
||||
|
||||
{
|
||||
int resultCount = 0;
|
||||
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
var countUp = 0;
|
||||
var countDown = topicsToFind.Count - 1;
|
||||
for (; countUp < topicsToFind.Count; ++countUp, --countDown)
|
||||
{
|
||||
var topicToFind = topicsToFind[countDown];
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 hashMask;
|
||||
bool hasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash((string)topicToFind, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
var result = clientSession.SubscriptionsManager.CheckSubscriptions((string)topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient");
|
||||
if (result.IsSubscribed)
|
||||
{
|
||||
++matchCount;
|
||||
}
|
||||
++resultCount;
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write("Match count: " + matchCount + "; ");
|
||||
|
||||
Console.WriteLine(subject + " lookup milliseconds: " + stopWatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
byte[] GetBytes(UInt64 value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
// Ensure that highest byte comes first for comparison left to right
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return bytes.Reverse().ToArray();
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
@@ -50,7 +50,7 @@ namespace MQTTnet.Tests.Server
|
||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync("#");
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Unsubscribe_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Unsubscribing()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
await client.UnsubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
@@ -49,7 +48,7 @@ namespace MQTTnet.Tests.Server
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
@@ -53,7 +53,7 @@ namespace MQTTnet.Tests.Server
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
public class TopicGenerator
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
const uint ErrorOperationAborted = 0x800703E3;
|
||||
const int ReadBufferSize = 4096;
|
||||
|
||||
|
||||
readonly IMqttChannel _channel;
|
||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
@@ -72,26 +73,7 @@ namespace MQTTnet.Adapter
|
||||
|
||||
try
|
||||
{
|
||||
/*
|
||||
* 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 canceled.", cancellationToken);
|
||||
}
|
||||
|
||||
// Make sure that the exception from the connect task gets thrown.
|
||||
await connectTask.ConfigureAwait(false);
|
||||
}
|
||||
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -194,7 +176,7 @@ namespace MQTTnet.Adapter
|
||||
// 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
|
||||
// worker thread is still sending publish packets etc.
|
||||
using (await _syncRoot.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -206,14 +188,11 @@ namespace MQTTnet.Adapter
|
||||
|
||||
_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)
|
||||
{
|
||||
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);
|
||||
await _channel.WriteAsync(packetBuffer.Payload.Array, packetBuffer.Payload.Offset, packetBuffer.Payload.Count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
|
||||
@@ -295,29 +274,11 @@ namespace MQTTnet.Adapter
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
// Check two times for cancellation because the call to _ReadAsync_ might block for some time.
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return ReadFixedHeaderResult.Canceled;
|
||||
}
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
int bytesRead;
|
||||
try
|
||||
{
|
||||
bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return ReadFixedHeaderResult.Canceled;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
return ReadFixedHeaderResult.ConnectionClosed;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return ReadFixedHeaderResult.Canceled;
|
||||
return ReadFixedHeaderResult.Cancelled;
|
||||
}
|
||||
|
||||
if (bytesRead == 0)
|
||||
@@ -380,7 +341,7 @@ namespace MQTTnet.Adapter
|
||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, EmptyBuffer.ArraySegment, 2);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, PlatformAbstractionLayer.EmptyByteArraySegment, 2);
|
||||
}
|
||||
|
||||
var bodyLength = fixedHeader.RemainingLength;
|
||||
|
||||
@@ -12,17 +12,13 @@ namespace MQTTnet.Channel
|
||||
public interface IMqttChannel : IDisposable
|
||||
{
|
||||
string Endpoint { get; }
|
||||
|
||||
bool IsSecureConnection { get; }
|
||||
|
||||
X509Certificate2 ClientCertificate { get; }
|
||||
|
||||
Task ConnectAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task DisconnectAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||
|
||||
Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken);
|
||||
Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +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 MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientDisconnectedException : MqttCommunicationException
|
||||
{
|
||||
public MqttClientDisconnectedException(Exception innerException) : base("The MQTT client is disconnected.", innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,32 +7,32 @@ namespace MQTTnet.Client
|
||||
{
|
||||
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<MqttClientConnectedEventArgs, Task> ConnectedAsync;
|
||||
|
||||
event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync;
|
||||
|
||||
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
||||
|
||||
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
||||
|
||||
|
||||
bool IsConnected { get; }
|
||||
|
||||
|
||||
MqttClientOptions Options { get; }
|
||||
|
||||
|
||||
Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default);
|
||||
|
||||
|
||||
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken = default);
|
||||
|
||||
|
||||
Task PingAsync(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);
|
||||
|
||||
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
+409
-446
File diff suppressed because it is too large
Load Diff
@@ -69,17 +69,6 @@ namespace MQTTnet.Client
|
||||
return mqttClient.PublishBinaryAsync(topic, payloadBuffer, qualityOfServiceLevel, retain, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task ReconnectAsync(this IMqttClient client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client.Options == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The MQTT client was not connected before. A reconnect is only permitted when the client was already connected or at least tried to.");
|
||||
}
|
||||
|
||||
return client.ConnectAsync(client.Options, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data)
|
||||
{
|
||||
if (client == null)
|
||||
@@ -128,26 +117,6 @@ namespace MQTTnet.Client
|
||||
return mqttClient.SubscribeAsync(subscribeOptions, cancellationToken);
|
||||
}
|
||||
|
||||
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<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient mqttClient, string topic, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (mqttClient == null)
|
||||
|
||||
@@ -10,38 +10,29 @@ namespace MQTTnet.Client
|
||||
public sealed class MqttClientPublishResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if the overall status of the publish is a success. This can be the reason code _Success_ or
|
||||
/// _NoMatchingSubscribers_. _NoMatchingSubscribers_ usually indicates only that no other client is interested in the topic but overall transmit
|
||||
/// to the server etc. was a success.
|
||||
/// </summary>
|
||||
public bool IsSuccess => ReasonCode == MqttClientPublishReasonCode.Success || ReasonCode == MqttClientPublishReasonCode.NoMatchingSubscribers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the packet identifier which was used for this publish.
|
||||
/// Gets the packet identifier which was used for this publish.
|
||||
/// </summary>
|
||||
public ushort? PacketIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets the reason code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets the reason string.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
|
||||
/// packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
||||
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,83 +13,80 @@ namespace MQTTnet.Client
|
||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
readonly Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> _acknowledgeHandler;
|
||||
|
||||
|
||||
int _isAcknowledged;
|
||||
|
||||
|
||||
public MqttApplicationMessageReceivedEventArgs(
|
||||
string clientId,
|
||||
string clientId,
|
||||
MqttApplicationMessage applicationMessage,
|
||||
MqttPublishPacket publishPacket,
|
||||
Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> acknowledgeHandler)
|
||||
{
|
||||
ClientId = clientId;
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
||||
PublishPacket = publishPacket;
|
||||
PacketIdentifier = publishPacket.PacketIdentifier;
|
||||
_acknowledgeHandler = acknowledgeHandler;
|
||||
}
|
||||
|
||||
public MqttApplicationMessage ApplicationMessage { get; }
|
||||
|
||||
internal MqttPublishPacket PublishPacket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the library should send MQTT ACK packets automatically if required.
|
||||
/// </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.
|
||||
/// Gets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
public string ClientId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this message was handled.
|
||||
/// This value can be used in user code for custom control flow.
|
||||
/// </summary>
|
||||
public bool IsHandled { get; set; }
|
||||
public MqttApplicationMessage ApplicationMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the MQTT packet
|
||||
/// </summary>
|
||||
public ushort PacketIdentifier => PublishPacket.PacketIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public bool ProcessingFailed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason code which will be sent to the server.
|
||||
/// 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.
|
||||
/// 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>
|
||||
public bool IsHandled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the library should send MQTT ACK packets automatically if required.
|
||||
/// </summary>
|
||||
public bool AutoAcknowledge { get; set; } = true;
|
||||
|
||||
public object Tag { get; set; }
|
||||
|
||||
internal MqttPublishPacket PublishPacket { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the indentifier of MQTT packet
|
||||
/// </summary>
|
||||
public ushort PacketIdentifier { get; }
|
||||
|
||||
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
||||
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_acknowledgeHandler == null)
|
||||
{
|
||||
throw new NotSupportedException("Deferred acknowledgement of application message is not yet supported in MQTTnet server.");
|
||||
}
|
||||
|
||||
|
||||
if (Interlocked.CompareExchange(ref _isAcknowledged, 1, 0) == 0)
|
||||
{
|
||||
return _acknowledgeHandler(this, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
throw new InvalidOperationException("The application message is already acknowledged.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,12 @@ namespace MQTTnet.Exceptions
|
||||
{
|
||||
public class MqttCommunicationException : Exception
|
||||
{
|
||||
protected MqttCommunicationException()
|
||||
{
|
||||
}
|
||||
|
||||
public MqttCommunicationException(Exception innerException)
|
||||
: base(innerException?.Message ?? "MQTT communication failed.", innerException)
|
||||
: base(innerException.Message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -3,15 +3,14 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Exceptions
|
||||
{
|
||||
public sealed class MqttClientUnexpectedDisconnectReceivedException : MqttCommunicationException
|
||||
public class MqttUnexpectedDisconnectReceivedException : MqttCommunicationException
|
||||
{
|
||||
public MqttClientUnexpectedDisconnectReceivedException(MqttDisconnectPacket disconnectPacket)
|
||||
public MqttUnexpectedDisconnectReceivedException(MqttDisconnectPacket disconnectPacket)
|
||||
: base($"Unexpected DISCONNECT (Reason code={disconnectPacket.ReasonCode}) received.")
|
||||
{
|
||||
ReasonCode = disconnectPacket.ReasonCode;
|
||||
@@ -6,13 +6,13 @@ using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttBufferReader
|
||||
{
|
||||
byte[] _buffer = EmptyBuffer.Array;
|
||||
byte[] _buffer = PlatformAbstractionLayer.EmptyByteArray;
|
||||
int _initialPosition;
|
||||
int _length;
|
||||
int _position;
|
||||
@@ -29,7 +29,7 @@ namespace MQTTnet.Formatter
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return EmptyBuffer.Array;
|
||||
return PlatformAbstractionLayer.EmptyByteArray;
|
||||
}
|
||||
|
||||
ValidateReceiveBuffer(length);
|
||||
@@ -66,7 +66,7 @@ namespace MQTTnet.Formatter
|
||||
|
||||
if (bufferLength == 0)
|
||||
{
|
||||
return EmptyBuffer.Array;
|
||||
return PlatformAbstractionLayer.EmptyByteArray;
|
||||
}
|
||||
|
||||
var buffer = new byte[bufferLength];
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public readonly struct MqttPacketBuffer
|
||||
{
|
||||
static readonly ArraySegment<byte> EmptyPayload = EmptyBuffer.ArraySegment;
|
||||
static readonly ArraySegment<byte> EmptyPayload = PlatformAbstractionLayer.EmptyByteArraySegment;
|
||||
|
||||
public MqttPacketBuffer(ArraySegment<byte> packet, ArraySegment<byte> payload)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace MQTTnet.Formatter
|
||||
public sealed class MqttPacketFactories
|
||||
{
|
||||
public MqttConnAckPacketFactory ConnAck { get; } = new MqttConnAckPacketFactory();
|
||||
|
||||
public MqttConnectPacketFactory Connect { get; } = new MqttConnectPacketFactory();
|
||||
|
||||
public MqttDisconnectPacketFactory Disconnect { get; } = new MqttDisconnectPacketFactory();
|
||||
|
||||
@@ -12,10 +12,7 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttPubAckPacketFactory
|
||||
{
|
||||
public MqttPubAckPacket Create(
|
||||
MqttPublishPacket publishPacket,
|
||||
InterceptingPublishEventArgs interceptingPublishEventArgs,
|
||||
DispatchApplicationMessageResult dispatchApplicationMessageResult)
|
||||
public MqttPubAckPacket Create(MqttPublishPacket publishPacket, InterceptingPublishEventArgs interceptingPublishEventArgs)
|
||||
{
|
||||
if (publishPacket == null)
|
||||
{
|
||||
@@ -24,19 +21,10 @@ namespace MQTTnet.Formatter
|
||||
|
||||
var pubAckPacket = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = publishPacket.PacketIdentifier
|
||||
PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubAckReasonCode.Success
|
||||
};
|
||||
|
||||
if (dispatchApplicationMessageResult.MatchingSubscribersCount == 0)
|
||||
{
|
||||
// NoMatchingSubscribers is ONLY sent by the server!
|
||||
pubAckPacket.ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers;
|
||||
}
|
||||
else
|
||||
{
|
||||
pubAckPacket.ReasonCode = MqttPubAckReasonCode.Success;
|
||||
}
|
||||
|
||||
if (interceptingPublishEventArgs != null)
|
||||
{
|
||||
pubAckPacket.ReasonCode = (MqttPubAckReasonCode)(int)interceptingPublishEventArgs.Response.ReasonCode;
|
||||
@@ -54,12 +42,24 @@ namespace MQTTnet.Formatter
|
||||
throw new ArgumentNullException(nameof(applicationMessageReceivedEventArgs));
|
||||
}
|
||||
|
||||
var pubAckPacket = Create(applicationMessageReceivedEventArgs.PublishPacket, applicationMessageReceivedEventArgs.ReasonCode);
|
||||
pubAckPacket.UserProperties = applicationMessageReceivedEventArgs.ResponseUserProperties;
|
||||
pubAckPacket.ReasonString = applicationMessageReceivedEventArgs.ResponseReasonString;
|
||||
|
||||
return pubAckPacket;
|
||||
}
|
||||
|
||||
static MqttPubAckPacket Create(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode applicationMessageReceivedReasonCode)
|
||||
{
|
||||
if (publishPacket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(publishPacket));
|
||||
}
|
||||
|
||||
var pubAckPacket = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = applicationMessageReceivedEventArgs.PublishPacket.PacketIdentifier,
|
||||
ReasonCode = (MqttPubAckReasonCode)(int)applicationMessageReceivedEventArgs.ReasonCode,
|
||||
UserProperties = applicationMessageReceivedEventArgs.ResponseUserProperties,
|
||||
ReasonString = applicationMessageReceivedEventArgs.ResponseReasonString
|
||||
PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
ReasonCode = (MqttPubAckReasonCode)(int)applicationMessageReceivedReasonCode
|
||||
};
|
||||
|
||||
return pubAckPacket;
|
||||
|
||||
@@ -25,10 +25,7 @@ namespace MQTTnet.Formatter
|
||||
return pubRecPacket;
|
||||
}
|
||||
|
||||
public MqttPacket Create(
|
||||
MqttPublishPacket publishPacket,
|
||||
InterceptingPublishEventArgs interceptingPublishEventArgs,
|
||||
DispatchApplicationMessageResult dispatchApplicationMessageResult)
|
||||
public MqttPacket Create(MqttPublishPacket publishPacket, InterceptingPublishEventArgs interceptingPublishEventArgs)
|
||||
{
|
||||
if (publishPacket == null)
|
||||
{
|
||||
@@ -41,11 +38,6 @@ namespace MQTTnet.Formatter
|
||||
ReasonCode = MqttPubRecReasonCode.Success
|
||||
};
|
||||
|
||||
if (dispatchApplicationMessageResult.MatchingSubscribersCount == 0)
|
||||
{
|
||||
pubRecPacket.ReasonCode = MqttPubRecReasonCode.NoMatchingSubscribers;
|
||||
}
|
||||
|
||||
if (interceptingPublishEventArgs != null)
|
||||
{
|
||||
pubRecPacket.ReasonCode = (MqttPubRecReasonCode)(int)interceptingPublishEventArgs.Response.ReasonCode;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
@@ -81,17 +80,5 @@ namespace MQTTnet.Formatter
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public MqttPublishPacket Create(MqttRetainedMessageMatch retainedMessage)
|
||||
{
|
||||
if (retainedMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(retainedMessage));
|
||||
}
|
||||
|
||||
var publishPacket = Create(retainedMessage.ApplicationMessage);
|
||||
publishPacket.QualityOfServiceLevel = retainedMessage.SubscriptionQualityOfServiceLevel;
|
||||
return publishPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,16 +10,16 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttUnsubAckPacketFactory
|
||||
{
|
||||
public MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, UnsubscribeResult unsubscribeResult)
|
||||
public MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, MqttUnsubscribeResult mqttUnsubscribeResult)
|
||||
{
|
||||
if (unsubscribePacket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
}
|
||||
|
||||
if (unsubscribeResult == null)
|
||||
if (mqttUnsubscribeResult == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(unsubscribeResult));
|
||||
throw new ArgumentNullException(nameof(mqttUnsubscribeResult));
|
||||
}
|
||||
|
||||
var unsubAckPacket = new MqttUnsubAckPacket
|
||||
@@ -28,7 +28,7 @@ namespace MQTTnet.Formatter
|
||||
};
|
||||
|
||||
// MQTTv5.0.0 only.
|
||||
unsubAckPacket.ReasonCodes = unsubscribeResult.ReasonCodes;
|
||||
unsubAckPacket.ReasonCodes = mqttUnsubscribeResult.ReasonCodes;
|
||||
|
||||
return unsubAckPacket;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public struct ReadFixedHeaderResult
|
||||
{
|
||||
public static ReadFixedHeaderResult Canceled { get; } = new ReadFixedHeaderResult
|
||||
public static ReadFixedHeaderResult Cancelled { get; } = new ReadFixedHeaderResult
|
||||
{
|
||||
IsCanceled = true
|
||||
IsCancelled = true
|
||||
};
|
||||
|
||||
public static ReadFixedHeaderResult ConnectionClosed { get; } = new ReadFixedHeaderResult
|
||||
@@ -16,7 +16,7 @@ namespace MQTTnet.Formatter
|
||||
IsConnectionClosed = true
|
||||
};
|
||||
|
||||
public bool IsCanceled { get; set; }
|
||||
public bool IsCancelled { get; set; }
|
||||
|
||||
public bool IsConnectionClosed { get; set; }
|
||||
|
||||
|
||||
@@ -15,20 +15,14 @@ namespace MQTTnet.Implementations
|
||||
public sealed class CrossPlatformSocket : IDisposable
|
||||
{
|
||||
readonly Socket _socket;
|
||||
|
||||
#if !NET5_0_OR_GREATER
|
||||
readonly Action _socketDisposeAction;
|
||||
#endif
|
||||
|
||||
NetworkStream _networkStream;
|
||||
|
||||
public CrossPlatformSocket(AddressFamily addressFamily)
|
||||
{
|
||||
_socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
#if !NET5_0_OR_GREATER
|
||||
_socketDisposeAction = _socket.Dispose;
|
||||
#endif
|
||||
}
|
||||
|
||||
public CrossPlatformSocket()
|
||||
@@ -37,76 +31,17 @@ namespace MQTTnet.Implementations
|
||||
// will make use of dual mode in the .net framework.
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
#if !NET5_0_OR_GREATER
|
||||
_socketDisposeAction = _socket.Dispose;
|
||||
#endif
|
||||
}
|
||||
|
||||
CrossPlatformSocket(Socket socket)
|
||||
public CrossPlatformSocket(Socket socket)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
_networkStream = new NetworkStream(socket, true);
|
||||
|
||||
#if !NET5_0_OR_GREATER
|
||||
_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
|
||||
{
|
||||
// We cannot use the _NoDelay_ property from the socket because there is an issue in .NET 4.5.2, 4.6.
|
||||
@@ -116,20 +51,24 @@ namespace MQTTnet.Implementations
|
||||
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
|
||||
{
|
||||
get => _socket.ReceiveBufferSize;
|
||||
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
|
||||
{
|
||||
get => _socket.SendBufferSize;
|
||||
@@ -142,6 +81,16 @@ namespace MQTTnet.Implementations
|
||||
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()
|
||||
{
|
||||
try
|
||||
@@ -160,6 +109,8 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public EndPoint LocalEndPoint => _socket.LocalEndPoint;
|
||||
|
||||
public void Bind(EndPoint localEndPoint)
|
||||
{
|
||||
if (localEndPoint is null)
|
||||
@@ -170,6 +121,11 @@ namespace MQTTnet.Implementations
|
||||
_socket.Bind(localEndPoint);
|
||||
}
|
||||
|
||||
public void Listen(int connectionBacklog)
|
||||
{
|
||||
_socket.Listen(connectionBacklog);
|
||||
}
|
||||
|
||||
public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
|
||||
{
|
||||
if (host is null)
|
||||
@@ -181,17 +137,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
try
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
|
||||
if (_networkStream != null)
|
||||
{
|
||||
await _networkStream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _socket.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
_networkStream?.Dispose();
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
await _socket.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(_socketDisposeAction))
|
||||
{
|
||||
@@ -225,26 +175,20 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||
{
|
||||
_networkStream?.Dispose();
|
||||
_socket?.Dispose();
|
||||
}
|
||||
|
||||
public NetworkStream GetStream()
|
||||
{
|
||||
var networkStream = _networkStream;
|
||||
if (networkStream == null)
|
||||
try
|
||||
{
|
||||
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 Listen(int connectionBacklog)
|
||||
{
|
||||
_socket.Listen(connectionBacklog);
|
||||
}
|
||||
|
||||
public async Task<int> ReceiveAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||
@@ -264,27 +208,28 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||
public NetworkStream GetStream()
|
||||
{
|
||||
try
|
||||
var networkStream = _networkStream;
|
||||
if (networkStream == null)
|
||||
{
|
||||
#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.
|
||||
throw new IOException("The socket is not connected.");
|
||||
}
|
||||
|
||||
return networkStream;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_networkStream?.Dispose();
|
||||
_socket?.Dispose();
|
||||
}
|
||||
|
||||
#if NET452 || NET461
|
||||
sealed class SocketWrapper
|
||||
{
|
||||
readonly ArraySegment<byte> _buffer;
|
||||
readonly Socket _socket;
|
||||
readonly ArraySegment<byte> _buffer;
|
||||
readonly SocketFlags _socketFlags;
|
||||
|
||||
public SocketWrapper(Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags)
|
||||
@@ -294,28 +239,16 @@ namespace MQTTnet.Implementations
|
||||
_socketFlags = socketFlags;
|
||||
}
|
||||
|
||||
public static IAsyncResult BeginReceive(AsyncCallback callback, object state)
|
||||
{
|
||||
var socketWrapper = (SocketWrapper)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);
|
||||
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)
|
||||
{
|
||||
var socketWrapper = (SocketWrapper)state;
|
||||
return socketWrapper._socket.BeginReceive(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -108,12 +108,12 @@ namespace MQTTnet.Implementations
|
||||
return _readStream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
// 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
|
||||
// Flush method.
|
||||
_writeStream.Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||
_writeStream.Write(buffer, offset, count);
|
||||
return _writeStream.FlushAsync(cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Runtime.ExceptionServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -155,7 +154,7 @@ namespace MQTTnet.Implementations
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Dispose();
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -226,7 +225,7 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -240,12 +239,12 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
||||
await stream.WriteAsync(buffer.AsMemory(), cancellationToken).ConfigureAwait(false);
|
||||
await stream.WriteAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(_disposeAction))
|
||||
{
|
||||
await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
|
||||
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -57,13 +56,13 @@ namespace MQTTnet.Implementations
|
||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
Cleanup();
|
||||
return CompletedTask.Instance;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -125,7 +124,7 @@ namespace MQTTnet.Implementations
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler == null)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
return clientHandler(channelAdapter);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user