1 vor 2 Jahren
Ursprung
Commit
ade853da23
100 geänderte Dateien mit 33150 neuen und 10 gelöschten Zeilen
  1. +12
    -0
      BAP.MQTTnet.Test/BAP.MQTTnet.Test.csproj
  2. +275
    -0
      BAP.MQTTnet.Test/Client/Client_Connection_Samples.cs
  3. +46
    -0
      BAP.MQTTnet.Test/Client/Client_Publish_Samples.cs
  4. +83
    -0
      BAP.MQTTnet.Test/Client/Client_Subscribe_Samples.cs
  5. +153
    -0
      BAP.MQTTnet.Test/Controllers/HomeController.cs
  6. +11
    -0
      BAP.MQTTnet.Test/Models/ErrorViewModel.cs
  7. +433
    -0
      BAP.MQTTnet.Test/MqttClient.cs
  8. +26
    -0
      BAP.MQTTnet.Test/Program.cs
  9. +53
    -0
      BAP.MQTTnet.Test/Startup.cs
  10. +8
    -0
      BAP.MQTTnet.Test/Views/Home/Index.cshtml
  11. +6
    -0
      BAP.MQTTnet.Test/Views/Home/Privacy.cshtml
  12. +25
    -0
      BAP.MQTTnet.Test/Views/Shared/Error.cshtml
  13. +48
    -0
      BAP.MQTTnet.Test/Views/Shared/_Layout.cshtml
  14. +2
    -0
      BAP.MQTTnet.Test/Views/Shared/_ValidationScriptsPartial.cshtml
  15. +3
    -0
      BAP.MQTTnet.Test/Views/_ViewImports.cshtml
  16. +3
    -0
      BAP.MQTTnet.Test/Views/_ViewStart.cshtml
  17. +9
    -0
      BAP.MQTTnet.Test/appsettings.Development.json
  18. +10
    -0
      BAP.MQTTnet.Test/appsettings.json
  19. +71
    -0
      BAP.MQTTnet.Test/wwwroot/css/site.css
  20. BIN
     
  21. +22
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/LICENSE
  22. +3719
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
  23. +7
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
  24. +331
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
  25. +8
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
  26. +10038
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.css
  27. +7
    -0
      BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
  28. +12
    -0
      BAP.MQTTnet.Test/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt
  29. +22
    -0
      BAP.MQTTnet.Test/wwwroot/lib/jquery-validation/LICENSE.md
  30. +36
    -0
      BAP.MQTTnet.Test/wwwroot/lib/jquery/LICENSE.txt
  31. +11
    -0
      BPA.AspNetCore.Test/BPA.AspNetCore.Test.csproj
  32. +275
    -0
      BPA.AspNetCore.Test/Client/Client_Connection_Samples.cs
  33. +46
    -0
      BPA.AspNetCore.Test/Client/Client_Publish_Samples.cs
  34. +83
    -0
      BPA.AspNetCore.Test/Client/Client_Subscribe_Samples.cs
  35. +26
    -0
      BPA.AspNetCore.Test/Pages/Error.cshtml
  36. +31
    -0
      BPA.AspNetCore.Test/Pages/Error.cshtml.cs
  37. +10
    -0
      BPA.AspNetCore.Test/Pages/Index.cshtml
  38. +25
    -0
      BPA.AspNetCore.Test/Pages/Index.cshtml.cs
  39. +8
    -0
      BPA.AspNetCore.Test/Pages/Privacy.cshtml
  40. +24
    -0
      BPA.AspNetCore.Test/Pages/Privacy.cshtml.cs
  41. +50
    -0
      BPA.AspNetCore.Test/Pages/Shared/_Layout.cshtml
  42. +2
    -0
      BPA.AspNetCore.Test/Pages/Shared/_ValidationScriptsPartial.cshtml
  43. +3
    -0
      BPA.AspNetCore.Test/Pages/_ViewImports.cshtml
  44. +3
    -0
      BPA.AspNetCore.Test/Pages/_ViewStart.cshtml
  45. +26
    -0
      BPA.AspNetCore.Test/Program.cs
  46. +56
    -0
      BPA.AspNetCore.Test/Startup.cs
  47. +9
    -0
      BPA.AspNetCore.Test/appsettings.Development.json
  48. +10
    -0
      BPA.AspNetCore.Test/appsettings.json
  49. +71
    -0
      BPA.AspNetCore.Test/wwwroot/css/site.css
  50. BIN
     
  51. +22
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/LICENSE
  52. +3719
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
  53. +7
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
  54. +331
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
  55. +8
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
  56. +10038
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.css
  57. +7
    -0
      BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
  58. +12
    -0
      BPA.AspNetCore.Test/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt
  59. +22
    -0
      BPA.AspNetCore.Test/wwwroot/lib/jquery-validation/LICENSE.md
  60. +36
    -0
      BPA.AspNetCore.Test/wwwroot/lib/jquery/LICENSE.txt
  61. +32
    -9
      MQTTnet.sln
  62. +2
    -1
      Samples/Client/Client_Subscribe_Samples.cs
  63. +52
    -0
      Source/BPA.MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
  64. +19
    -0
      Source/BPA.MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
  65. +61
    -0
      Source/BPA.MQTTnet.AspnetCore/BPA - Backup (1).MQTTnet.AspNetCore.csproj
  66. +61
    -0
      Source/BPA.MQTTnet.AspnetCore/BPA - Backup.MQTTnet.AspNetCore.csproj
  67. +75
    -0
      Source/BPA.MQTTnet.AspnetCore/BPA.MQTTnet.AspNetCore.csproj
  68. +38
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
  69. +26
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
  70. +45
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
  71. +74
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
  72. +41
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
  73. +105
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
  74. +272
    -0
      Source/BPA.MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs
  75. +16
    -0
      Source/BPA.MQTTnet.AspnetCore/ConnectionBuilderExtensions.cs
  76. +29
    -0
      Source/BPA.MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
  77. +25
    -0
      Source/BPA.MQTTnet.AspnetCore/EndpointRouterExtensions.cs
  78. +193
    -0
      Source/BPA.MQTTnet.AspnetCore/MqttConnectionContext.cs
  79. +58
    -0
      Source/BPA.MQTTnet.AspnetCore/MqttConnectionHandler.cs
  80. +33
    -0
      Source/BPA.MQTTnet.AspnetCore/MqttHostedServer.cs
  81. +37
    -0
      Source/BPA.MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
  82. +67
    -0
      Source/BPA.MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
  83. +113
    -0
      Source/BPA.MQTTnet.AspnetCore/ReaderExtensions.cs
  84. +105
    -0
      Source/BPA.MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
  85. +41
    -0
      Source/BPA.MQTTnet/Adapter/IMqttChannelAdapter.cs
  86. +14
    -0
      Source/BPA.MQTTnet/Adapter/IMqttClientAdapterFactory.cs
  87. +19
    -0
      Source/BPA.MQTTnet/Adapter/IMqttServerAdapter.cs
  88. +426
    -0
      Source/BPA.MQTTnet/Adapter/MqttChannelAdapter.cs
  89. +23
    -0
      Source/BPA.MQTTnet/Adapter/MqttConnectingFailedException.cs
  90. +99
    -0
      Source/BPA.MQTTnet/Adapter/MqttPacketInspector.cs
  91. +26
    -0
      Source/BPA.MQTTnet/Adapter/ReceivedMqttPacket.cs
  92. +97
    -0
      Source/BPA.MQTTnet/BPA.MQTTnet.csproj
  93. +32
    -0
      Source/BPA.MQTTnet/Certificates/BlobCertificateProvider.cs
  94. +13
    -0
      Source/BPA.MQTTnet/Certificates/ICertificateProvider.cs
  95. +26
    -0
      Source/BPA.MQTTnet/Certificates/X509CertificateProvider.cs
  96. +24
    -0
      Source/BPA.MQTTnet/Channel/IMqttChannel.cs
  97. +120
    -0
      Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResult.cs
  98. +32
    -0
      Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResultCode.cs
  99. +107
    -0
      Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResultFactory.cs
  100. +22
    -0
      Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs

+ 12
- 0
BAP.MQTTnet.Test/BAP.MQTTnet.Test.csproj Datei anzeigen

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Source\BPA.MQTTnet.AspnetCore\BPA.MQTTnet.AspNetCore.csproj" />
<ProjectReference Include="..\Source\BPA.MQTTnet\BPA.MQTTnet.csproj" />
</ItemGroup>

</Project>

+ 275
- 0
BAP.MQTTnet.Test/Client/Client_Connection_Samples.cs Datei anzeigen

@@ -0,0 +1,275 @@
// 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;
using MQTTnet.Client;
using MQTTnet.Formatter;
using System;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;

namespace BPA.MQTTnet.Samples.Client {

public static class Client_Connection_Samples
{
public static async Task Connect_Client()
{
/*
* This sample creates a simple MQTT client and connects to a public broker.
*
* Always dispose the client when it is no longer used.
* The default version of MQTT is 3.1.1.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
// Use builder classes where possible in this project.
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("10.2.1.21",1883).Build();

// This will throw an exception if the server is not available.
// The result from this message returns additional data which was sent
// from the server. Please refer to the MQTT protocol specification for details.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

//response.DumpToConsole();

// Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
// gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();

await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
}
}

public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();

try
{
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Timeout while connecting.");
}
}
}

public static async Task Connect_Client_Using_MQTTv5()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();

// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Connect_Client_Using_TLS_1_2()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTls(
o =>
{
o.SslProtocol = SslProtocols.Tls12;
})
.Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");
}
}

public static async Task Connect_Client_Using_WebSockets()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();

var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Connect_Client_With_TLS_Encryption()
{
/*
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTls(
o =>
{
o.SslProtocol = SslProtocols.Tls12; // The default value is determined by the OS. Set manually to force version.
})
.Build();

// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Ping_Server()
{
/*
* This sample sends a PINGREQ packet to the server and waits for a reply.
*
* This is only supported in METTv5.0.0+.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

// This will throw an exception if the server does not reply.
await mqttClient.PingAsync(CancellationToken.None);

Console.WriteLine("The MQTT server replied to the ping request.");
}
}

public static async Task Reconnect_Using_Event()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses one of the events from the client.
* This approach has a risk of dead locks! Consider using the timer approach (see sample).
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

mqttClient.DisconnectedAsync += async e =>
{
if (e.ClientWasConnected)
{
// Use the current options as the new options.
await mqttClient.ConnectAsync(mqttClient.Options);
}
};

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
}
}

public static void Reconnect_Using_Timer()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses a custom Task/Thread which will monitor the connection status.
* This is the recommended way but requires more custom code!
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

_ = Task.Run(
async () =>
{
// User proper cancellation and no while(true).
while (true)
{
try
{
// 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.");
}
}
catch
{
// Handle the exception properly (logging etc.).
}
finally
{
// Check the connection state every 5 seconds and perform a reconnect if required.
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
});
}
}
}
}

+ 46
- 0
BAP.MQTTnet.Test/Client/Client_Publish_Samples.cs Datei anzeigen

@@ -0,0 +1,46 @@
// 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.Client;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.Samples.Client {

public static class Client_Publish_Samples
{
public static async Task Publish_Application_Message()
{
/*
* This sample pushes a simple application message including a topic and a payload.
*
* Always use builders where they exist. Builders (in this project) are designed to be
* backward compatible. Creating an _MqttApplicationMessage_ via its constructor is also
* supported but the class might change often in future releases where the builder does not
* or at least provides backward compatibility where possible.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("10.2.1.21", 1883)
.Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("mqttnet/samples/topic/2")
.WithPayload("19.asssssssssss5")
.Build();

await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);

Console.WriteLine("MQTT application message is published.");
}
}
}
}

+ 83
- 0
BAP.MQTTnet.Test/Client/Client_Subscribe_Samples.cs Datei anzeigen

@@ -0,0 +1,83 @@
// 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.Client;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.Samples.Client {

public static class Client_Subscribe_Samples
{
public static async Task Handle_Received_Application_Message()
{
/*
* This sample subscribes to a topic and processes the received message.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("10.2.1.21",1883)
.Build();

// Setup message handling before connecting so that queued messages
// are also handled properly. When there is no event handler attached all
// received messages get lost.
mqttClient.ApplicationMessageReceivedAsync += e =>
{
Console.WriteLine("Received application message.");
// e.DumpToConsole();

return Task.CompletedTask;
};

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
.Build();

await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);

Console.WriteLine("MQTT client subscribed to topic.");

Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
}

public static async Task Subscribe_Topic()
{
/*
* This sample subscribes to a topic.
*/

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 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();
}
}
}
}

