mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-28 01:02:37 +08:00
Merge branch 'master' into feature/Single_Connection_Monitoring_Task
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||||
<description>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>
|
<description>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>
|
||||||
<releaseNotes>
|
<releaseNotes>
|
||||||
* [Server] ..
|
* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal).
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>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</tags>
|
<tags>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</tags>
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ using WebSocket4Net;
|
|||||||
|
|
||||||
namespace MQTTnet.Extensions.WebSocket4Net
|
namespace MQTTnet.Extensions.WebSocket4Net
|
||||||
{
|
{
|
||||||
public class WebSocket4NetMqttChannel : IMqttChannel
|
public sealed class WebSocket4NetMqttChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||||
|
|
||||||
private readonly IMqttClientOptions _clientOptions;
|
readonly IMqttClientOptions _clientOptions;
|
||||||
private readonly MqttClientWebSocketOptions _webSocketOptions;
|
readonly MqttClientWebSocketOptions _webSocketOptions;
|
||||||
|
|
||||||
private WebSocket _webSocket;
|
WebSocket _webSocket;
|
||||||
|
|
||||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
|
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
|
||||||
{
|
{
|
||||||
@@ -166,12 +166,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocket = null;
|
_webSocket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnError(object sender, ErrorEventArgs e)
|
static void OnError(object sender, ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.Write(e.Exception.ToString());
|
System.Diagnostics.Debug.Write(e.Exception.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataReceived(object sender, DataReceivedEventArgs e)
|
void OnDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
{
|
{
|
||||||
foreach (var @byte in e.Data)
|
foreach (var @byte in e.Data)
|
||||||
{
|
{
|
||||||
@@ -179,7 +179,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_webSocket.Error += OnError;
|
_webSocket.Error += OnError;
|
||||||
_webSocket.DataReceived += OnDataReceived;
|
_webSocket.DataReceived += OnDataReceived;
|
||||||
|
|||||||
-134
@@ -1,134 +0,0 @@
|
|||||||
using MQTTnet.Client;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
using WebSocket4Net;
|
|
||||||
|
|
||||||
namespace MQTTnet.TestApp.NetCore
|
|
||||||
{
|
|
||||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
|
||||||
{
|
|
||||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
if (!(options.ChannelOptions is MqttClientWebSocketOptions))
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Only WebSocket connections are supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options), new MqttPacketSerializer(), logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WebSocket4NetMqttChannel : IMqttChannel
|
|
||||||
{
|
|
||||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
|
||||||
|
|
||||||
private readonly IMqttClientOptions _clientOptions;
|
|
||||||
private WebSocket4Net.WebSocket _webSocket;
|
|
||||||
|
|
||||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions)
|
|
||||||
{
|
|
||||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Endpoint { get; } = "";
|
|
||||||
|
|
||||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var channelOptions = (MqttClientWebSocketOptions)_clientOptions.ChannelOptions;
|
|
||||||
|
|
||||||
var uri = "ws://" + channelOptions.Uri;
|
|
||||||
var sslProtocols = SslProtocols.None;
|
|
||||||
|
|
||||||
if (channelOptions.TlsOptions.UseTls)
|
|
||||||
{
|
|
||||||
uri = "wss://" + channelOptions.Uri;
|
|
||||||
sslProtocols = SslProtocols.Tls12;
|
|
||||||
}
|
|
||||||
|
|
||||||
var subProtocol = channelOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
|
||||||
|
|
||||||
_webSocket = new WebSocket4Net.WebSocket(uri, subProtocol, sslProtocols: sslProtocols);
|
|
||||||
_webSocket.DataReceived += OnDataReceived;
|
|
||||||
_webSocket.Open();
|
|
||||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Open, _clientOptions.CommunicationTimeout);
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
if (_webSocket != null)
|
|
||||||
{
|
|
||||||
_webSocket.DataReceived -= OnDataReceived;
|
|
||||||
_webSocket.Close();
|
|
||||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Closed, _clientOptions.CommunicationTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
_webSocket = null;
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var readBytes = 0;
|
|
||||||
while (count > 0 && !cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
byte @byte;
|
|
||||||
if (readBytes == 0)
|
|
||||||
{
|
|
||||||
// Block until at lease one byte was received.
|
|
||||||
@byte = _receiveBuffer.Take(cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_receiveBuffer.TryTake(out @byte))
|
|
||||||
{
|
|
||||||
return Task.FromResult(readBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[offset] = @byte;
|
|
||||||
offset++;
|
|
||||||
count--;
|
|
||||||
readBytes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(readBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_webSocket.Send(buffer, offset, count);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_webSocket != null)
|
|
||||||
{
|
|
||||||
_webSocket.DataReceived -= OnDataReceived;
|
|
||||||
_webSocket.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e)
|
|
||||||
{
|
|
||||||
foreach (var @byte in e.Data)
|
|
||||||
{
|
|
||||||
_receiveBuffer.Add(@byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -212,9 +212,15 @@ namespace MQTTnet.Server.Mqtt
|
|||||||
// Configure encrypted connections
|
// Configure encrypted connections
|
||||||
if (_settings.EncryptedTcpEndPoint.Enabled)
|
if (_settings.EncryptedTcpEndPoint.Enabled)
|
||||||
{
|
{
|
||||||
|
#if NETCOREAPP3_1 || NET5_0
|
||||||
|
options
|
||||||
|
.WithEncryptedEndpoint()
|
||||||
|
.WithEncryptionSslProtocol(SslProtocols.Tls13);
|
||||||
|
#else
|
||||||
options
|
options
|
||||||
.WithEncryptedEndpoint()
|
.WithEncryptedEndpoint()
|
||||||
.WithEncryptionSslProtocol(SslProtocols.Tls12);
|
.WithEncryptionSslProtocol(SslProtocols.Tls12);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
|
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ using MQTTnet.Client.Connecting;
|
|||||||
|
|
||||||
namespace MQTTnet.Client.Disconnecting
|
namespace MQTTnet.Client.Disconnecting
|
||||||
{
|
{
|
||||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
public sealed class MqttClientDisconnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reasonCode)
|
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reason)
|
||||||
{
|
{
|
||||||
ClientWasConnected = clientWasConnected;
|
ClientWasConnected = clientWasConnected;
|
||||||
Exception = exception;
|
Exception = exception;
|
||||||
AuthenticateResult = authenticateResult;
|
AuthenticateResult = authenticateResult;
|
||||||
ReasonCode = reasonCode;
|
Reason = reason;
|
||||||
|
|
||||||
|
ReasonCode = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ClientWasConnected { get; }
|
public bool ClientWasConnected { get; }
|
||||||
@@ -19,6 +21,9 @@ namespace MQTTnet.Client.Disconnecting
|
|||||||
|
|
||||||
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
||||||
|
|
||||||
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
public MqttClientDisconnectReason Reason { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Please use 'Reason' instead. This property will be removed in the future!")]
|
||||||
|
public MqttClientDisconnectReason ReasonCode { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ namespace MQTTnet.Client.Options
|
|||||||
|
|
||||||
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
|
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1 || NET5_0
|
||||||
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
|
||||||
|
#else
|
||||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
|
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ namespace MQTTnet.Client.Options
|
|||||||
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1 || NET5_0
|
||||||
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
|
||||||
|
#else
|
||||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||||
|
#endif
|
||||||
|
|
||||||
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
||||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
|
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
|
||||||
|
|||||||
@@ -75,11 +75,23 @@ namespace MQTTnet.Implementations
|
|||||||
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
|
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1 || NET5_0
|
||||||
|
var socketProtectionLevel = SocketProtectionLevel.Tls13;
|
||||||
|
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls12)
|
||||||
|
{
|
||||||
|
socketProtectionLevel = SocketProtectionLevel.Tls12;
|
||||||
|
}
|
||||||
|
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
|
||||||
|
{
|
||||||
|
socketProtectionLevel = SocketProtectionLevel.Tls11;
|
||||||
|
}
|
||||||
|
#else
|
||||||
var socketProtectionLevel = SocketProtectionLevel.Tls12;
|
var socketProtectionLevel = SocketProtectionLevel.Tls12;
|
||||||
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
|
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
|
||||||
{
|
{
|
||||||
socketProtectionLevel = SocketProtectionLevel.Tls11;
|
socketProtectionLevel = SocketProtectionLevel.Tls11;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
|
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
|
||||||
{
|
{
|
||||||
socketProtectionLevel = SocketProtectionLevel.Tls10;
|
socketProtectionLevel = SocketProtectionLevel.Tls10;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace MQTTnet.Implementations
|
|||||||
ApplicationProtocols = _options.TlsOptions.ApplicationProtocols,
|
ApplicationProtocols = _options.TlsOptions.ApplicationProtocols,
|
||||||
ClientCertificates = LoadCertificates(),
|
ClientCertificates = LoadCertificates(),
|
||||||
EnabledSslProtocols = _options.TlsOptions.SslProtocol,
|
EnabledSslProtocols = _options.TlsOptions.SslProtocol,
|
||||||
CertificateRevocationCheckMode = _options.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
CertificateRevocationCheckMode = _options.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : X509RevocationMode.Online,
|
||||||
TargetHost = _options.Server
|
TargetHost = _options.Server
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -103,6 +104,16 @@ namespace MQTTnet
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithQualityOfServiceLevel(int qualityOfServiceLevel)
|
||||||
|
{
|
||||||
|
if (qualityOfServiceLevel < 0 || qualityOfServiceLevel > 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(qualityOfServiceLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithQualityOfServiceLevel((MqttQualityOfServiceLevel)qualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithRetainFlag(bool value = true)
|
public MqttApplicationMessageBuilder WithRetainFlag(bool value = true)
|
||||||
{
|
{
|
||||||
_retain = value;
|
_retain = value;
|
||||||
|
|||||||
@@ -68,10 +68,13 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public IMqttClient CreateClient()
|
public IMqttClient CreateClient()
|
||||||
{
|
{
|
||||||
var client = _mqttFactory.CreateMqttClient(ClientLogger);
|
lock (_clients)
|
||||||
_clients.Add(client);
|
{
|
||||||
|
var client = _mqttFactory.CreateMqttClient(ClientLogger);
|
||||||
|
_clients.Add(client);
|
||||||
|
|
||||||
return new TestClientWrapper(client, TestContext);
|
return new TestClientWrapper(client, TestContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IMqttServer> StartServerAsync()
|
public Task<IMqttServer> StartServerAsync()
|
||||||
|
|||||||
@@ -469,11 +469,19 @@ namespace MQTTnet.Tests
|
|||||||
var client1 = await testEnvironment.ConnectClientAsync(options);
|
var client1 = await testEnvironment.ConnectClientAsync(options);
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
MqttClientDisconnectReason disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
|
||||||
|
client1.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(c =>
|
||||||
|
{
|
||||||
|
disconnectReason = c.Reason;
|
||||||
|
});
|
||||||
|
|
||||||
var client2 = await testEnvironment.ConnectClientAsync(options);
|
var client2 = await testEnvironment.ConnectClientAsync(options);
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
Assert.IsFalse(client1.IsConnected);
|
Assert.IsFalse(client1.IsConnected);
|
||||||
Assert.IsTrue(client2.IsConnected);
|
Assert.IsTrue(client2.IsConnected);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,11 +652,11 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
// Clear retained message.
|
// Clear retained message.
|
||||||
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
.WithPayload(new byte[0]).WithRetainFlag().Build());
|
.WithPayload(new byte[0]).WithRetainFlag().WithQualityOfServiceLevel(1).Build());
|
||||||
|
|
||||||
// Set retained message.
|
// Set retained message.
|
||||||
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
.WithPayload("value").WithRetainFlag().Build());
|
.WithPayload("value").WithRetainFlag().WithQualityOfServiceLevel(1).Build());
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user