mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-20 11:45:43 +08:00
Use latest language features (#2176)
* Make use of language features line AsyncDispose, init etc. * Fix build errors * Improve Unit Tests * Improve Unit Tests * Improve Unit Tests * Update samples * Remove obsolete benchmarks * Code cleanup * Improve unit tests * Improve Unit Tests * Fix Unit Tests * Improve Unit Tests * Improve Unit Tests * Restore unit tests * Exclude load tests from unit tests
This commit is contained in:
@@ -2,27 +2,58 @@
|
||||
// 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 MQTTnet.Internal;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public sealed class AsyncLockTest
|
||||
{
|
||||
public sealed class AsyncLockTest
|
||||
public async Task Run()
|
||||
{
|
||||
public async Task Run()
|
||||
try
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
var semaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
using (var cancellationToken = new CancellationTokenSource())
|
||||
await semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
// Wait for data from socket etc...
|
||||
// Then get an exception.
|
||||
semaphore.Dispose();
|
||||
throw new MqttCommunicationException("Connection closed");
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception.ToString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// var asyncLock = new AsyncLock();
|
||||
//
|
||||
// using var cancellationToken = new CancellationTokenSource();
|
||||
// for (var i = 0; i < 100000; i++)
|
||||
// {
|
||||
// using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -6,49 +6,48 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class ClientFlowTest
|
||||
{
|
||||
public static class ClientFlowTest
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
public static async Task RunAsync()
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var factory = new MqttClientFactory(logger);
|
||||
var factory = new MqttClientFactory(logger);
|
||||
|
||||
var client = factory.CreateMqttClient();
|
||||
var client = factory.CreateMqttClient();
|
||||
|
||||
var options = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.Build();
|
||||
var options = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.Build();
|
||||
|
||||
Console.WriteLine("BEFORE CONNECT");
|
||||
await client.ConnectAsync(options);
|
||||
Console.WriteLine("AFTER CONNECT");
|
||||
Console.WriteLine("BEFORE CONNECT");
|
||||
await client.ConnectAsync(options);
|
||||
Console.WriteLine("AFTER CONNECT");
|
||||
|
||||
Console.WriteLine("BEFORE SUBSCRIBE");
|
||||
await client.SubscribeAsync("test/topic");
|
||||
Console.WriteLine("AFTER SUBSCRIBE");
|
||||
Console.WriteLine("BEFORE SUBSCRIBE");
|
||||
await client.SubscribeAsync("test/topic");
|
||||
Console.WriteLine("AFTER SUBSCRIBE");
|
||||
|
||||
Console.WriteLine("BEFORE PUBLISH");
|
||||
await client.PublishStringAsync("test/topic", "payload");
|
||||
Console.WriteLine("AFTER PUBLISH");
|
||||
Console.WriteLine("BEFORE PUBLISH");
|
||||
await client.PublishStringAsync("test/topic", "payload");
|
||||
Console.WriteLine("AFTER PUBLISH");
|
||||
|
||||
await Task.Delay(1000);
|
||||
await Task.Delay(1000);
|
||||
|
||||
Console.WriteLine("BEFORE DISCONNECT");
|
||||
await client.DisconnectAsync();
|
||||
Console.WriteLine("AFTER DISCONNECT");
|
||||
Console.WriteLine("BEFORE DISCONNECT");
|
||||
await client.DisconnectAsync();
|
||||
Console.WriteLine("AFTER DISCONNECT");
|
||||
|
||||
Console.WriteLine("FINISHED");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
Console.WriteLine("FINISHED");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,99 +10,98 @@ using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class ClientTest
|
||||
{
|
||||
public static class ClientTest
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
public static async Task RunAsync()
|
||||
try
|
||||
{
|
||||
try
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var factory = new MqttClientFactory(logger);
|
||||
var client = factory.CreateMqttClient();
|
||||
var clientOptions = new MqttClientOptions
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var factory = new MqttClientFactory(logger);
|
||||
var client = factory.CreateMqttClient();
|
||||
var clientOptions = new MqttClientOptions
|
||||
ChannelOptions = new MqttClientTcpOptions
|
||||
{
|
||||
ChannelOptions = new MqttClientTcpOptions
|
||||
{
|
||||
RemoteEndpoint = new DnsEndPoint("127.0.0.1", 0)
|
||||
}
|
||||
};
|
||||
RemoteEndpoint = new DnsEndPoint("127.0.0.1", 0)
|
||||
}
|
||||
};
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.Payload.Length > 0)
|
||||
{
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.Payload.Length > 0)
|
||||
{
|
||||
payloadText = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
|
||||
}
|
||||
payloadText = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
|
||||
}
|
||||
|
||||
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
|
||||
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
|
||||
Console.WriteLine($"+ Payload = {payloadText}");
|
||||
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
|
||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
|
||||
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
|
||||
Console.WriteLine($"+ Payload = {payloadText}");
|
||||
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
|
||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||
Console.WriteLine();
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
client.ConnectedAsync += async e =>
|
||||
{
|
||||
Console.WriteLine("### CONNECTED WITH SERVER ###");
|
||||
client.ConnectedAsync += async _ =>
|
||||
{
|
||||
Console.WriteLine("### CONNECTED WITH SERVER ###");
|
||||
|
||||
await client.SubscribeAsync("#");
|
||||
await client.SubscribeAsync("#");
|
||||
|
||||
Console.WriteLine("### SUBSCRIBED ###");
|
||||
};
|
||||
Console.WriteLine("### SUBSCRIBED ###");
|
||||
};
|
||||
|
||||
client.DisconnectedAsync += async e =>
|
||||
{
|
||||
Console.WriteLine("### DISCONNECTED FROM SERVER ###");
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(clientOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("### RECONNECTING FAILED ###");
|
||||
}
|
||||
};
|
||||
client.DisconnectedAsync += async _ =>
|
||||
{
|
||||
Console.WriteLine("### DISCONNECTED FROM SERVER ###");
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(clientOptions);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("### CONNECTING FAILED ###" + Environment.NewLine + exception);
|
||||
Console.WriteLine("### RECONNECTING FAILED ###");
|
||||
}
|
||||
};
|
||||
|
||||
Console.WriteLine("### WAITING FOR APPLICATION MESSAGES ###");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.ReadLine();
|
||||
|
||||
await client.SubscribeAsync("test");
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("A/B/C")
|
||||
.WithPayload("Hello World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build();
|
||||
|
||||
await client.PublishAsync(applicationMessage);
|
||||
}
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(clientOptions);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
Console.WriteLine("### CONNECTING FAILED ###" + Environment.NewLine + exception);
|
||||
}
|
||||
|
||||
Console.WriteLine("### WAITING FOR APPLICATION MESSAGES ###");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.ReadLine();
|
||||
|
||||
await client.SubscribeAsync("test");
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("A/B/C")
|
||||
.WithPayload("Hello World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build();
|
||||
|
||||
await client.PublishAsync(applicationMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,342 +1,345 @@
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
/// <summary>
|
||||
/// Connect a number of publisher clients and one subscriber client, then publish messages and measure
|
||||
/// the number of messages per second that can be exchanged between publishers and subscriber.
|
||||
/// Measurements are performed for subscriptions containing no wildcard, a single wildcard or multiple wildcards.
|
||||
/// </summary>
|
||||
public class MessageThroughputTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Connect a number of publisher clients and one subscriber client, then publish messages and measure
|
||||
/// the number of messages per second that can be exchanged between publishers and subscriber.
|
||||
/// Measurements are performed for subscriptions containing no wildcard, a single wildcard or multiple wildcards.
|
||||
/// </summary>
|
||||
public class MessageThroughputTest
|
||||
// Change these constants to suit
|
||||
const int NumPublishers = 5000;
|
||||
const int NumTopicsPerPublisher = 10;
|
||||
|
||||
// Note: Other code changes may be required when changing this constant:
|
||||
const int NumSubscribers = 1; // Fixed
|
||||
|
||||
// Number of publish calls before a response for all published messages is awaited.
|
||||
// This must be limited to a reasonable value that the server or TCP pipeline can handle.
|
||||
const int NumPublishCallsPerBatch = 250;
|
||||
|
||||
// Message counters are set/reset in the PublishAllAsync loop
|
||||
int _messagesReceivedCount;
|
||||
int _messagesExpectedCount;
|
||||
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
MqttServer _mqttServer;
|
||||
Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName;
|
||||
List<IMqttClient> _mqttSubscriberClients;
|
||||
|
||||
Dictionary<string, List<string>> _topicsByPublisher;
|
||||
Dictionary<string, List<string>> _singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> _multiWildcardTopicsByPublisher;
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
// Change these constants to suit
|
||||
const int NumPublishers = 5000;
|
||||
const int NumTopicsPerPublisher = 10;
|
||||
|
||||
// Note: Other code changes may be required when changing this constant:
|
||||
const int NumSubscribers = 1; // Fixed
|
||||
|
||||
// Number of publish calls before a response for all published messages is awaited.
|
||||
// This must be limited to a reasonable value that the server or TCP pipeline can handle.
|
||||
const int NumPublishCallsPerBatch = 250;
|
||||
|
||||
// Message counters are set/reset in the PublishAllAsync loop
|
||||
int _messagesReceivedCount;
|
||||
int _messagesExpectedCount;
|
||||
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
MqttServer _mqttServer;
|
||||
Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName;
|
||||
List<IMqttClient> _mqttSubscriberClients;
|
||||
|
||||
Dictionary<string, List<string>> _topicsByPublisher;
|
||||
Dictionary<string, List<string>> _singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> _multiWildcardTopicsByPublisher;
|
||||
|
||||
public async Task Run()
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Begin message throughput test");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Begin message throughput test");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Number of publishers: " + NumPublishers);
|
||||
Console.WriteLine("Number of published topics (total): " + NumPublishers * NumTopicsPerPublisher);
|
||||
Console.WriteLine("Number of subscribers: " + NumSubscribers);
|
||||
Console.WriteLine("Number of publishers: " + NumPublishers);
|
||||
Console.WriteLine("Number of published topics (total): " + NumPublishers * NumTopicsPerPublisher);
|
||||
Console.WriteLine("Number of subscribers: " + NumSubscribers);
|
||||
|
||||
await Setup();
|
||||
await Setup();
|
||||
|
||||
await Subscribe_to_No_Wildcard_Topics();
|
||||
await Subscribe_to_Single_Wildcard_Topics();
|
||||
await Subscribe_to_Multi_Wildcard_Topics();
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("End message throughput test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleWriteLineError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Setup()
|
||||
{
|
||||
new TopicGenerator().Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out _singleWildcardTopicsByPublisher, out _multiWildcardTopicsByPublisher);
|
||||
|
||||
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
var mqttClientFactory = new MqttClientFactory();
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
_mqttServer = mqttServerFactory.CreateMqttServer(serverOptions);
|
||||
await _mqttServer.StartAsync();
|
||||
await Subscribe_to_No_Wildcard_Topics();
|
||||
await Subscribe_to_Single_Wildcard_Topics();
|
||||
await Subscribe_to_Multi_Wildcard_Topics();
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Begin connect " + NumPublishers + " publisher(s)...");
|
||||
Console.WriteLine("End message throughput test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleWriteLineError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
_mqttPublisherClientsByPublisherName = new Dictionary<string, IMqttClient>();
|
||||
foreach (var pt in _topicsByPublisher)
|
||||
{
|
||||
var publisherName = pt.Key;
|
||||
var mqttClient = mqttClientFactory.CreateMqttClient();
|
||||
var publisherOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithClientId(publisherName)
|
||||
.Build();
|
||||
await mqttClient.ConnectAsync(publisherOptions);
|
||||
_mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
|
||||
}
|
||||
stopWatch.Stop();
|
||||
public async Task Setup()
|
||||
{
|
||||
new TopicGenerator().Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out _singleWildcardTopicsByPublisher, out _multiWildcardTopicsByPublisher);
|
||||
|
||||
Console.Write($"{NumPublishers} publisher(s) connected in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"connections per second: {NumPublishers / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
var mqttClientFactory = new MqttClientFactory();
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
_mqttServer = mqttServerFactory.CreateMqttServer(serverOptions);
|
||||
await _mqttServer.StartAsync();
|
||||
|
||||
_mqttSubscriberClients = new List<IMqttClient>();
|
||||
for (var i = 0; i < NumSubscribers; ++i)
|
||||
{
|
||||
var mqttClient = mqttClientFactory.CreateMqttClient();
|
||||
var subsriberOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithClientId("sub" + i)
|
||||
.Build();
|
||||
await mqttClient.ConnectAsync(subsriberOptions);
|
||||
mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
|
||||
_mqttSubscriberClients.Add(mqttClient);
|
||||
}
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Begin connect " + NumPublishers + " publisher(s)...");
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
_mqttPublisherClientsByPublisherName = [];
|
||||
foreach (var pt in _topicsByPublisher)
|
||||
{
|
||||
var publisherName = pt.Key;
|
||||
var mqttClient = mqttClientFactory.CreateMqttClient();
|
||||
var publisherOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithClientId(publisherName)
|
||||
.Build();
|
||||
await mqttClient.ConnectAsync(publisherOptions);
|
||||
_mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
|
||||
}
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{NumPublishers} publisher(s) connected in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"connections per second: {NumPublishers / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
|
||||
_mqttSubscriberClients = new List<IMqttClient>();
|
||||
for (var i = 0; i < NumSubscribers; ++i)
|
||||
{
|
||||
var mqttClient = mqttClientFactory.CreateMqttClient();
|
||||
var subsriberOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithClientId("sub" + i)
|
||||
.Build();
|
||||
await mqttClient.ConnectAsync(subsriberOptions);
|
||||
mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
|
||||
_mqttSubscriberClients.Add(mqttClient);
|
||||
}
|
||||
}
|
||||
|
||||
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
// count messages and signal cancellation when expected message count is reached
|
||||
++_messagesReceivedCount;
|
||||
if (_messagesReceivedCount == _messagesExpectedCount)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public async Task Cleanup()
|
||||
{
|
||||
foreach (var mqttClient in _mqttSubscriberClients)
|
||||
{
|
||||
await mqttClient.DisconnectAsync();
|
||||
mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
|
||||
mqttClient.Dispose();
|
||||
}
|
||||
|
||||
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
foreach (var pub in _mqttPublisherClientsByPublisherName)
|
||||
{
|
||||
// count messages and signal cancellation when expected message count is reached
|
||||
++_messagesReceivedCount;
|
||||
if (_messagesReceivedCount == _messagesExpectedCount)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
return CompletedTask.Instance;
|
||||
var mqttClient = pub.Value;
|
||||
await mqttClient.DisconnectAsync();
|
||||
mqttClient.Dispose();
|
||||
}
|
||||
|
||||
public async Task Cleanup()
|
||||
await _mqttServer.StopAsync();
|
||||
_mqttServer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure no-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_No_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_topicsByPublisher, "no wildcards");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure single-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_Single_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_singleWildcardTopicsByPublisher, "single wildcard");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure multi-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_Multi_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_multiWildcardTopicsByPublisher, "multi wildcard");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to all topics, then run message exchange
|
||||
/// </summary>
|
||||
public async Task ProcessMessages(Dictionary<string, List<string>> topicsByPublisher, string topicTypeDescription)
|
||||
{
|
||||
var numTopics = CountTopics(topicsByPublisher);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.Write($"Subscribing to {numTopics} topics ");
|
||||
ConsoleWriteInfo($"({topicTypeDescription})");
|
||||
Console.WriteLine($" for {NumSubscribers} subscriber(s)...");
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
|
||||
// each subscriber subscribes to all topics, one call per topic
|
||||
foreach (var subscriber in _mqttSubscriberClients)
|
||||
{
|
||||
foreach (var mqttClient in _mqttSubscriberClients)
|
||||
{
|
||||
await mqttClient.DisconnectAsync();
|
||||
mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
|
||||
mqttClient.Dispose();
|
||||
}
|
||||
|
||||
foreach (var pub in _mqttPublisherClientsByPublisherName)
|
||||
{
|
||||
var mqttClient = pub.Value;
|
||||
await mqttClient.DisconnectAsync();
|
||||
mqttClient.Dispose();
|
||||
}
|
||||
|
||||
await _mqttServer.StopAsync();
|
||||
_mqttServer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure no-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_No_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_topicsByPublisher, "no wildcards");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure single-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_Single_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_singleWildcardTopicsByPublisher, "single wildcard");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measure multi-wildcard topic subscription message exchange performance
|
||||
/// </summary>
|
||||
public Task Subscribe_to_Multi_Wildcard_Topics()
|
||||
{
|
||||
return ProcessMessages(_multiWildcardTopicsByPublisher, "multi wildcard");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to all topics, then run message exchange
|
||||
/// </summary>
|
||||
public async Task ProcessMessages(Dictionary<string, List<string>> topicsByPublisher, string topicTypeDescription)
|
||||
{
|
||||
var numTopics = CountTopics(topicsByPublisher);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.Write($"Subscribing to {numTopics} topics ");
|
||||
ConsoleWriteInfo($"({topicTypeDescription})");
|
||||
Console.WriteLine($" for {NumSubscribers} subscriber(s)...");
|
||||
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
|
||||
// each subscriber subscribes to all topics, one call per topic
|
||||
foreach (var subscriber in _mqttSubscriberClients)
|
||||
{
|
||||
foreach (var tp in topicsByPublisher)
|
||||
{
|
||||
var topics = tp.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
var topicFilter = new Packets.MqttTopicFilter() { Topic = topic };
|
||||
await subscriber.SubscribeAsync(topicFilter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{NumSubscribers} subscriber(s) subscribed in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"subscribe calls per second: {numTopics / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
|
||||
await PublishAllAsync();
|
||||
|
||||
Console.WriteLine($"Unsubscribing {numTopics} topics ({topicTypeDescription}) for {NumSubscribers} subscriber(s)...");
|
||||
|
||||
stopWatch.Restart();
|
||||
|
||||
// unsubscribe to all topics, one call per topic
|
||||
foreach (var subscriber in _mqttSubscriberClients)
|
||||
{
|
||||
foreach (var tp in topicsByPublisher)
|
||||
{
|
||||
var topics = tp.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
var topicFilter = new Packets.MqttTopicFilter() { Topic = topic };
|
||||
|
||||
MqttClientUnsubscribeOptions options =
|
||||
new MqttClientUnsubscribeOptionsBuilder()
|
||||
.WithTopicFilter(topicFilter)
|
||||
.Build();
|
||||
await subscriber.UnsubscribeAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{NumSubscribers} subscriber(s) unsubscribed in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"unsubscribe calls per second: {numTopics / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish messages in batches of NumPublishCallsPerBatch, wait for messages sent to subscriber
|
||||
/// </summary>
|
||||
async Task PublishAllAsync()
|
||||
{
|
||||
Console.WriteLine("Begin message exchange...");
|
||||
|
||||
int publisherIndexCounter = 0; // index to loop around all publishers to publish
|
||||
int topicIndexCounter = 0; // index to loop around all topics to publish
|
||||
int totalNumMessagesReceived = 0;
|
||||
|
||||
var publisherNames = _topicsByPublisher.Keys.ToList();
|
||||
|
||||
// There should be one message received per publish
|
||||
_messagesExpectedCount = NumPublishCallsPerBatch;
|
||||
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
|
||||
// Loop for a while and exchange messages
|
||||
|
||||
while (stopWatch.ElapsedMilliseconds < 10000)
|
||||
{
|
||||
_messagesReceivedCount = 0;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
for (var publishCallCount = 0; publishCallCount < NumPublishCallsPerBatch; ++publishCallCount)
|
||||
{
|
||||
// pick a publisher
|
||||
var publisherName = publisherNames[publisherIndexCounter % publisherNames.Count];
|
||||
var publisherTopics = _topicsByPublisher[publisherName];
|
||||
// pick a publisher topic
|
||||
var topic = publisherTopics[topicIndexCounter % publisherTopics.Count];
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.Build();
|
||||
await _mqttPublisherClientsByPublisherName[publisherName].PublishAsync(message).ConfigureAwait(false);
|
||||
++topicIndexCounter;
|
||||
++publisherIndexCounter;
|
||||
}
|
||||
|
||||
// Wait for at least one message per publish to be received by subscriber (in the subscriber's application message handler),
|
||||
// then loop around to send another batch
|
||||
try
|
||||
{
|
||||
await Task.Delay(30000, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Dispose();
|
||||
|
||||
if (_messagesReceivedCount < _messagesExpectedCount)
|
||||
{
|
||||
ConsoleWriteLineError(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
|
||||
return;
|
||||
}
|
||||
|
||||
totalNumMessagesReceived += _messagesReceivedCount;
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{totalNumMessagesReceived} messages published and received in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
ConsoleWriteLineSuccess($"messages per second: {(int)(totalNumMessagesReceived / (stopWatch.ElapsedMilliseconds / 1000.0))}");
|
||||
}
|
||||
|
||||
int CountTopics(Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var tp in topicsByPublisher)
|
||||
{
|
||||
count += tp.Value.Count;
|
||||
var topics = tp.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
var topicFilter = new Packets.MqttTopicFilter() { Topic = topic };
|
||||
await subscriber.SubscribeAsync(topicFilter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void ConsoleWriteLineError(string message)
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{NumSubscribers} subscriber(s) subscribed in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"subscribe calls per second: {numTopics / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
|
||||
await PublishAllAsync();
|
||||
|
||||
Console.WriteLine($"Unsubscribing {numTopics} topics ({topicTypeDescription}) for {NumSubscribers} subscriber(s)...");
|
||||
|
||||
stopWatch.Restart();
|
||||
|
||||
// unsubscribe to all topics, one call per topic
|
||||
foreach (var subscriber in _mqttSubscriberClients)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
foreach (var tp in topicsByPublisher)
|
||||
{
|
||||
var topics = tp.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
var topicFilter = new Packets.MqttTopicFilter { Topic = topic };
|
||||
|
||||
var options =
|
||||
new MqttClientUnsubscribeOptionsBuilder()
|
||||
.WithTopicFilter(topicFilter)
|
||||
.Build();
|
||||
await subscriber.UnsubscribeAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWriteLineSuccess(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
void ConsoleWriteInfo(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.Write(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{NumSubscribers} subscriber(s) unsubscribed in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
Console.WriteLine($"unsubscribe calls per second: {numTopics / (stopWatch.ElapsedMilliseconds / 1000.0):0.000}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish messages in batches of NumPublishCallsPerBatch, wait for messages sent to subscriber
|
||||
/// </summary>
|
||||
async Task PublishAllAsync()
|
||||
{
|
||||
Console.WriteLine("Begin message exchange...");
|
||||
|
||||
var publisherIndexCounter = 0; // index to loop around all publishers to publish
|
||||
var topicIndexCounter = 0; // index to loop around all topics to publish
|
||||
var totalNumMessagesReceived = 0;
|
||||
|
||||
var publisherNames = _topicsByPublisher.Keys.ToList();
|
||||
|
||||
// There should be one message received per publish
|
||||
_messagesExpectedCount = NumPublishCallsPerBatch;
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
|
||||
// Loop for a while and exchange messages
|
||||
|
||||
while (stopWatch.ElapsedMilliseconds < 10000)
|
||||
{
|
||||
_messagesReceivedCount = 0;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
for (var publishCallCount = 0; publishCallCount < NumPublishCallsPerBatch; ++publishCallCount)
|
||||
{
|
||||
// pick a publisher
|
||||
var publisherName = publisherNames[publisherIndexCounter % publisherNames.Count];
|
||||
var publisherTopics = _topicsByPublisher[publisherName];
|
||||
// pick a publisher topic
|
||||
var topic = publisherTopics[topicIndexCounter % publisherTopics.Count];
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.Build();
|
||||
|
||||
await _mqttPublisherClientsByPublisherName[publisherName].PublishAsync(message).ConfigureAwait(false);
|
||||
++topicIndexCounter;
|
||||
++publisherIndexCounter;
|
||||
}
|
||||
|
||||
// Wait for at least one message per publish to be received by subscriber (in the subscriber's application message handler),
|
||||
// then loop around to send another batch
|
||||
try
|
||||
{
|
||||
await Task.Delay(30000, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Write(exception.ToString());
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Dispose();
|
||||
|
||||
if (_messagesReceivedCount < _messagesExpectedCount)
|
||||
{
|
||||
ConsoleWriteLineError($"Messages Received Count mismatch, expected {_messagesExpectedCount}, received {_messagesReceivedCount}");
|
||||
return;
|
||||
}
|
||||
|
||||
totalNumMessagesReceived += _messagesReceivedCount;
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write($"{totalNumMessagesReceived} messages published and received in {stopWatch.ElapsedMilliseconds / 1000.0:0.000} seconds, ");
|
||||
ConsoleWriteLineSuccess($"messages per second: {(int)(totalNumMessagesReceived / (stopWatch.ElapsedMilliseconds / 1000.0))}");
|
||||
}
|
||||
|
||||
static int CountTopics(Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var tp in topicsByPublisher)
|
||||
{
|
||||
count += tp.Value.Count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void ConsoleWriteLineError(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
static void ConsoleWriteLineSuccess(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
static void ConsoleWriteInfo(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.Write(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,58 +6,57 @@ using System;
|
||||
using System.Text;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class MqttNetConsoleLogger
|
||||
{
|
||||
public static class MqttNetConsoleLogger
|
||||
static readonly object Lock = new();
|
||||
|
||||
public static void ForwardToConsole(MqttNetEventLogger logger)
|
||||
{
|
||||
static readonly object _lock = new object();
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
public static void ForwardToConsole(MqttNetEventLogger logger)
|
||||
logger.LogMessagePublished -= PrintToConsole;
|
||||
logger.LogMessagePublished += PrintToConsole;
|
||||
}
|
||||
|
||||
public static void PrintToConsole(string message, ConsoleColor color)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
logger.LogMessagePublished -= PrintToConsole;
|
||||
logger.LogMessagePublished += PrintToConsole;
|
||||
}
|
||||
|
||||
public static void PrintToConsole(string message, ConsoleColor color)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var backupColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(message);
|
||||
Console.ForegroundColor = backupColor;
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintToConsole(object sender, MqttNetLogMessagePublishedEventArgs e)
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
output.AppendLine($">> [{e.LogMessage.Timestamp:O}] [{e.LogMessage.ThreadId}] [{e.LogMessage.Source}] [{e.LogMessage.Level}]: {e.LogMessage.Message}");
|
||||
if (e.LogMessage.Exception != null)
|
||||
{
|
||||
output.AppendLine(e.LogMessage.Exception.ToString());
|
||||
}
|
||||
|
||||
var color = ConsoleColor.Red;
|
||||
switch (e.LogMessage.Level)
|
||||
{
|
||||
case MqttNetLogLevel.Error:
|
||||
color = ConsoleColor.Red;
|
||||
break;
|
||||
case MqttNetLogLevel.Warning:
|
||||
color = ConsoleColor.Yellow;
|
||||
break;
|
||||
case MqttNetLogLevel.Info:
|
||||
color = ConsoleColor.Green;
|
||||
break;
|
||||
case MqttNetLogLevel.Verbose:
|
||||
color = ConsoleColor.Gray;
|
||||
break;
|
||||
}
|
||||
|
||||
PrintToConsole(output.ToString(), color);
|
||||
var backupColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(message);
|
||||
Console.ForegroundColor = backupColor;
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintToConsole(object sender, MqttNetLogMessagePublishedEventArgs e)
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
output.AppendLine($">> [{e.LogMessage.Timestamp:O}] [{e.LogMessage.ThreadId}] [{e.LogMessage.Source}] [{e.LogMessage.Level}]: {e.LogMessage.Message}");
|
||||
if (e.LogMessage.Exception != null)
|
||||
{
|
||||
output.AppendLine(e.LogMessage.Exception.ToString());
|
||||
}
|
||||
|
||||
var color = ConsoleColor.Red;
|
||||
switch (e.LogMessage.Level)
|
||||
{
|
||||
case MqttNetLogLevel.Error:
|
||||
color = ConsoleColor.Red;
|
||||
break;
|
||||
case MqttNetLogLevel.Warning:
|
||||
color = ConsoleColor.Yellow;
|
||||
break;
|
||||
case MqttNetLogLevel.Info:
|
||||
color = ConsoleColor.Green;
|
||||
break;
|
||||
case MqttNetLogLevel.Verbose:
|
||||
color = ConsoleColor.Gray;
|
||||
break;
|
||||
}
|
||||
|
||||
PrintToConsole(output.ToString(), color);
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -10,173 +10,172 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class PublicBrokerTest
|
||||
{
|
||||
public static class PublicBrokerTest
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
public static async Task RunAsync()
|
||||
// TLS13 is only available in Net5.0
|
||||
var unsafeTls13 = new MqttClientTlsOptions
|
||||
{
|
||||
// TLS13 is only available in Net5.0
|
||||
var unsafeTls13 = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls13,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls13,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
|
||||
// Also defining TLS12 for servers that don't seem no to support TLS13.
|
||||
var unsafeTls12 = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls12,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
|
||||
// mqtt.eclipseprojects.io
|
||||
await ExecuteTestsAsync(
|
||||
"mqtt.eclipseprojects.io TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"mqtt.eclipseprojects.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:80/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync("mqtt.eclipseprojects.io WS TLS13",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:443/mqtt"))
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
// test.mosquitto.org
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP - Authenticated",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884).WithCredentials("rw", "readwrite").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
await ExecuteTestsAsync("test.mosquitto.org TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP TLS12 - Authenticated",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
|
||||
.WithCredentials("rw", "readwrite")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTlsOptions(unsafeTls12)
|
||||
.Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8080/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8081/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
// broker.emqx.io
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
await ExecuteTestsAsync("broker.emqx.io TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8083/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8084/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
// broker.hivemq.com
|
||||
await ExecuteTestsAsync(
|
||||
"broker.hivemq.com TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.hivemq.com WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
// mqtt.swifitch.cz: Does not seem to operate any more
|
||||
// cloudmqtt.com: Cannot test because it does not offer a free plan any more.
|
||||
|
||||
Write("Finished.", ConsoleColor.White);
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static async Task ExecuteTestsAsync(string name, MqttClientOptions options)
|
||||
// Also defining TLS12 for servers that don't seem no to support TLS13.
|
||||
var unsafeTls12 = new MqttClientTlsOptions
|
||||
{
|
||||
options.ProtocolVersion = MqttProtocolVersion.V311;
|
||||
await ExecuteTestAsync(name + " V3.1.1", options);
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls12,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
|
||||
options.ProtocolVersion = MqttProtocolVersion.V500;
|
||||
await ExecuteTestAsync(name + " V5.0.0", options);
|
||||
}
|
||||
// mqtt.eclipseprojects.io
|
||||
await ExecuteTestsAsync(
|
||||
"mqtt.eclipseprojects.io TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
static async Task ExecuteTestAsync(string name, MqttClientOptions options)
|
||||
await ExecuteTestsAsync(
|
||||
"mqtt.eclipseprojects.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:80/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync("mqtt.eclipseprojects.io WS TLS13",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:443/mqtt"))
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
// test.mosquitto.org
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP - Authenticated",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884).WithCredentials("rw", "readwrite").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
await ExecuteTestsAsync("test.mosquitto.org TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org TCP TLS12 - Authenticated",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
|
||||
.WithCredentials("rw", "readwrite")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTlsOptions(unsafeTls12)
|
||||
.Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8080/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"test.mosquitto.org WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8081/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
// broker.emqx.io
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
await ExecuteTestsAsync("broker.emqx.io TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8083/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.emqx.io WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8084/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
// broker.hivemq.com
|
||||
await ExecuteTestsAsync(
|
||||
"broker.hivemq.com TCP",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestsAsync(
|
||||
"broker.hivemq.com WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
// mqtt.swifitch.cz: Does not seem to operate any more
|
||||
// cloudmqtt.com: Cannot test because it does not offer a free plan any more.
|
||||
|
||||
Write("Finished.", ConsoleColor.White);
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static async Task ExecuteTestsAsync(string name, MqttClientOptions options)
|
||||
{
|
||||
options.ProtocolVersion = MqttProtocolVersion.V311;
|
||||
await ExecuteTestAsync(name + " V3.1.1", options);
|
||||
|
||||
options.ProtocolVersion = MqttProtocolVersion.V500;
|
||||
await ExecuteTestAsync(name + " V5.0.0", options);
|
||||
}
|
||||
|
||||
static async Task ExecuteTestAsync(string name, MqttClientOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
Write("Testing '" + name + "'... ", ConsoleColor.Gray);
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
Write("Testing '" + name + "'... ", ConsoleColor.Gray);
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
|
||||
using (var client = factory.CreateMqttClient())
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
|
||||
var topic = Guid.NewGuid().ToString();
|
||||
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
||||
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
||||
var topic = Guid.NewGuid().ToString();
|
||||
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
||||
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
|
||||
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
|
||||
|
||||
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
|
||||
{
|
||||
throw new Exception("Message invalid.");
|
||||
}
|
||||
|
||||
await client.UnsubscribeAsync(topic).ConfigureAwait(false);
|
||||
await client.DisconnectAsync().ConfigureAwait(false);
|
||||
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
|
||||
{
|
||||
throw new Exception("Message invalid.");
|
||||
}
|
||||
|
||||
Write("[OK]\n", ConsoleColor.Green);
|
||||
await client.UnsubscribeAsync(topic).ConfigureAwait(false);
|
||||
await client.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Write("[FAILED]" + Environment.NewLine, ConsoleColor.Red);
|
||||
Write(exception + Environment.NewLine, ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
|
||||
static void Write(string message, ConsoleColor color)
|
||||
Write("[OK]\n", ConsoleColor.Green);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(message);
|
||||
Write("[FAILED]" + Environment.NewLine, ConsoleColor.Red);
|
||||
Write(exception + Environment.NewLine, ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
|
||||
static void Write(string message, ConsoleColor color)
|
||||
{
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(message);
|
||||
}
|
||||
}
|
||||
@@ -7,26 +7,25 @@ using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class ServerAndClientTest
|
||||
{
|
||||
public static class ServerAndClientTest
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
var mqttClientFactory = new MqttClientFactory(logger);
|
||||
var server = mqttServerFactory.CreateMqttServer( new MqttServerOptionsBuilder().Build());
|
||||
var client = mqttClientFactory.CreateMqttClient();
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
var mqttClientFactory = new MqttClientFactory(logger);
|
||||
var server = mqttServerFactory.CreateMqttServer( new MqttServerOptionsBuilder().Build());
|
||||
var client = mqttClientFactory.CreateMqttClient();
|
||||
|
||||
await server.StartAsync();
|
||||
await server.StartAsync();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
await client.ConnectAsync(clientOptions);
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
await client.ConnectAsync(clientOptions);
|
||||
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,189 +13,188 @@ using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public static class ServerTest
|
||||
{
|
||||
public static class ServerTest
|
||||
public static void RunEmptyServer()
|
||||
{
|
||||
public static void RunEmptyServer()
|
||||
{
|
||||
var mqttServer = new MqttServerFactory().CreateMqttServer(new MqttServerOptions());
|
||||
mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
var mqttServer = new MqttServerFactory().CreateMqttServer(new MqttServerOptions());
|
||||
mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
public static void RunEmptyServerWithLogging()
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory(logger);
|
||||
var mqttServer = mqttServerFactory.CreateMqttServer(new MqttServerOptions());
|
||||
mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new MqttServerOptionsBuilder()
|
||||
.WithDefaultEndpoint()
|
||||
.Build();
|
||||
|
||||
// Extend the timestamp for all messages from clients.
|
||||
// Protect several topics from being subscribed from every client.
|
||||
|
||||
//var certificate = new X509Certificate(@"C:\certs\test\test.cer", "");
|
||||
//options.TlsEndpointOptions.Certificate = certificate.Export(X509ContentType.Cert);
|
||||
//options.ConnectionBacklog = 5;
|
||||
//options.DefaultEndpointOptions.IsEnabled = true;
|
||||
//options.TlsEndpointOptions.IsEnabled = false;
|
||||
|
||||
var mqttServer = new MqttServerFactory().CreateMqttServer(options);
|
||||
|
||||
const string Filename = "C:\\MQTT\\RetainedMessages.json";
|
||||
|
||||
mqttServer.RetainedMessageChangedAsync += e =>
|
||||
{
|
||||
var directory = Path.GetDirectoryName(Filename);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.RetainedMessagesClearedAsync += e =>
|
||||
{
|
||||
File.Delete(Filename);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||
{
|
||||
List<MqttApplicationMessage> retainedMessages;
|
||||
if (File.Exists(Filename))
|
||||
{
|
||||
var json = File.ReadAllText(Filename);
|
||||
retainedMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
retainedMessages = new List<MqttApplicationMessage>();
|
||||
}
|
||||
|
||||
e.LoadedRetainedMessages = retainedMessages;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
{
|
||||
if (MqttTopicFilterComparer.Compare(e.ApplicationMessage.Topic, "/myTopic/WithTimestamp/#") == MqttTopicFilterCompareResult.IsMatch)
|
||||
{
|
||||
// Replace the payload with the timestamp. But also extending a JSON
|
||||
// based payload with the timestamp is a suitable use case.
|
||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")));
|
||||
}
|
||||
|
||||
if (e.ApplicationMessage.Topic == "not_allowed_topic")
|
||||
{
|
||||
e.ProcessPublish = false;
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.ClientId == "SpecialClient")
|
||||
{
|
||||
if (e.UserName != "USER" || e.Password != "PASS")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic.StartsWith("admin/foo/bar") && e.ClientId != "theAdmin")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
}
|
||||
|
||||
if (e.TopicFilter.Topic.StartsWith("the/secret/stuff") && e.ClientId != "Imperator")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
{
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.Payload.Length > 0)
|
||||
{
|
||||
payloadText = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
|
||||
}
|
||||
|
||||
MqttNetConsoleLogger.PrintToConsole($"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{payloadText}'", ConsoleColor.Magenta);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
//options.ApplicationMessageInterceptor = c =>
|
||||
//{
|
||||
// if (c.ApplicationMessage.Payload == null || c.ApplicationMessage.Payload.Length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// var content = JObject.Parse(Encoding.UTF8.GetString(c.ApplicationMessage.Payload));
|
||||
// var timestampProperty = content.Property("timestamp");
|
||||
// if (timestampProperty != null && timestampProperty.Value.Type == JTokenType.Null)
|
||||
// {
|
||||
// timestampProperty.Value = DateTime.Now.ToString("O");
|
||||
// c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString());
|
||||
// }
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// }
|
||||
//};
|
||||
|
||||
mqttServer.ClientConnectedAsync += e =>
|
||||
{
|
||||
Console.Write("Client disconnected event fired.");
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static void RunEmptyServerWithLogging()
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
MqttNetConsoleLogger.ForwardToConsole(logger);
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory(logger);
|
||||
var mqttServer = mqttServerFactory.CreateMqttServer(new MqttServerOptions());
|
||||
mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new MqttServerOptionsBuilder()
|
||||
.WithDefaultEndpoint()
|
||||
.Build();
|
||||
|
||||
// Extend the timestamp for all messages from clients.
|
||||
// Protect several topics from being subscribed from every client.
|
||||
|
||||
//var certificate = new X509Certificate(@"C:\certs\test\test.cer", "");
|
||||
//options.TlsEndpointOptions.Certificate = certificate.Export(X509ContentType.Cert);
|
||||
//options.ConnectionBacklog = 5;
|
||||
//options.DefaultEndpointOptions.IsEnabled = true;
|
||||
//options.TlsEndpointOptions.IsEnabled = false;
|
||||
|
||||
var mqttServer = new MqttServerFactory().CreateMqttServer(options);
|
||||
|
||||
const string filename = @"C:\MQTT\RetainedMessages.json";
|
||||
|
||||
mqttServer.RetainedMessageChangedAsync += e =>
|
||||
{
|
||||
var directory = Path.GetDirectoryName(filename);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory!);
|
||||
}
|
||||
|
||||
File.WriteAllText(filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.RetainedMessagesClearedAsync += _ =>
|
||||
{
|
||||
File.Delete(filename);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||
{
|
||||
List<MqttApplicationMessage> retainedMessages;
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
var json = File.ReadAllText(filename);
|
||||
retainedMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
retainedMessages = new List<MqttApplicationMessage>();
|
||||
}
|
||||
|
||||
e.LoadedRetainedMessages = retainedMessages;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
{
|
||||
if (MqttTopicFilterComparer.Compare(e.ApplicationMessage.Topic, "/myTopic/WithTimestamp/#") == MqttTopicFilterCompareResult.IsMatch)
|
||||
{
|
||||
// Replace the payload with the timestamp. But also extending a JSON
|
||||
// based payload with the timestamp is a suitable use case.
|
||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")));
|
||||
}
|
||||
|
||||
if (e.ApplicationMessage.Topic == "not_allowed_topic")
|
||||
{
|
||||
e.ProcessPublish = false;
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.ClientId == "SpecialClient")
|
||||
{
|
||||
if (e.UserName != "USER" || e.Password != "PASS")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic.StartsWith("admin/foo/bar") && e.ClientId != "theAdmin")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
}
|
||||
|
||||
if (e.TopicFilter.Topic.StartsWith("the/secret/stuff") && e.ClientId != "Imperator")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
e.CloseConnection = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
{
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.Payload.Length > 0)
|
||||
{
|
||||
payloadText = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
|
||||
}
|
||||
|
||||
MqttNetConsoleLogger.PrintToConsole($"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{payloadText}'", ConsoleColor.Magenta);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
//options.ApplicationMessageInterceptor = c =>
|
||||
//{
|
||||
// if (c.ApplicationMessage.Payload == null || c.ApplicationMessage.Payload.Length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// var content = JObject.Parse(Encoding.UTF8.GetString(c.ApplicationMessage.Payload));
|
||||
// var timestampProperty = content.Property("timestamp");
|
||||
// if (timestampProperty != null && timestampProperty.Value.Type == JTokenType.Null)
|
||||
// {
|
||||
// timestampProperty.Value = DateTime.Now.ToString("O");
|
||||
// c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString());
|
||||
// }
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// }
|
||||
//};
|
||||
|
||||
mqttServer.ClientConnectedAsync += _ =>
|
||||
{
|
||||
Console.Write("Client disconnected event fired.");
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
@@ -2,97 +2,96 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Extensions.TopicTemplate;
|
||||
|
||||
namespace MQTTnet.TestApp
|
||||
namespace MQTTnet.TestApp;
|
||||
|
||||
public class TopicGenerator
|
||||
{
|
||||
public class TopicGenerator
|
||||
public void Generate(
|
||||
int numPublishers, int numTopicsPerPublisher,
|
||||
out Dictionary<string, List<string>> topicsByPublisher,
|
||||
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
||||
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher
|
||||
)
|
||||
{
|
||||
public void Generate(
|
||||
int numPublishers, int numTopicsPerPublisher,
|
||||
out Dictionary<string, List<string>> topicsByPublisher,
|
||||
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
||||
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher
|
||||
)
|
||||
topicsByPublisher = new Dictionary<string, List<string>>();
|
||||
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
|
||||
MqttTopicTemplate baseTemplate = new MqttTopicTemplate("{publisher}/{building}/{level}/{sensor}");
|
||||
|
||||
// Find some reasonable distribution across three topic levels
|
||||
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
|
||||
|
||||
int numLevel1Topics = topicsPerLevel;
|
||||
if (numLevel1Topics <= 0)
|
||||
{
|
||||
topicsByPublisher = new Dictionary<string, List<string>>();
|
||||
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
numLevel1Topics = 1;
|
||||
}
|
||||
int numLevel2Topics = topicsPerLevel;
|
||||
if (numLevel2Topics <= 0)
|
||||
{
|
||||
numLevel2Topics = 1;
|
||||
}
|
||||
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
||||
if (maxNumLevel3Topics <= 0)
|
||||
{
|
||||
maxNumLevel3Topics = 1;
|
||||
}
|
||||
for (var p = 0; p < numPublishers; ++p)
|
||||
{
|
||||
int publisherTopicCount = 0;
|
||||
|
||||
MqttTopicTemplate baseTemplate = new MqttTopicTemplate("{publisher}/{building}/{level}/{sensor}");
|
||||
|
||||
// Find some reasonable distribution across three topic levels
|
||||
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
|
||||
|
||||
int numLevel1Topics = topicsPerLevel;
|
||||
if (numLevel1Topics <= 0)
|
||||
var publisherName = "pub" + p;
|
||||
var publisherTemplate = baseTemplate
|
||||
.WithParameter("publisher", publisherName);
|
||||
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
||||
{
|
||||
numLevel1Topics = 1;
|
||||
}
|
||||
int numLevel2Topics = topicsPerLevel;
|
||||
if (numLevel2Topics <= 0)
|
||||
{
|
||||
numLevel2Topics = 1;
|
||||
}
|
||||
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
||||
if (maxNumLevel3Topics <= 0)
|
||||
{
|
||||
maxNumLevel3Topics = 1;
|
||||
}
|
||||
for (var p = 0; p < numPublishers; ++p)
|
||||
{
|
||||
int publisherTopicCount = 0;
|
||||
|
||||
var publisherName = "pub" + p;
|
||||
var publisherTemplate = baseTemplate
|
||||
.WithParameter("publisher", publisherName);
|
||||
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
||||
var l1Template = publisherTemplate
|
||||
.WithParameter("building", "building" + (l1 + 1));
|
||||
for (var l2 = 0; l2 < numLevel2Topics; ++l2)
|
||||
{
|
||||
var l1Template = publisherTemplate
|
||||
.WithParameter("building", "building" + (l1 + 1));
|
||||
for (var l2 = 0; l2 < numLevel2Topics; ++l2)
|
||||
var l2Template = l1Template
|
||||
.WithParameter("level", "level" + (l2 + 1));
|
||||
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
||||
{
|
||||
var l2Template = l1Template
|
||||
.WithParameter("level", "level" + (l2 + 1));
|
||||
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
||||
{
|
||||
if (publisherTopicCount >= numTopicsPerPublisher)
|
||||
break;
|
||||
if (publisherTopicCount >= numTopicsPerPublisher)
|
||||
break;
|
||||
|
||||
var topic = l2Template
|
||||
var topic = l2Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, topic, topicsByPublisher);
|
||||
|
||||
if (l2 == 0)
|
||||
{
|
||||
var singleWildcardTopic = l1Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, topic, topicsByPublisher);
|
||||
|
||||
if (l2 == 0)
|
||||
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
||||
if (l1 == 0)
|
||||
{
|
||||
var singleWildcardTopic = l1Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
||||
if (l1 == 0)
|
||||
{
|
||||
var multiWildcardTopic = publisherTemplate
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
||||
}
|
||||
var multiWildcardTopic = publisherTemplate
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
||||
}
|
||||
|
||||
++publisherTopicCount;
|
||||
}
|
||||
|
||||
++publisherTopicCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddPublisherTopic(string publisherName, string topic, Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
List<string> topicList;
|
||||
if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByPublisher.Add(publisherName, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddPublisherTopic(string publisherName, string topic, Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
List<string> topicList;
|
||||
if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByPublisher.Add(publisherName, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user