+ 153
- 0
BAP.MQTTnet.Test/Controllers/HomeController.cs Datei anzeigen

@@ -0,0 +1,153 @@
using BAP.MQTTnet.Test.Models;
using BPA.MQTTnet.Samples.Client;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace BAP.MQTTnet.Test.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
// Client_Connection_Samples.Connect_Client();
// Task.WaitAll(Publish_Application_Message());
Handle_Received_Application_Message();
_logger = logger;
}

public IActionResult Index()
{
return View();
}

public IActionResult Privacy()
{
return View();
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}

public static async Task Publish_Application_Message()
{
/*
* This sample pushes a simple application message including a topic and a payload.
*
* Always use builders where they exist. Builders (in this project) are designed to be
* backward compatible. Creating an _MqttApplicationMessage_ via its constructor is also
* supported but the class might change often in future releases where the builder does not
* or at least provides backward compatibility where possible.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("10.2.1.21", 1883)
.Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("mqttnet/samples/topic/2")
.WithPayload("19.asssssssssss5")
.Build();

await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);

Console.WriteLine("MQTT application message is published.");
}
}

public async Task Handle_Received_Application_Message()
{
/*
* This sample subscribes to a topic and processes the received message.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("10.2.1.21", 1883)
.WithCredentials("client1","222")
.WithTimeout(TimeSpan.FromMilliseconds(200))
.WithClientId(Guid.NewGuid().ToString().Substring(0,5))
.Build();

// Setup message handling before connecting so that queued messages
// are also handled properly. When there is no event handler attached all
// received messages get lost.

var p= await mqttClient.ConnectAsync(mqttClientOptions);

//mqttClient.UseConnectedHandler(async e =>
//{
// Console.WriteLine("### CONNECTED WITH SERVER ###");

// // Subscribe to a topic
// await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("my/topic").Build());

// Console.WriteLine("### SUBSCRIBED ###");
//});

var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
.Build();

mqttClient.ApplicationMessageReceivedAsync += e =>
{
var a = e.ApplicationMessage.Topic;
Console.WriteLine("Received application message.");
// e.DumpToConsole();

return Task.CompletedTask;
};
mqttClient.DisconnectedAsync+=async e =>
{
Console.WriteLine("与服务器之间的连接断开了,正在尝试重新连接");
await Task.Delay(TimeSpan.FromSeconds(5));
try
{
// 重新连接
await mqttClient.ConnectAsync(mqttClientOptions);
}
catch (Exception ex)
{
Console.WriteLine($"重新连接服务器失败:{ex}");
}

//return Task.CompletedTask;
};
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
//using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
//{
//}

// var data = await mqttClient.SubscribeAsync(mqttSubscribeOptions);

//Console.WriteLine("MQTT client subscribed to topic.");

//Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
}
}
}

+ 11
- 0
BAP.MQTTnet.Test/Models/ErrorViewModel.cs Datei anzeigen

@@ -0,0 +1,11 @@
using System;

namespace BAP.MQTTnet.Test.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

+ 433
- 0
BAP.MQTTnet.Test/MqttClient.cs Datei anzeigen

@@ -0,0 +1,433 @@

using MQTTnet;
using System;
using System.Text;

namespace MQTT
{
public class MqttClient
{
#region Field Area

/// <summary>
/// Mqtt factory
/// </summary>
private MqttFactory _factory;
/// <summary>
/// Mqtt client
/// </summary>
private IMqttClient _mqttClient;
/// <summary>
/// Mqtt 配置信息
/// </summary>
private MqttClientConfig _mqttClientConfig;
/// <summary>
/// Mqtt options
/// </summary>
private IMqttClientOptions _options;

#endregion

#region CTOR

/// <summary>
/// 默认启动IP为127.0.0.1 端口为1883
/// </summary>
public MqttClient()
{
_mqttClientConfig = new MqttClientConfig
{
ServerIp = "127.0.0.1",
Port = 1883
};
Init();

// <summary>
// 调用示例
///
/// //var client = new MqttClient(s =>
/// {
/// s.Port = 1883;
/// s.ServerIp = "127.0.0.1";
/// s.UserName = "mqtt-test";
/// s.Password = "mqtt-test";
/// s.ReciveMsgCallback = s =>
/// {
/// Console.WriteLine(s.Payload_UTF8);
/// };
/// }, true);
/// client.Subscribe("/TopicName/");
/// </summary>
/// <param name="config">客户端配置信息</param>
/// <param name="autoStart">直接启动</param>
public MqttClient(Action<MqttClientConfig> config, bool autoStart = false)
{
_mqttClientConfig = new MqttClientConfig();
config(_mqttClientConfig);
Init();
if (autoStart)
{
Start();
}
}


#endregion

/// <summary>
/// 获取MqttClient实例
///
/// 调用示例
/// //var client = MqttClient.Instance(s =>
/// {
/// s.Port = 1883;
/// s.ServerIp = "127.0.0.1";
/// s.UserName = "mqtt-test";
/// s.Password = "mqtt-test";
/// s.ReciveMsgCallback = s =>
/// {
/// Console.WriteLine(s.Payload_UTF8);
/// };
/// }, true);
/// client.Subscribe("/TopicName/");
/// </summary>
/// <param name="config">客户端配置信息</param>
/// <param name="autoStart">直接启动</param>
/// <returns></returns>
public static MqttClient Instance(Action<MqttClientConfig> config, bool autoStart = false)
=> new MqttClient(config, autoStart);

/// <summary>
/// 初始化注册
/// </summary>
private void Init()
{
try
{
_factory = new MqttFactory();

_mqttClient = _factory.CreateMqttClient();

_options = new MqttClientOptionsBuilder()
WithTcpServer(_mqttClientConfig.ServerIp, _mqttClientConfig.Port)
WithCredentials(_mqttClientConfig.UserName, _mqttClientConfig.Password)
WithClientId(_mqttClientConfig.ClientId)
Build();

//消息回调
_mqttClient.UseApplicationMessageReceivedHandler(ReciveMsg);
}
catch (Exception exp)
{
if (_mqttClientConfig.Exception is null)
{
throw exp;
}
_mqttClientConfig.Exception(exp);
}
}


#region 内部事件转换处理

/// <summary>
/// 消息接收回调
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
private void ReciveMsg(MqttApplicationMessageReceivedEventArgs e)
{
if (_mqttClientConfig.ReciveMsgCallback != null)
{
_mqttClientConfig.ReciveMsgCallback(new MqttClientReciveMsg
{
Topic = e.ApplicationMessage.Topic,
Payload_UTF8 = Encoding.UTF8.GetString(e.ApplicationMessage.Payload),
Payload = e.ApplicationMessage.Payload,
Qos = e.ApplicationMessage.QualityOfServiceLevel,
Retain = e.ApplicationMessage.Retain,
});
}
}

/// <summary>
/// 订阅
/// </summary>
/// <param name="topicName"></param>
public async void Subscribe(string topicName)
{
topicName = topicName.Trim();
if (string.IsNullOrEmpty(topicName))
{
throw new Exception("订阅主题不能为空!");
}

if (!_mqttClient.IsConnected)
{
throw new Exception("MQTT客户端尚未连接!请先启动连接");
}
await _mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topicName).Build());
}

/// <summary>
/// 取消订阅
/// </summary>
/// <param name="topicName"></param>
public async void Unsubscribe(string topicName)
{
topicName = topicName.Trim();
if (string.IsNullOrEmpty(topicName))
{
throw new Exception("订阅主题不能为空!");
}

if (!_mqttClient.IsConnected)
{
throw new Exception("MQTT客户端尚未连接!请先启动连接");
}
await _mqttClient.UnsubscribeAsync(topicName);
}

/// <summary>
/// 重连机制
/// </summary>
private void ReConnected()
{
if (_mqttClient.IsConnected)
{
return;
}
for (int i = 0; i < 10; i++)
{
//重连机制
_mqttClient.UseDisconnectedHandler(async e =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(_mqttClientConfig.ReconneTime));
await _mqttClient.ConnectAsync(_options);
return;
}
catch (Exception exp)
{
if (_mqttClientConfig.Exception is null)
{
throw exp;
}
_mqttClientConfig.Exception(exp);
}
});
}
}


#endregion


#region 消息发送

/// <summary>
/// 发送消息,含有重连机制,如掉线会自动重连
/// </summary>
/// <param name="message"></param>
private async Task PublishAsync(string topicName, MqttApplicationMessageBuilder MessageBuilder, PublicQos qos = 0)
{
233 string topic = topicName.Trim();
234 if (string.IsNullOrEmpty(topic))
235 {
236 throw new Exception("主题不能为空!");
}
ReConnected();

MessageBuilder.WithTopic(topic).WithRetainFlag();
if (qos == PublicQos.Qos_0)
{
MessageBuilder.WithAtLeastOnceQoS();
}
else if (qos == PublicQos.Qos_1)
{
MessageBuilder.WithAtMostOnceQoS();
}
else
{
MessageBuilder.WithExactlyOnceQoS();
}
var Message = MessageBuilder.Build();
try
{
await _mqttClient.PublishAsync(Message);
}
catch (Exception e)
{
if (_mqttClientConfig.Exception is null)
{
throw e;
}
_mqttClientConfig.Exception(e);
}

}

/// <summary>
/// 发送消息,含有重连机制,如掉线会自动重连
/// </summary>
/// <param name="message">文字消息</param>
public async Task Publish(string topicName, string message, PublicQos qos = 0)
{
await PublishAsync(topicName, new MqttApplicationMessageBuilder()
.WithPayload(message), qos);
}
/// <summary>
/// 发送消息,含有重连机制,如掉线会自动重连
/// </summary>
/// <param name="message">消息流</param>
public async void Publish(string topicName, Stream message, PublicQos qos = 0)
=> await PublishAsync(topicName, new MqttApplicationMessageBuilder()
.WithPayload(message), qos);

/// <summary>
/// 发送消息,含有重连机制,如掉线会自动重连
/// </summary>
/// <param name="message">Byte消息</param>
public async void Publish(string topicName, IEnumerable<byte> message, PublicQos qos = 0)
=> await PublishAsync(topicName, new MqttApplicationMessageBuilder()
.WithPayload(message), qos);

/// <summary>
/// 发送消息,含有重连机制,如掉线会自动重连
/// </summary>
/// <param name="message">Byte消息</param>
public async void Publish(string topicName, byte[] message, PublicQos qos = 0)
=> await PublishAsync(topicName, new MqttApplicationMessageBuilder()
.WithPayload(message), qos);


#endregion

/// <summary>
/// 启动服务
/// </summary>
/// <returns></returns>
public async Task Start()
=> await _mqttClient.ConnectAsync(_options);

/// <summary>
/// 停止服务
/// </summary>
/// <returns></returns>
public async Task Stop()
=> await _mqttClient.DisconnectAsync(new MqttClientDisconnectOptions { ReasonCode = MqttClientDisconnectReason.NormalDisconnection }, CancellationToken.None);

}
public class MqttClientConfig
{
private string _serverIp;
/// <summary>
/// 服务器IP
/// </summary>
public string ServerIp
{
get => _serverIp;
set
{
if (string.IsNullOrEmpty(value.Trim()))
{
throw new ArgumentException("ServerIp can't be null or empty!");
}
_serverIp = value;
}
}
private int _port;
/// <summary>
/// 服务器端口
/// </summary>
public int Port
{
get => _port;
set
{
if (value <= 0)
{
throw new ArgumentException("Port can't below the zero!");
}
_port = value;
}
}

/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }

private string _clientId;
/// <summary>
/// 唯一用户ID,默认使用Guid
/// </summary>
public string ClientId
{
get
{
_clientId = _clientId ?? Guid.NewGuid().ToString();
return _clientId;
}
set => _clientId = value;
}
/// <summary>
/// 客户端掉线重连时间,单位/s,默认5s
/// </summary>
public double ReconneTime { get; set; } = 5;
/// <summary>
/// 异常回调,默认为空,为空抛异常
/// </summary>
public Action<Exception> Exception = null;

/// <summary>
/// 接收消息回调,默认不接收
/// </summary>
public Action<MqttClientReciveMsg> ReciveMsgCallback = null;
}
public class MqttClientReciveMsg
{
/// <summary>
/// 主题
/// </summary>
public string Topic { get; set; }
/// <summary>
/// UTF-8格式下的 负载/消息
/// </summary>
public string Payload_UTF8 { get; set; }
/// <summary>
/// 原始 负载/消息
/// </summary>
public byte[] Payload { get; set; }
/// <summary>
/// Qos
/// </summary>
public MqttQualityOfServiceLevel Qos { get; set; }
/// <summary>
/// 保留
/// </summary>
public bool Retain { get; set; }
}
public enum PublicQos
{
/// <summary>
/// //At most once,至多一次
/// </summary>
Qos_0,
/// <summary>
/// //At least once,至少一次
/// </summary>
Qos_1,
/// <summary>
/// //QoS2,Exactly once
/// </summary>
Qos_2,
}
}
}


+ 26
- 0
BAP.MQTTnet.Test/Program.cs Datei anzeigen

@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BAP.MQTTnet.Test
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

+ 53
- 0
BAP.MQTTnet.Test/Startup.cs Datei anzeigen

@@ -0,0 +1,53 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BAP.MQTTnet.Test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

+ 8
- 0
BAP.MQTTnet.Test/Views/Home/Index.cshtml Datei anzeigen

@@ -0,0 +1,8 @@
@{
ViewData["Title"] = "Home Page";
}

<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

+ 6
- 0
BAP.MQTTnet.Test/Views/Home/Privacy.cshtml Datei anzeigen

@@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

+ 25
- 0
BAP.MQTTnet.Test/Views/Shared/Error.cshtml Datei anzeigen

@@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

+ 48
- 0
BAP.MQTTnet.Test/Views/Shared/_Layout.cshtml Datei anzeigen

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - BAP.MQTTnet.Test</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">BAP.MQTTnet.Test</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>

<footer class="border-top footer text-muted">
<div class="container">
&copy; 2022 - BAP.MQTTnet.Test - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>

+ 2
- 0
BAP.MQTTnet.Test/Views/Shared/_ValidationScriptsPartial.cshtml Datei anzeigen

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

+ 3
- 0
BAP.MQTTnet.Test/Views/_ViewImports.cshtml Datei anzeigen

@@ -0,0 +1,3 @@
@using BAP.MQTTnet.Test
@using BAP.MQTTnet.Test.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

+ 3
- 0
BAP.MQTTnet.Test/Views/_ViewStart.cshtml Datei anzeigen

@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

+ 9
- 0
BAP.MQTTnet.Test/appsettings.Development.json Datei anzeigen

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

+ 10
- 0
BAP.MQTTnet.Test/appsettings.json Datei anzeigen

@@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

+ 71
- 0
BAP.MQTTnet.Test/wwwroot/css/site.css Datei anzeigen

@@ -0,0 +1,71 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */

a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}

/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}

.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}

.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}

.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}

.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}

button.accept-policy {
font-size: 1rem;
line-height: inherit;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}

body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px; /* Vertically center the text there */
}


+ 22
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/LICENSE Datei anzeigen

@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2011-2018 Twitter, Inc.
Copyright (c) 2011-2018 The Bootstrap Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 3719
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 7
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 331
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css Datei anzeigen

@@ -0,0 +1,331 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}

html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}

[tabindex="-1"]:focus {
outline: 0 !important;
}

hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}

h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}

p {
margin-top: 0;
margin-bottom: 1rem;
}

abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}

address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}

ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}

ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}

dt {
font-weight: 700;
}

dd {
margin-bottom: .5rem;
margin-left: 0;
}

blockquote {
margin: 0 0 1rem;
}

b,
strong {
font-weight: bolder;
}

small {
font-size: 80%;
}

sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}

sub {
bottom: -.25em;
}

sup {
top: -.5em;
}

a {
color: #007bff;
text-decoration: none;
background-color: transparent;
}

a:hover {
color: #0056b3;
text-decoration: underline;
}

a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):focus {
outline: 0;
}

pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}

pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
}

figure {
margin: 0 0 1rem;
}

img {
vertical-align: middle;
border-style: none;
}

svg {
overflow: hidden;
vertical-align: middle;
}

table {
border-collapse: collapse;
}

caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}

th {
text-align: inherit;
}

label {
display: inline-block;
margin-bottom: 0.5rem;
}

button {
border-radius: 0;
}

button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}

input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}

button,
input {
overflow: visible;
}

button,
select {
text-transform: none;
}

select {
word-wrap: normal;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}

button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}

input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}

input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}

textarea {
overflow: auto;
resize: vertical;
}

fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}

legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}

progress {
vertical-align: baseline;
}

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}

[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}

[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}

::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}

output {
display: inline-block;
}

summary {
display: list-item;
cursor: pointer;
}

template {
display: none;
}

[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

+ 8
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css Datei anzeigen

@@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

+ 10038
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 7
- 0
BAP.MQTTnet.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 12
- 0
BAP.MQTTnet.Test/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt Datei anzeigen

@@ -0,0 +1,12 @@
Copyright (c) .NET Foundation. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the
License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

+ 22
- 0
BAP.MQTTnet.Test/wwwroot/lib/jquery-validation/LICENSE.md Datei anzeigen

@@ -0,0 +1,22 @@
The MIT License (MIT)
=====================

Copyright Jörn Zaefferer

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 36
- 0
BAP.MQTTnet.Test/wwwroot/lib/jquery/LICENSE.txt Datei anzeigen

@@ -0,0 +1,36 @@
Copyright JS Foundation and other contributors, https://js.foundation/

This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery

The following license applies to all parts of this software except as
documented below:

====

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

====

All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.

+ 11
- 0
BPA.AspNetCore.Test/BPA.AspNetCore.Test.csproj Datei anzeigen

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Source\BPA.MQTTnet.AspnetCore\BPA.MQTTnet.AspNetCore.csproj" />
</ItemGroup>

</Project>

+ 275
- 0
BPA.AspNetCore.Test/Client/Client_Connection_Samples.cs Datei anzeigen

@@ -0,0 +1,275 @@
// 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;
using MQTTnet.Client;
using MQTTnet.Formatter;
using System;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;

namespace BPA.MQTTnet.Samples.Client {

public static class Client_Connection_Samples
{
public static async Task Connect_Client()
{
/*
* This sample creates a simple MQTT client and connects to a public broker.
*
* Always dispose the client when it is no longer used.
* The default version of MQTT is 3.1.1.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
// Use builder classes where possible in this project.
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("10.2.1.21",1883).Build();

// This will throw an exception if the server is not available.
// The result from this message returns additional data which was sent
// from the server. Please refer to the MQTT protocol specification for details.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

//response.DumpToConsole();

// Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
// gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();

await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
}
}

public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();

try
{
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Timeout while connecting.");
}
}
}

public static async Task Connect_Client_Using_MQTTv5()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();

// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Connect_Client_Using_TLS_1_2()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTls(
o =>
{
o.SslProtocol = SslProtocols.Tls12;
})
.Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");
}
}

public static async Task Connect_Client_Using_WebSockets()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();

var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Connect_Client_With_TLS_Encryption()
{
/*
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTls(
o =>
{
o.SslProtocol = SslProtocols.Tls12; // The default value is determined by the OS. Set manually to force version.
})
.Build();

// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

Console.WriteLine("The MQTT client is connected.");

// response.DumpToConsole();
}
}

public static async Task Ping_Server()
{
/*
* This sample sends a PINGREQ packet to the server and waits for a reply.
*
* This is only supported in METTv5.0.0+.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

// This will throw an exception if the server does not reply.
await mqttClient.PingAsync(CancellationToken.None);

Console.WriteLine("The MQTT server replied to the ping request.");
}
}

public static async Task Reconnect_Using_Event()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses one of the events from the client.
* This approach has a risk of dead locks! Consider using the timer approach (see sample).
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

mqttClient.DisconnectedAsync += async e =>
{
if (e.ClientWasConnected)
{
// Use the current options as the new options.
await mqttClient.ConnectAsync(mqttClient.Options);
}
};

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
}
}

public static void Reconnect_Using_Timer()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses a custom Task/Thread which will monitor the connection status.
* This is the recommended way but requires more custom code!
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();

_ = Task.Run(
async () =>
{
// User proper cancellation and no while(true).
while (true)
{
try
{
// 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.");
}
}
catch
{
// Handle the exception properly (logging etc.).
}
finally
{
// Check the connection state every 5 seconds and perform a reconnect if required.
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
});
}
}
}
}

+ 46
- 0
BPA.AspNetCore.Test/Client/Client_Publish_Samples.cs Datei anzeigen

@@ -0,0 +1,46 @@
// 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.Client;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.Samples.Client {

public static class Client_Publish_Samples
{
public static async Task Publish_Application_Message()
{
/*
* This sample pushes a simple application message including a topic and a payload.
*
* Always use builders where they exist. Builders (in this project) are designed to be
* backward compatible. Creating an _MqttApplicationMessage_ via its constructor is also
* supported but the class might change often in future releases where the builder does not
* or at least provides backward compatibility where possible.
*/

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);

Console.WriteLine("MQTT application message is published.");
}
}
}
}

+ 83
- 0
BPA.AspNetCore.Test/Client/Client_Subscribe_Samples.cs Datei anzeigen

@@ -0,0 +1,83 @@
// 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.Client;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.Samples.Client {

public static class Client_Subscribe_Samples
{
public static async Task Handle_Received_Application_Message()
{
/*
* This sample subscribes to a topic and processes the received message.
*/

var mqttFactory = new MqttFactory();

using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("10.2.1.21",1883)
.Build();

// Setup message handling before connecting so that queued messages
// are also handled properly. When there is no event handler attached all
// received messages get lost.
mqttClient.ApplicationMessageReceivedAsync += e =>
{
Console.WriteLine("Received application message.");
// e.DumpToConsole();

return Task.CompletedTask;
};

await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
.Build();

await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);

Console.WriteLine("MQTT client subscribed to topic.");

Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
}

public static async Task Subscribe_Topic()
{
/*
* This sample subscribes to a topic.
*/

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 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();
}
}
}
}

+ 26
- 0
BPA.AspNetCore.Test/Pages/Error.cshtml Datei anzeigen

@@ -0,0 +1,26 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

+ 31
- 0
BPA.AspNetCore.Test/Pages/Error.cshtml.cs Datei anzeigen

@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace BPA.AspNetCore.Test.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

private readonly ILogger<ErrorModel> _logger;

public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}

public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

+ 10
- 0
BPA.AspNetCore.Test/Pages/Index.cshtml Datei anzeigen

@@ -0,0 +1,10 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}

<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

+ 25
- 0
BPA.AspNetCore.Test/Pages/Index.cshtml.cs Datei anzeigen

@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BPA.AspNetCore.Test.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;

public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}

public void OnGet()
{

}
}
}

+ 8
- 0
BPA.AspNetCore.Test/Pages/Privacy.cshtml Datei anzeigen

@@ -0,0 +1,8 @@
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

+ 24
- 0
BPA.AspNetCore.Test/Pages/Privacy.cshtml.cs Datei anzeigen

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BPA.AspNetCore.Test.Pages
{
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;

public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}

public void OnGet()
{
}
}
}

+ 50
- 0
BPA.AspNetCore.Test/Pages/Shared/_Layout.cshtml Datei anzeigen

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - BPA.AspNetCore.Test</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">BPA.AspNetCore.Test</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>

<footer class="border-top footer text-muted">
<div class="container">
&copy; 2022 - BPA.AspNetCore.Test - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>

@RenderSection("Scripts", required: false)
</body>
</html>

+ 2
- 0
BPA.AspNetCore.Test/Pages/Shared/_ValidationScriptsPartial.cshtml Datei anzeigen

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

+ 3
- 0
BPA.AspNetCore.Test/Pages/_ViewImports.cshtml Datei anzeigen

@@ -0,0 +1,3 @@
@using BPA.AspNetCore.Test
@namespace BPA.AspNetCore.Test.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

+ 3
- 0
BPA.AspNetCore.Test/Pages/_ViewStart.cshtml Datei anzeigen

@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

+ 26
- 0
BPA.AspNetCore.Test/Program.cs Datei anzeigen

@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BPA.AspNetCore.Test
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

+ 56
- 0
BPA.AspNetCore.Test/Startup.cs Datei anzeigen

@@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BPA.AspNetCore.Test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}

+ 9
- 0
BPA.AspNetCore.Test/appsettings.Development.json Datei anzeigen

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

+ 10
- 0
BPA.AspNetCore.Test/appsettings.json Datei anzeigen

@@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

+ 71
- 0
BPA.AspNetCore.Test/wwwroot/css/site.css Datei anzeigen

@@ -0,0 +1,71 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */

a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}

/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}

.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}

.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}

.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}

.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}

button.accept-policy {
font-size: 1rem;
line-height: inherit;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}

body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px; /* Vertically center the text there */
}


+ 22
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/LICENSE Datei anzeigen

@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2011-2018 Twitter, Inc.
Copyright (c) 2011-2018 The Bootstrap Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 3719
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 7
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 331
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css Datei anzeigen

@@ -0,0 +1,331 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}

html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}

[tabindex="-1"]:focus {
outline: 0 !important;
}

hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}

h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}

p {
margin-top: 0;
margin-bottom: 1rem;
}

abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}

address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}

ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}

ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}

dt {
font-weight: 700;
}

dd {
margin-bottom: .5rem;
margin-left: 0;
}

blockquote {
margin: 0 0 1rem;
}

b,
strong {
font-weight: bolder;
}

small {
font-size: 80%;
}

sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}

sub {
bottom: -.25em;
}

sup {
top: -.5em;
}

a {
color: #007bff;
text-decoration: none;
background-color: transparent;
}

a:hover {
color: #0056b3;
text-decoration: underline;
}

a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):focus {
outline: 0;
}

pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}

pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
}

figure {
margin: 0 0 1rem;
}

img {
vertical-align: middle;
border-style: none;
}

svg {
overflow: hidden;
vertical-align: middle;
}

table {
border-collapse: collapse;
}

caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}

th {
text-align: inherit;
}

label {
display: inline-block;
margin-bottom: 0.5rem;
}

button {
border-radius: 0;
}

button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}

input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}

button,
input {
overflow: visible;
}

button,
select {
text-transform: none;
}

select {
word-wrap: normal;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}

button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}

input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}

input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}

textarea {
overflow: auto;
resize: vertical;
}

fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}

legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}

progress {
vertical-align: baseline;
}

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}

[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}

[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}

::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}

output {
display: inline-block;
}

summary {
display: list-item;
cursor: pointer;
}

template {
display: none;
}

[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

+ 8
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css Datei anzeigen

@@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

+ 10038
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 7
- 0
BPA.AspNetCore.Test/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 12
- 0
BPA.AspNetCore.Test/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt Datei anzeigen

@@ -0,0 +1,12 @@
Copyright (c) .NET Foundation. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the
License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

+ 22
- 0
BPA.AspNetCore.Test/wwwroot/lib/jquery-validation/LICENSE.md Datei anzeigen

@@ -0,0 +1,22 @@
The MIT License (MIT)
=====================

Copyright Jörn Zaefferer

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 36
- 0
BPA.AspNetCore.Test/wwwroot/lib/jquery/LICENSE.txt Datei anzeigen

@@ -0,0 +1,36 @@
Copyright JS Foundation and other contributors, https://js.foundation/

This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery

The following license applies to all parts of this software except as
documented below:

====

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

====

All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.

+ 32
- 9
MQTTnet.sln Datei anzeigen

@@ -1,15 +1,15 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31919.166
# Visual Studio Version 16
VisualStudioVersion = 16.0.32228.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
ProjectSection(SolutionItems) = preProject
CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md
LICENSE = LICENSE
README.md = README.md
CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
@@ -20,17 +20,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bpa", "bpa", "{F7F3B037-B2AC-426E-BB9E-F41BA1F50C91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BPA.MQTTnet", "Source\BPA.MQTTnet\BPA.MQTTnet.csproj", "{CD8AFE00-C885-4617-AB70-720B8BBABFCD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BPA.MQTTnet.AspNetCore", "Source\BPA.MQTTnet.AspnetCore\BPA.MQTTnet.AspNetCore.csproj", "{962FE385-013D-496B-9C0E-6800F8906B8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BAP.MQTTnet.Test", "BAP.MQTTnet.Test\BAP.MQTTnet.Test.csproj", "{BA264BFA-A33C-4278-AB2E-B3184B79D620}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -82,11 +90,26 @@ Global
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.Build.0 = Release|Any CPU
{CD8AFE00-C885-4617-AB70-720B8BBABFCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD8AFE00-C885-4617-AB70-720B8BBABFCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD8AFE00-C885-4617-AB70-720B8BBABFCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD8AFE00-C885-4617-AB70-720B8BBABFCD}.Release|Any CPU.Build.0 = Release|Any CPU
{962FE385-013D-496B-9C0E-6800F8906B8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{962FE385-013D-496B-9C0E-6800F8906B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{962FE385-013D-496B-9C0E-6800F8906B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{962FE385-013D-496B-9C0E-6800F8906B8E}.Release|Any CPU.Build.0 = Release|Any CPU
{BA264BFA-A33C-4278-AB2E-B3184B79D620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA264BFA-A33C-4278-AB2E-B3184B79D620}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA264BFA-A33C-4278-AB2E-B3184B79D620}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA264BFA-A33C-4278-AB2E-B3184B79D620}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CD8AFE00-C885-4617-AB70-720B8BBABFCD} = {F7F3B037-B2AC-426E-BB9E-F41BA1F50C91}
{962FE385-013D-496B-9C0E-6800F8906B8E} = {F7F3B037-B2AC-426E-BB9E-F41BA1F50C91}
{BA264BFA-A33C-4278-AB2E-B3184B79D620} = {F7F3B037-B2AC-426E-BB9E-F41BA1F50C91}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}


+ 2
- 1
Samples/Client/Client_Subscribe_Samples.cs Datei anzeigen

@@ -5,7 +5,7 @@
using MQTTnet.Client;
using MQTTnet.Samples.Helpers;

namespace MQTTnet.Samples.Client;
namespace MQTTnet.Samples.Client {

public static class Client_Subscribe_Samples
{
@@ -77,4 +77,5 @@ public static class Client_Subscribe_Samples
response.DumpToConsole();
}
}
}
}

+ 52
- 0
Source/BPA.MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs Datei anzeigen

@@ -0,0 +1,52 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;

namespace MQTTnet.AspNetCore
{
public static class ApplicationBuilderExtensions
{
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
{
await next();
return;
}

string subProtocol = null;

if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
}

var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
{
await adapter.RunWebSocketConnectionAsync(webSocket, context);
}
});

return app;
}

public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<MqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<MqttServer>();

configure(server);

return app;
}
}
}

+ 19
- 0
Source/BPA.MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs Datei anzeigen

@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using MQTTnet.Server;
using System;

namespace MQTTnet.AspNetCore
{
public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
{
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}

public IServiceProvider ServiceProvider { get; }
}
}

+ 61
- 0
Source/BPA.MQTTnet.AspnetCore/BPA - Backup (1).MQTTnet.AspNetCore.csproj Datei anzeigen

@@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Company>The contributors of MQTTnet</Company>
<Product>BPA</Product>
<Description>BPA.MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</Description>
<Authors>The contributors of MQTTnet</Authors>
<PackageId>BPA.MQTTnet.AspNetCore</PackageId>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Copyright>Christian Kratky 2016-2022</Copyright>
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<VersionPrefix>1.0.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.MQTTnet\BPA.MQTTnet.csproj" />
</ItemGroup>
</Project>

+ 61
- 0
Source/BPA.MQTTnet.AspnetCore/BPA - Backup.MQTTnet.AspNetCore.csproj Datei anzeigen

@@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<AssemblyName>BPA.MQTTnet.AspNetCore</AssemblyName>
<RootNamespace>BPA.MQTTnet.AspNetCore</RootNamespace>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Company>The contributors of MQTTnet</Company>
<Product>BPA.MQTTnet</Product>
<Description>BPA.MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</Description>
<Authors>The contributors of MQTTnet</Authors>
<PackageId>BPA.MQTTnet.AspNetCore</PackageId>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Copyright>Christian Kratky 2016-2022</Copyright>
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<VersionPrefix>1.0.3</VersionPrefix>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.MQTTnet\BPA.MQTTnet.csproj" />
</ItemGroup>
</Project>

+ 75
- 0
Source/BPA.MQTTnet.AspnetCore/BPA.MQTTnet.AspNetCore.csproj Datei anzeigen

@@ -0,0 +1,75 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>

<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Company>The contributors of MQTTnet</Company>
<Product>BPA</Product>
<Description>BPA.MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</Description>
<Authors>The contributors of MQTTnet</Authors>
<PackageId>BPA.MQTTnet.AspNetCore</PackageId>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Copyright>Christian Kratky 2016-2022</Copyright>
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Nuget.Tools.V2" Version="1.1.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BPA.MQTTnet\BPA.MQTTnet.csproj" />
</ItemGroup>

</Project>

+ 38
- 0
Source/BPA.MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs Datei anzeigen

@@ -0,0 +1,38 @@
// 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.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Formatter;
using System;
using System.Net;
using MQTTnet.Client;
using MQTTnet.Diagnostics;

namespace MQTTnet.AspNetCore.Client
{
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));

switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535));
return new MqttConnectionContext(formatter, tcpConnection);
}
default:
{
throw new NotSupportedException();
}
}
}
}
}

+ 26
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs Datei anzeigen

@@ -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;
using System.Runtime.InteropServices;

namespace MQTTnet.AspNetCore.Client.Tcp
{
public static class BufferExtensions
{
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
{
return ((ReadOnlyMemory<byte>)memory).GetArray();
}

public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (!MemoryMarshal.TryGetArray(memory, out var result))
{
throw new InvalidOperationException("Buffer backed by array was expected");
}
return result;
}
}
}

+ 45
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs Datei anzeigen

@@ -0,0 +1,45 @@
// 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.IO.Pipelines;

namespace MQTTnet.AspNetCore.Client.Tcp
{
public class DuplexPipe : IDuplexPipe
{
public DuplexPipe(PipeReader reader, PipeWriter writer)
{
Input = reader;
Output = writer;
}

public PipeReader Input { get; }

public PipeWriter Output { get; }

public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
{
var input = new Pipe(inputOptions);
var output = new Pipe(outputOptions);

var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);

return new DuplexPipePair(applicationToTransport, transportToApplication);
}

// This class exists to work around issues with value tuple on .NET Framework
public readonly struct DuplexPipePair
{
public IDuplexPipe Transport { get; }
public IDuplexPipe Application { get; }

public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
{
Transport = transport;
Application = application;
}
}
}
}

+ 74
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs Datei anzeigen

@@ -0,0 +1,74 @@
// 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.IO.Pipelines;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketAwaitable : ICriticalNotifyCompletion
{
private static readonly Action _callbackCompleted = () => { };

private readonly PipeScheduler _ioScheduler;

private Action _callback;
private int _bytesTransferred;
private SocketError _error;

public SocketAwaitable(PipeScheduler ioScheduler)
{
_ioScheduler = ioScheduler;
}

public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);

public SocketAwaitable GetAwaiter() => this;

public int GetResult()
{
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));

_callback = null;

if (_error != SocketError.Success)
{
throw new SocketException((int)_error);
}

return _bytesTransferred;
}

public void OnCompleted(Action continuation)
{
if (ReferenceEquals(_callback, _callbackCompleted) ||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
{
Task.Run(continuation);
}
}

public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
}

public void Complete(int bytesTransferred, SocketError socketError)
{
_error = socketError;
_bytesTransferred = bytesTransferred;
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);

if (continuation != null)
{
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
}
}
}
}

+ 41
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs Datei anzeigen

@@ -0,0 +1,41 @@
// 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.IO.Pipelines;
using System.Net.Sockets;

namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketReceiver
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;

public SocketReceiver(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}

public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
{
#if NETCOREAPP2_1
_eventArgs.SetBuffer(buffer);
#else
var segment = buffer.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.ReceiveAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}

return _awaitable;
}
}
}

+ 105
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs Datei anzeigen

@@ -0,0 +1,105 @@
// 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.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;

#if NETCOREAPP2_1
using System.Runtime.InteropServices;
#endif

namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketSender
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;

private List<ArraySegment<byte>> _bufferList;

public SocketSender(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}

public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
{
if (buffers.IsSingleSegment)
{
return SendAsync(buffers.First);
}

#if NETCOREAPP2_1
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
#else
if (_eventArgs.Buffer != null)
#endif
{
_eventArgs.SetBuffer(null, 0, 0);
}

_eventArgs.BufferList = GetBufferList(buffers);

if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}

return _awaitable;
}

private SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
{
// The BufferList getter is much less expensive then the setter.
if (_eventArgs.BufferList != null)
{
_eventArgs.BufferList = null;
}

#if NETCOREAPP2_1
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
#else
var segment = memory.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}

return _awaitable;
}

private List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
{
Debug.Assert(!buffer.IsEmpty);
Debug.Assert(!buffer.IsSingleSegment);

if (_bufferList == null)
{
_bufferList = new List<ArraySegment<byte>>();
}
else
{
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
_bufferList.Clear();
}

foreach (var b in buffer)
{
_bufferList.Add(b.GetArray());
}

return _bufferList;
}
}
}

+ 272
- 0
Source/BPA.MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs Datei anzeigen

@@ -0,0 +1,272 @@
// 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 Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace MQTTnet.AspNetCore.Client.Tcp
{
public class TcpConnection : ConnectionContext
{
private volatile bool _aborted;
private readonly EndPoint _endPoint;
private SocketSender _sender;
private SocketReceiver _receiver;

private Socket _socket;
private IDuplexPipe _application;

public bool IsConnected { get; private set; }
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 TcpConnection(EndPoint endPoint)
{
_endPoint = endPoint;
}

public TcpConnection(Socket socket)
{
_socket = socket;
_endPoint = socket.RemoteEndPoint;

_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
public override ValueTask DisposeAsync()
#else
public Task DisposeAsync()
#endif
{
IsConnected = false;

Transport?.Output.Complete();
Transport?.Input.Complete();

_socket?.Dispose();

#if NETCOREAPP3_1 || NET5_0_OR_GREATER

return base.DisposeAsync();
}
#else

return Task.CompletedTask;
}
#endif

public async Task StartAsync()
{
if (_socket == null)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
await _socket.ConnectAsync(_endPoint);
}

var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);

Transport = pair.Transport;
_application = pair.Application;

_ = ExecuteAsync();

IsConnected = true;
}

private async Task ExecuteAsync()
{
Exception sendError = null;
try
{
// Spawn send and receive logic
var receiveTask = DoReceive();
var sendTask = DoSend();

// If the sending task completes then close the receive
// We don't need to do this in the other direction because the kestrel
// will trigger the output closing once the input is complete.
if (await Task.WhenAny(receiveTask, sendTask) == sendTask)
{
// Tell the reader it's being aborted
_socket.Dispose();
}

// Now wait for both to complete
await receiveTask;
sendError = await sendTask;

// Dispose the socket(should noop if already called)
_socket.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected exception in {nameof(TcpConnection)}.{nameof(StartAsync)}: " + ex);
}
finally
{
// Complete the output after disposing the socket
_application.Input.Complete(sendError);
}
}
private async Task DoReceive()
{
Exception error = null;

try
{
await ProcessReceives();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
{
error = new MqttCommunicationException(ex);
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted ||
ex.SocketErrorCode == SocketError.ConnectionAborted ||
ex.SocketErrorCode == SocketError.Interrupted ||
ex.SocketErrorCode == SocketError.InvalidArgument)
{
if (!_aborted)
{
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
error = ConnectionAborted();
}
}
catch (ObjectDisposedException)
{
if (!_aborted)
{
error = ConnectionAborted();
}
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
if (_aborted)
{
error = error ?? ConnectionAborted();
}

_application.Output.Complete(error);
}
}

private async Task ProcessReceives()
{
while (true)
{
// Ensure we have some reasonable amount of buffer space
var buffer = _application.Output.GetMemory();

var bytesReceived = await _receiver.ReceiveAsync(buffer);

if (bytesReceived == 0)
{
// FIN
break;
}

_application.Output.Advance(bytesReceived);

var flushTask = _application.Output.FlushAsync();

if (!flushTask.IsCompleted)
{
await flushTask;
}

var result = flushTask.GetAwaiter().GetResult();
if (result.IsCompleted)
{
// Pipe consumer is shut down, do we stop writing
break;
}
}
}

private Exception ConnectionAborted()
{
return new MqttCommunicationException("Connection Aborted");
}

private async Task<Exception> DoSend()
{
Exception error = null;

try
{
await ProcessSends();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
}
catch (ObjectDisposedException)
{
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
_aborted = true;
_socket.Shutdown(SocketShutdown.Both);
}

return error;
}

private async Task ProcessSends()
{
while (true)
{
// Wait for data to write from the pipe producer
var result = await _application.Input.ReadAsync();
var buffer = result.Buffer;

if (result.IsCanceled)
{
break;
}

var end = buffer.End;
var isCompleted = result.IsCompleted;
if (!buffer.IsEmpty)
{
await _sender.SendAsync(buffer);
}

_application.Input.AdvanceTo(end);

if (isCompleted)
{
break;
}
}
}
}
}

+ 16
- 0
Source/BPA.MQTTnet.AspnetCore/ConnectionBuilderExtensions.cs Datei anzeigen

@@ -0,0 +1,16 @@
// 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 Microsoft.AspNetCore.Connections;

namespace MQTTnet.AspNetCore
{
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
{
return builder.UseConnectionHandler<MqttConnectionHandler>();
}
}
}

+ 29
- 0
Source/BPA.MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs Datei anzeigen

@@ -0,0 +1,29 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections;

#if NETCOREAPP3_1
using System;
#endif

namespace MQTTnet.AspNetCore
{
public static class ConnectionRouteBuilderExtensions
{
#if NETCOREAPP3_1
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
#endif
#if NETCOREAPP3_1 || NETCOREAPP2_1 || NETSTANDARD
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
{
connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
#endif
}
}

+ 25
- 0
Source/BPA.MQTTnet.AspnetCore/EndpointRouterExtensions.cs Datei anzeigen

@@ -0,0 +1,25 @@
// 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.


#if NETCOREAPP3_1 || NET5_0_OR_GREATER

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;

namespace MQTTnet.AspNetCore
{
public static class EndpointRouterExtensions
{
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
}
}

#endif

+ 193
- 0
Source/BPA.MQTTnet.AspnetCore/MqttConnectionContext.cs Datei anzeigen

@@ -0,0 +1,193 @@
// 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 Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using System;
using System.IO.Pipelines;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Internal;

namespace MQTTnet.AspNetCore
{
public sealed class MqttConnectionContext : IMqttChannelAdapter
{
readonly AsyncLock _writerLock = new AsyncLock();
PipeReader _input;
PipeWriter _output;
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
{
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
Connection = connection ?? throw new ArgumentNullException(nameof(connection));

if (Connection.Transport != null)
{
_input = Connection.Transport.Input;
_output = Connection.Transport.Output;
}
}

public string Endpoint
{
get
{
#if NETCOREAPP3_1
if (Connection?.RemoteEndPoint != null)
{
return Connection.RemoteEndPoint.ToString();
}
#endif
var connection = Http?.HttpContext?.Connection;
if (connection == null)
{
return Connection.ConnectionId;
}

return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
}
}

public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;

public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;

public ConnectionContext Connection { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }

public long BytesSent { get; set; }

public long BytesReceived { get; set; }

public bool IsReadingPacket { get; private set; }

IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();

public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (Connection is TcpConnection tcp && !tcp.IsConnected)
{
await tcp.StartAsync().ConfigureAwait(false);
}

_input = Connection.Transport.Input;
_output = Connection.Transport.Output;
}

public Task DisconnectAsync(CancellationToken cancellationToken)
{
_input?.Complete();
_output?.Complete();

return Task.CompletedTask;
}

public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
var input = Connection.Transport.Input;

try
{
while (!cancellationToken.IsCancellationRequested)
{
ReadResult readResult;
var readTask = input.ReadAsync(cancellationToken);
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}

var buffer = readResult.Buffer;

var consumed = buffer.Start;
var observed = buffer.Start;

try
{
if (!buffer.IsEmpty)
{
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
{
BytesReceived += received;
return packet;
}
else
{
// we did receive something but the message is not yet complete
IsReadingPacket = true;
}
}
else if (readResult.IsCompleted)
{
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
input.AdvanceTo(consumed, observed);
}
}
}
catch (Exception e)
{
// completing the channel makes sure that there is no more data read after a protocol error
_input?.Complete(e);
_output?.Complete(e);
throw;
}
finally
{
IsReadingPacket = false;
}

cancellationToken.ThrowIfCancellationRequested();
return null;
}

public void ResetStatistics()
{
BytesReceived = 0;
BytesSent = 0;
}

public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
{
var formatter = PacketFormatterAdapter;
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
{
var buffer = formatter.Encode(packet);
var msg = buffer.Join().AsMemory();
var output = _output;
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
if (result.IsCompleted)
{
BytesSent += msg.Length;
}

PacketFormatterAdapter.Cleanup();
}
}

public void Dispose()
{
}
}
}

+ 58
- 0
Source/BPA.MQTTnet.AspnetCore/MqttConnectionHandler.cs Datei anzeigen

@@ -0,0 +1,58 @@
// 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 Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;

namespace MQTTnet.AspNetCore
{
public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
MqttServerOptions _serverOptions;
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }

public override async Task OnConnectedAsync(ConnectionContext connection)
{
// required for websocket transport to work
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
if (transferFormatFeature != null)
{
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
using (var adapter = new MqttConnectionContext(formatter, connection))
{
var clientHandler = ClientHandler;
if (clientHandler != null)
{
await clientHandler(adapter).ConfigureAwait(false);
}
}
}

public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
_serverOptions = options;

return Task.CompletedTask;
}

public Task StopAsync()
{
return Task.CompletedTask;
}

public void Dispose()
{
}
}
}

+ 33
- 0
Source/BPA.MQTTnet.AspnetCore/MqttHostedServer.cs Datei anzeigen

@@ -0,0 +1,33 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;

namespace MQTTnet.AspNetCore
{
public sealed class MqttHostedServer : MqttServer, IHostedService
{
public MqttHostedServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
: base(options, adapters, logger)
{
}

public Task StartAsync(CancellationToken cancellationToken)
{
_ = StartAsync();
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync();
}
}
}

+ 37
- 0
Source/BPA.MQTTnet.AspnetCore/MqttSubProtocolSelector.cs Datei anzeigen

@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;

namespace MQTTnet.AspNetCore
{
public static class MqttSubProtocolSelector
{
public static string SelectSubProtocol(HttpRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));

string subProtocol = null;
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
}

return subProtocol;
}
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
if (requestedSubProtocolValues == null) throw new ArgumentNullException(nameof(requestedSubProtocolValues));

// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues
.OrderByDescending(p => p.Length)
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
}
}
}

+ 67
- 0
Source/BPA.MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs Datei anzeigen

@@ -0,0 +1,67 @@
// 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 Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;

namespace MQTTnet.AspNetCore
{
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
{
IMqttNetLogger _logger = new MqttNetNullLogger();
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }

public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
return Task.CompletedTask;
}

public Task StopAsync()
{
return Task.CompletedTask;
}

public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));

var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";

var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
try
{
var isSecureConnection = clientCertificate != null;

var clientHandler = ClientHandler;
if (clientHandler != null)
{
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);

using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
{
await clientHandler(channelAdapter).ConfigureAwait(false);
}
}
}
finally
{
clientCertificate?.Dispose();
}
}

public void Dispose()
{
}
}
}

+ 113
- 0
Source/BPA.MQTTnet.AspnetCore/ReaderExtensions.cs Datei anzeigen

@@ -0,0 +1,113 @@
// 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.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;

namespace MQTTnet.AspNetCore
{
public static class ReaderExtensions
{
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
in ReadOnlySequence<byte> input,
out MqttPacket packet,
out SequencePosition consumed,
out SequencePosition observed,
out int bytesRead)
{
if (formatter == null) throw new ArgumentNullException(nameof(formatter));

packet = null;
consumed = input.Start;
observed = input.End;
bytesRead = 0;
var copy = input;

if (copy.Length < 2)
{
return false;
}

var fixedHeader = copy.First.Span[0];
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
{
return false;
}

if (copy.Length < bodyLength)
{
return false;
}

var bodySlice = copy.Slice(0, bodyLength);
var buffer = bodySlice.GetMemory().ToArray();
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, new ArraySegment<byte>(buffer, 0, buffer.Length), buffer.Length + 2);

if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
formatter.DetectProtocolVersion(receivedMqttPacket);
}

packet = formatter.Decode(receivedMqttPacket);
consumed = bodySlice.End;
observed = bodySlice.End;
bytesRead = headerLength + bodyLength;
return true;
}

static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
{
if (input.IsSingleSegment)
{
return input.First;
}

// Should be rare
return input.ToArray();
}

static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
{
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
var multiplier = 1;
var value = 0;
byte encodedByte;
var index = 1;
headerLength = 0;
bodyLength = 0;
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory().Span;

do
{
if (index == temp.Length)
{
return false;
}

encodedByte = temp[index];
index++;

value += (byte)(encodedByte & 127) * multiplier;
if (multiplier > 128 * 128 * 128)
{
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
}

multiplier *= 128;
} while ((encodedByte & 128) != 0);

input = input.Slice(index);

headerLength = index;
bodyLength = value;
return true;
}
}
}

+ 105
- 0
Source/BPA.MQTTnet.AspnetCore/ServiceCollectionExtensions.cs Datei anzeigen

@@ -0,0 +1,105 @@
// 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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.Server;

namespace MQTTnet.AspNetCore
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action<MqttServerOptionsBuilder> configure = null)
{
if (serviceCollection is null)
{
throw new ArgumentNullException(nameof(serviceCollection));
}

serviceCollection.AddMqttConnectionHandler();
serviceCollection.AddHostedMqttServer(configure);
return serviceCollection;
}

public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));

services.AddSingleton(options);

services.AddHostedMqttServer();

return services;
}

public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure = null)
{
services.AddSingleton(s =>
{
var serverOptionsBuilder = new MqttServerOptionsBuilder();
configure?.Invoke(serverOptionsBuilder);
return serverOptionsBuilder.Build();
});

services.AddHostedMqttServer();

return services;
}

public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
{
services.AddSingleton(s =>
{
var builder = new AspNetMqttServerOptionsBuilder(s);
configure(builder);
return builder.Build();
});

services.AddHostedMqttServer();

return services;
}

static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
var logger = new MqttNetEventLogger();

services.AddSingleton<IMqttNetLogger>(logger);
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());

return services;
}

public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttWebSocketServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());

return services;
}

public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttTcpServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());

return services;
}

public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
{
services.AddSingleton<MqttConnectionHandler>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttConnectionHandler>());

return services;
}
}
}

+ 41
- 0
Source/BPA.MQTTnet/Adapter/IMqttChannelAdapter.cs Datei anzeigen

@@ -0,0 +1,41 @@
// 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.Formatter;
using MQTTnet.Packets;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;


namespace MQTTnet.Adapter
{
public interface IMqttChannelAdapter : IDisposable
{
string Endpoint { get; }

bool IsSecureConnection { get; }

X509Certificate2 ClientCertificate { get; }

MqttPacketFormatterAdapter PacketFormatterAdapter { get; }

long BytesSent { get; }

long BytesReceived { get; }

bool IsReadingPacket { get; }

Task ConnectAsync(CancellationToken cancellationToken);

Task DisconnectAsync(CancellationToken cancellationToken);

Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken);

Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken);

void ResetStatistics();
}
}

+ 14
- 0
Source/BPA.MQTTnet/Adapter/IMqttClientAdapterFactory.cs Datei anzeigen

@@ -0,0 +1,14 @@
// 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.Client;
using MQTTnet.Diagnostics;

namespace MQTTnet.Adapter
{
public interface IMqttClientAdapterFactory
{
IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger);
}
}

+ 19
- 0
Source/BPA.MQTTnet/Adapter/IMqttServerAdapter.cs Datei anzeigen

@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Server;

namespace MQTTnet.Adapter
{
public interface IMqttServerAdapter : IDisposable
{
Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }

Task StartAsync(MqttServerOptions options, IMqttNetLogger logger);
Task StopAsync();
}
}

+ 426
- 0
Source/BPA.MQTTnet/Adapter/MqttChannelAdapter.cs Datei anzeigen

@@ -0,0 +1,426 @@
// 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.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Internal;
using MQTTnet.Packets;

namespace MQTTnet.Adapter
{
public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
{
const uint ErrorOperationAborted = 0x800703E3;
const int ReadBufferSize = 4096;
readonly IMqttChannel _channel;
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly MqttNetSourceLogger _logger;

readonly MqttPacketInspector _packetInspector;

readonly byte[] _singleByteBuffer = new byte[1];

readonly AsyncLock _syncRoot = new AsyncLock();

long _bytesReceived;
long _bytesSent;

public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
_packetInspector = packetInspector;

PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));

if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}

_logger = logger.WithSource(nameof(MqttChannelAdapter));
}

public long BytesReceived => Interlocked.Read(ref _bytesReceived);

public long BytesSent => Interlocked.Read(ref _bytesSent);

public X509Certificate2 ClientCertificate => _channel.ClientCertificate;

public string Endpoint => _channel.Endpoint;

public bool IsReadingPacket { get; private set; }

public bool IsSecureConnection => _channel.IsSecureConnection;

public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }

public async Task ConnectAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();

try
{
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
if (!WrapAndThrowException(exception))
{
throw;
}
}
}

public async Task DisconnectAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();

try
{
await _channel.DisconnectAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
if (!WrapAndThrowException(exception))
{
throw;
}
}
}

public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();

try
{
_packetInspector?.BeginReceivePacket();

ReceivedMqttPacket receivedPacket;
var receivedPacketTask = ReceiveAsync(cancellationToken);
if (receivedPacketTask.IsCompleted)
{
receivedPacket = receivedPacketTask.Result;
}
else
{
receivedPacket = await receivedPacketTask.ConfigureAwait(false);
}

if (receivedPacket.TotalLength == 0 || cancellationToken.IsCancellationRequested)
{
return null;
}

_packetInspector?.EndReceivePacket();

Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);

if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
PacketFormatterAdapter.DetectProtocolVersion(receivedPacket);
}

var packet = PacketFormatterAdapter.Decode(receivedPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}

_logger.Verbose("RX ({0} bytes) <<< {1}", receivedPacket.TotalLength, packet);

return packet;
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
catch (Exception exception)
{
if (!WrapAndThrowException(exception))
{
throw;
}
}

return null;
}

public void ResetStatistics()
{
Interlocked.Exchange(ref _bytesReceived, 0L);
Interlocked.Exchange(ref _bytesSent, 0L);
}

public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
{
ThrowIfDisposed();

// 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.WaitAsync(cancellationToken).ConfigureAwait(false))
{
// Check for cancellation here again because "WaitAsync" might take some time.
cancellationToken.ThrowIfCancellationRequested();

try
{
var packetBuffer = PacketFormatterAdapter.Encode(packet);
_packetInspector?.BeginSendPacket(packetBuffer);

_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.Payload.Array, packetBuffer.Payload.Offset, packetBuffer.Payload.Count, cancellationToken).ConfigureAwait(false);
}

Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
}
catch (Exception exception)
{
if (!WrapAndThrowException(exception))
{
throw;
}
}
finally
{
PacketFormatterAdapter.Cleanup();
}
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_channel.Dispose();
_syncRoot.Dispose();
}

base.Dispose(disposing);
}

async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
int encodedByte = initialEncodedByte;

while ((encodedByte & 128) != 0)
{
offset++;
if (offset > 3)
{
throw new MqttProtocolViolationException("Remaining length is invalid.");
}

if (cancellationToken.IsCancellationRequested)
{
return -1;
}

var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);

if (cancellationToken.IsCancellationRequested)
{
return -1;
}

if (readCount == 0)
{
return -1;
}

_packetInspector?.FillReceiveBuffer(_singleByteBuffer);

encodedByte = _singleByteBuffer[0];

value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}

return value;
}

async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
var buffer = _fixedHeaderBuffer;
var totalBytesRead = 0;

while (totalBytesRead < buffer.Length)
{
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);

if (cancellationToken.IsCancellationRequested)
{
return ReadFixedHeaderResult.Cancelled;
}

if (bytesRead == 0)
{
return ReadFixedHeaderResult.ConnectionClosed;
}

totalBytesRead += bytesRead;
}

_packetInspector?.FillReceiveBuffer(buffer);

var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
};
}

var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);

if (bodyLength == -1)
{
return new ReadFixedHeaderResult
{
IsConnectionClosed = true
};
}

totalBytesRead += bodyLength;
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead)
};
}

async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return ReceivedMqttPacket.Empty;
}

var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false);

if (cancellationToken.IsCancellationRequested)
{
return ReceivedMqttPacket.Empty;
}

if (readFixedHeaderResult.IsConnectionClosed)
{
return ReceivedMqttPacket.Empty;
}

try
{
IsReadingPacket = true;

var fixedHeader = readFixedHeaderResult.FixedHeader;
if (fixedHeader.RemainingLength == 0)
{
return new ReceivedMqttPacket(fixedHeader.Flags, PlatformAbstractionLayer.EmptyByteArraySegment, 2);
}

var bodyLength = fixedHeader.RemainingLength;
var body = new byte[bodyLength];

var bodyOffset = 0;
var chunkSize = Math.Min(ReadBufferSize, bodyLength);

do
{
var bytesLeft = body.Length - bodyOffset;
if (chunkSize > bytesLeft)
{
chunkSize = bytesLeft;
}

var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);

if (cancellationToken.IsCancellationRequested)
{
return ReceivedMqttPacket.Empty;
}

if (readBytes == 0)
{
return ReceivedMqttPacket.Empty;
}

bodyOffset += readBytes;
} while (bodyOffset < bodyLength);

_packetInspector?.FillReceiveBuffer(body);

var bodySegment = new ArraySegment<byte>(body, 0, bodyLength);
return new ReceivedMqttPacket(fixedHeader.Flags, bodySegment, fixedHeader.TotalLength);
}
finally
{
IsReadingPacket = false;
}
}

static bool WrapAndThrowException(Exception exception)
{
if (exception is OperationCanceledException || exception is MqttCommunicationTimedOutException || exception is MqttCommunicationException ||
exception is MqttProtocolViolationException)
{
return false;
}

if (exception is IOException && exception.InnerException is SocketException innerException)
{
exception = innerException;
}

if (exception is SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.OperationAborted)
{
throw new OperationCanceledException();
}

if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new MqttCommunicationException(socketException);
}
}

if (exception is COMException comException)
{
if ((uint)comException.HResult == ErrorOperationAborted)
{
throw new OperationCanceledException();
}
}

throw new MqttCommunicationException(exception);
}
}
}

+ 23
- 0
Source/BPA.MQTTnet/Adapter/MqttConnectingFailedException.cs Datei anzeigen

@@ -0,0 +1,23 @@
// 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.Client;
using MQTTnet.Exceptions;

namespace MQTTnet.Adapter
{
public sealed class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(string message, Exception innerException, MqttClientConnectResult connectResult)
: base(message, innerException)
{
Result = connectResult;
}

public MqttClientConnectResult Result { get; }

public MqttClientConnectResultCode ResultCode => Result?.ResultCode ?? MqttClientConnectResultCode.UnspecifiedError;
}
}

+ 99
- 0
Source/BPA.MQTTnet/Adapter/MqttPacketInspector.cs Datei anzeigen

@@ -0,0 +1,99 @@
// 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.IO;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Internal;

namespace MQTTnet.Adapter
{
public sealed class MqttPacketInspector
{
readonly MqttNetSourceLogger _logger;
readonly AsyncEvent<InspectMqttPacketEventArgs> _asyncEvent;
MemoryStream _receivedPacketBuffer;
public MqttPacketInspector(AsyncEvent<InspectMqttPacketEventArgs> asyncEvent, IMqttNetLogger logger)
{
_asyncEvent = asyncEvent ?? throw new ArgumentNullException(nameof(asyncEvent));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.WithSource(nameof(MqttPacketInspector));
}

public void BeginReceivePacket()
{
if (!_asyncEvent.HasHandlers)
{
return;
}

if (_receivedPacketBuffer == null)
{
_receivedPacketBuffer = new MemoryStream();
}
_receivedPacketBuffer?.SetLength(0);
}

public void EndReceivePacket()
{
if (!_asyncEvent.HasHandlers)
{
return;
}

var buffer = _receivedPacketBuffer.ToArray();
_receivedPacketBuffer.SetLength(0);

InspectPacket(buffer, MqttPacketFlowDirection.Inbound);
}

public void BeginSendPacket(MqttPacketBuffer buffer)
{
if (!_asyncEvent.HasHandlers)
{
return;
}

// Create a copy of the actual packet so that the inspector gets no access
// to the internal buffers. This is waste of memory but this feature is only
// intended for debugging etc. so that this is OK.
var bufferCopy = buffer.ToArray();

InspectPacket(bufferCopy, MqttPacketFlowDirection.Outbound);
}

public void FillReceiveBuffer(byte[] buffer)
{
if (!_asyncEvent.HasHandlers)
{
return;
}
_receivedPacketBuffer?.Write(buffer, 0, buffer.Length);
}

void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction)
{
try
{
var eventArgs = new InspectMqttPacketEventArgs
{
Buffer = buffer,
Direction = direction
};

_asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult();
}
catch (Exception exception)
{
_logger.Error(exception, "Error while inspecting packet.");
}
}
}
}

+ 26
- 0
Source/BPA.MQTTnet/Adapter/ReceivedMqttPacket.cs Datei anzeigen

@@ -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;

namespace MQTTnet.Adapter
{
public readonly struct ReceivedMqttPacket
{
public static readonly ReceivedMqttPacket Empty = new ReceivedMqttPacket();
public ReceivedMqttPacket(byte fixedHeader, ArraySegment<byte> body, int totalLength)
{
FixedHeader = fixedHeader;
Body = body;
TotalLength = totalLength;
}

public byte FixedHeader { get; }

public ArraySegment<byte> Body { get; }

public int TotalLength { get; }
}
}

+ 97
- 0
Source/BPA.MQTTnet/BPA.MQTTnet.csproj Datei anzeigen

@@ -0,0 +1,97 @@
<Project Sdk="Microsoft.NET.Sdk">

<Target Name="ImportReleaseNotes" BeforeTargets="GenerateNuspec">
<ReadLinesFromFile File="..\..\.github\workflows\ReleaseNotes.md">
<Output TaskParameter="Lines" ItemName="ReleaseNotes" />
</ReadLinesFromFile>
<PropertyGroup>
<PackageReleaseNotes>@(ReleaseNotes, '%0a')</PackageReleaseNotes>
</PropertyGroup>
</Target>

<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<LangVersion>7.3</LangVersion>

<AssemblyName>MQTTnet</AssemblyName>
<RootNamespace>MQTTnet</RootNamespace>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Company>The contributors of MQTTnet</Company>
<Product>BPA.MQTTnet</Product>
<Description>BPA.MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</Description>
<Authors>The contributors of MQTTnet</Authors>
<PackageId>BPA.MQTTnet</PackageId>
<DelaySign>false</DelaySign>
<SignAssembly>false</SignAssembly>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Copyright>Christian Kratky 2016-2022</Copyright>
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)'=='uap10.0'">
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
<DefaultLanguage>en</DefaultLanguage>
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DebugType>Full</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
<PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.10" />
</ItemGroup>

</Project>

+ 32
- 0
Source/BPA.MQTTnet/Certificates/BlobCertificateProvider.cs Datei anzeigen

@@ -0,0 +1,32 @@
// 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.Security.Cryptography.X509Certificates;

namespace MQTTnet.Certificates
{
public class BlobCertificateProvider : ICertificateProvider
{
public BlobCertificateProvider(byte[] blob)
{
Blob = blob ?? throw new ArgumentNullException(nameof(blob));
}

public byte[] Blob { get; }

public string Password { get; set; }

public X509Certificate2 GetCertificate()
{
if (string.IsNullOrEmpty(Password))
{
// Use a different overload when no password is specified. Otherwise the constructor will fail.
return new X509Certificate2(Blob);
}

return new X509Certificate2(Blob, Password);
}
}
}

+ 13
- 0
Source/BPA.MQTTnet/Certificates/ICertificateProvider.cs Datei anzeigen

@@ -0,0 +1,13 @@
// 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.Security.Cryptography.X509Certificates;

namespace MQTTnet.Certificates
{
public interface ICertificateProvider
{
X509Certificate2 GetCertificate();
}
}

+ 26
- 0
Source/BPA.MQTTnet/Certificates/X509CertificateProvider.cs Datei anzeigen

@@ -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.

#if !WINDOWS_UWP
using System;
using System.Security.Cryptography.X509Certificates;

namespace MQTTnet.Certificates
{
public class X509CertificateProvider : ICertificateProvider
{
readonly X509Certificate2 _certificate;

public X509CertificateProvider(X509Certificate2 certificate)
{
_certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));
}

public X509Certificate2 GetCertificate()
{
return _certificate;
}
}
}
#endif

+ 24
- 0
Source/BPA.MQTTnet/Channel/IMqttChannel.cs Datei anzeigen

@@ -0,0 +1,24 @@
// 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.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;

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(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
}
}

+ 120
- 0
Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResult.cs Datei anzeigen

@@ -0,0 +1,120 @@
// 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 MQTTnet.Packets;
using MQTTnet.Protocol;

namespace MQTTnet.Client
{
public sealed class MqttClientConnectResult
{
/// <summary>
/// Gets the result code.
/// MQTTv5 only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; internal set; }

/// <summary>
/// Gets a value indicating whether a session was already available or not.
/// MQTTv5 only.
/// </summary>
public bool IsSessionPresent { get; internal set; }

/// <summary>
/// Gets a value indicating whether wildcards can be used in subscriptions at the current server.
/// MQTTv5 only.
/// </summary>
public bool WildcardSubscriptionAvailable { get; internal set; }

/// <summary>
/// Gets whether the server supports retained messages.
/// MQTTv5 only.
/// </summary>
public bool RetainAvailable { get; internal set; }

/// <summary>
/// Gets the client identifier which was chosen by the server.
/// MQTTv5 only.
/// </summary>
public string AssignedClientIdentifier { get; internal set; }

/// <summary>
/// Gets the authentication method.
/// MQTTv5 only.
/// </summary>
public string AuthenticationMethod { get; internal set; }

/// <summary>
/// Gets the authentication data.
/// MQTTv5 only.
/// </summary>
public byte[] AuthenticationData { get; internal set; }

public uint? MaximumPacketSize { get; internal set; }

/// <summary>
/// Gets the reason string.
/// MQTTv5 only.
/// </summary>
public string ReasonString { get; internal set; }

public ushort? ReceiveMaximum { get; internal set; }
/// <summary>
/// Gets the maximum QoS which is supported by the server.
/// MQTTv5 only.
/// </summary>
public MqttQualityOfServiceLevel MaximumQoS { get; internal set; }

/// <summary>
/// Gets the response information.
/// MQTTv5 only.
/// </summary>
public string ResponseInformation { get; internal set; }

/// <summary>
/// Gets the maximum value for a topic alias. 0 means not supported.
/// MQTTv5 only.
/// </summary>
public ushort TopicAliasMaximum { get; internal set; }

/// <summary>
/// Gets an alternate server which should be used instead of the current one.
/// MQTTv5 only.
/// </summary>
public string ServerReference { get; internal set; }

/// <summary>
/// MQTTv5 only.
/// Gets the keep alive interval which was chosen by the server instead of the
/// keep alive interval from the client CONNECT packet.
/// A value of 0 indicates that the feature is not used.
/// </summary>
public ushort ServerKeepAlive { get; internal set; }

public uint? SessionExpiryInterval { get; internal set; }

/// <summary>
/// Gets a value indicating whether the subscription identifiers are available or not.
/// MQTTv5 only.
/// </summary>
public bool SubscriptionIdentifiersAvailable { get; internal set; }

/// <summary>
/// Gets a value indicating whether the shared subscriptions are available or not.
/// MQTTv5 only.
/// </summary>
public bool SharedSubscriptionAvailable { get; internal set; }

/// <summary>
/// Gets 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.
/// MQTTv5 only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; internal set; }
}
}

+ 32
- 0
Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResultCode.cs Datei anzeigen

@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace MQTTnet.Client
{
public enum MqttClientConnectResultCode
{
Success = 0,
UnspecifiedError = 128,
MalformedPacket = 129,
ProtocolError = 130,
ImplementationSpecificError = 131,
UnsupportedProtocolVersion = 132,
ClientIdentifierNotValid = 133,
BadUserNameOrPassword = 134,
NotAuthorized = 135,
ServerUnavailable = 136,
ServerBusy = 137,
Banned = 138,
BadAuthenticationMethod = 140,
TopicNameInvalid = 144,
PacketTooLarge = 149,
QuotaExceeded = 151,
PayloadFormatInvalid = 153,
RetainNotSupported = 154,
QoSNotSupported = 155,
UseAnotherServer = 156,
ServerMoved = 157,
ConnectionRateExceeded = 159
}
}

+ 107
- 0
Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectResultFactory.cs Datei anzeigen

@@ -0,0 +1,107 @@
// 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;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;

namespace MQTTnet.Client
{
public sealed class MqttClientConnectResultFactory
{
public MqttClientConnectResult Create(MqttConnAckPacket connAckPacket, MqttProtocolVersion protocolVersion)
{
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));

if (protocolVersion == MqttProtocolVersion.V500)
{
return CreateForMqtt500(connAckPacket);
}

return CreateForMqtt311(connAckPacket);
}
static MqttClientConnectResult CreateForMqtt500(MqttConnAckPacket connAckPacket)
{
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));

return new MqttClientConnectResult
{
IsSessionPresent = connAckPacket.IsSessionPresent,
ResultCode = (MqttClientConnectResultCode) (int) connAckPacket.ReasonCode,
WildcardSubscriptionAvailable = connAckPacket.WildcardSubscriptionAvailable,
RetainAvailable = connAckPacket.RetainAvailable,
AssignedClientIdentifier = connAckPacket.AssignedClientIdentifier,
AuthenticationMethod = connAckPacket.AuthenticationMethod,
AuthenticationData = connAckPacket.AuthenticationData,
MaximumPacketSize = connAckPacket.MaximumPacketSize,
ReasonString = connAckPacket.ReasonString,
ReceiveMaximum = connAckPacket.ReceiveMaximum,
MaximumQoS = connAckPacket.MaximumQoS,
ResponseInformation = connAckPacket.ResponseInformation,
TopicAliasMaximum = connAckPacket.TopicAliasMaximum,
ServerReference = connAckPacket.ServerReference,
ServerKeepAlive = connAckPacket.ServerKeepAlive,
SessionExpiryInterval = connAckPacket.SessionExpiryInterval,
SubscriptionIdentifiersAvailable = connAckPacket.SubscriptionIdentifiersAvailable,
SharedSubscriptionAvailable = connAckPacket.SharedSubscriptionAvailable,
UserProperties = connAckPacket.UserProperties
};
}
static MqttClientConnectResult CreateForMqtt311(MqttConnAckPacket connAckPacket)
{
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));

return new MqttClientConnectResult
{
RetainAvailable = true, // Always true because v3.1.1 does not have a way to "disable" that feature.
WildcardSubscriptionAvailable = true, // Always true because v3.1.1 does not have a way to "disable" that feature.
IsSessionPresent = connAckPacket.IsSessionPresent,
ResultCode = ConvertReturnCodeToResultCode(connAckPacket.ReturnCode)
};
}

static MqttClientConnectResultCode ConvertReturnCodeToResultCode(MqttConnectReturnCode connectReturnCode)
{
switch (connectReturnCode)
{
case MqttConnectReturnCode.ConnectionAccepted:
{
return MqttClientConnectResultCode.Success;
}

case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
{
return MqttClientConnectResultCode.UnsupportedProtocolVersion;
}

case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
{
return MqttClientConnectResultCode.NotAuthorized;
}

case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
{
return MqttClientConnectResultCode.BadUserNameOrPassword;
}

case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
{
return MqttClientConnectResultCode.ClientIdentifierNotValid;
}

case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
{
return MqttClientConnectResultCode.ServerUnavailable;
}

default:
throw new MqttProtocolViolationException("Received unexpected return code.");
}
}
}
}

+ 22
- 0
Source/BPA.MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs Datei anzeigen

@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;

namespace MQTTnet.Client
{
public sealed class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult)
{
ConnectResult = connectResult ?? throw new ArgumentNullException(nameof(connectResult));
}

/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientConnectResult ConnectResult { get; }
}
}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.

Laden…
Abbrechen
Speichern