mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-20 19:58:57 +08:00
v4.0.0 (#1357)
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
// 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;
|
||||
@@ -43,10 +47,16 @@ namespace MQTTnet.Implementations
|
||||
// We cannot use the _NoDelay_ property from the socket because there is an issue in .NET 4.5.2, 4.6.
|
||||
// The decompiled code is: this.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Debug, value ? 1 : 0);
|
||||
// Which is wrong because the "NoDelay" should be set and not "Debug".
|
||||
get => (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
|
||||
get => (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) != 0;
|
||||
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public LingerOption LingerState
|
||||
{
|
||||
get => _socket.LingerState;
|
||||
set => _socket.LingerState = value;
|
||||
}
|
||||
|
||||
public bool DualMode
|
||||
{
|
||||
get => _socket.DualMode;
|
||||
@@ -58,7 +68,7 @@ namespace MQTTnet.Implementations
|
||||
get => _socket.ReceiveBufferSize;
|
||||
set => _socket.ReceiveBufferSize = value;
|
||||
}
|
||||
|
||||
|
||||
public int SendBufferSize
|
||||
{
|
||||
get => _socket.SendBufferSize;
|
||||
@@ -79,6 +89,8 @@ namespace MQTTnet.Implementations
|
||||
set => _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public bool IsConnected => _socket.Connected;
|
||||
|
||||
public async Task<CrossPlatformSocket> AcceptAsync()
|
||||
{
|
||||
try
|
||||
@@ -97,9 +109,14 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public EndPoint LocalEndPoint => _socket.LocalEndPoint;
|
||||
|
||||
public void Bind(EndPoint localEndPoint)
|
||||
{
|
||||
if (localEndPoint is null) throw new ArgumentNullException(nameof(localEndPoint));
|
||||
if (localEndPoint is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(localEndPoint));
|
||||
}
|
||||
|
||||
_socket.Bind(localEndPoint);
|
||||
}
|
||||
@@ -111,24 +128,31 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
|
||||
{
|
||||
if (host is null) throw new ArgumentNullException(nameof(host));
|
||||
if (host is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
_networkStream?.Dispose();
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
await _socket.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(_socketDisposeAction))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
#if NET452 || NET461
|
||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, host, port, null).ConfigureAwait(false);
|
||||
#else
|
||||
await _socket.ConnectAsync(host, port).ConfigureAwait(false);
|
||||
#endif
|
||||
_networkStream = new NetworkStream(_socket, true);
|
||||
}
|
||||
#endif
|
||||
_networkStream = new NetworkStream(_socket, true);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
@@ -137,6 +161,11 @@ namespace MQTTnet.Implementations
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
if (socketException.SocketErrorCode == SocketError.TimedOut)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException();
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException($"Error while connecting with host '{host}:{port}'.", socketException);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
@@ -197,7 +226,7 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
|
||||
#if NET452 || NET461
|
||||
class SocketWrapper
|
||||
sealed class SocketWrapper
|
||||
{
|
||||
readonly Socket _socket;
|
||||
readonly ArraySegment<byte> _buffer;
|
||||
@@ -224,4 +253,4 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Options;
|
||||
// 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.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Client;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class MqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
public sealed class MqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
readonly IMqttNetLogger _logger;
|
||||
|
||||
public MqttClientAdapterFactory(IMqttNetLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options)
|
||||
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -42,8 +38,9 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter());
|
||||
return new MqttChannelAdapter(channel, packetFormatterAdapter, options.PacketInspector, _logger);
|
||||
var bufferWriter = new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax);
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, bufferWriter);
|
||||
return new MqttChannelAdapter(channel, packetFormatterAdapter, packetInspector, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
@@ -12,7 +16,7 @@ using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Security.Cryptography.Certificates;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
@@ -26,13 +30,13 @@ namespace MQTTnet.Implementations
|
||||
Stream _readStream;
|
||||
Stream _writeStream;
|
||||
|
||||
public MqttTcpChannel(IMqttClientOptions clientOptions)
|
||||
public MqttTcpChannel(MqttClientOptions clientOptions)
|
||||
{
|
||||
_options = (MqttClientTcpOptions)clientOptions.ChannelOptions;
|
||||
_bufferSize = _options.BufferSize;
|
||||
}
|
||||
|
||||
public MqttTcpChannel(StreamSocket socket, X509Certificate2 clientCertificate, IMqttServerOptions serverOptions)
|
||||
public MqttTcpChannel(StreamSocket socket, X509Certificate2 clientCertificate, MqttServerOptions serverOptions)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
_bufferSize = serverOptions.DefaultEndpointOptions.BufferSize;
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
// 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 MQTTnet.Channel;
|
||||
using MQTTnet.Client.Options;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpChannel : IMqttChannel
|
||||
{
|
||||
readonly IMqttClientOptions _clientOptions;
|
||||
readonly MqttClientTcpOptions _tcpOptions;
|
||||
readonly MqttClientOptions _clientOptions;
|
||||
readonly Action _disposeAction;
|
||||
readonly MqttClientTcpOptions _tcpOptions;
|
||||
|
||||
Stream _stream;
|
||||
|
||||
@@ -27,7 +30,7 @@ namespace MQTTnet.Implementations
|
||||
_disposeAction = Dispose;
|
||||
}
|
||||
|
||||
public MqttTcpChannel(IMqttClientOptions clientOptions) : this()
|
||||
public MqttTcpChannel(MqttClientOptions clientOptions) : this()
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
_tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
|
||||
@@ -45,12 +48,12 @@ namespace MQTTnet.Implementations
|
||||
ClientCertificate = clientCertificate;
|
||||
}
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; }
|
||||
|
||||
public string Endpoint { get; private set; }
|
||||
|
||||
public bool IsSecureConnection { get; }
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; }
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CrossPlatformSocket socket = null;
|
||||
@@ -67,9 +70,14 @@ namespace MQTTnet.Implementations
|
||||
|
||||
socket.ReceiveBufferSize = _tcpOptions.BufferSize;
|
||||
socket.SendBufferSize = _tcpOptions.BufferSize;
|
||||
socket.SendTimeout = (int)_clientOptions.CommunicationTimeout.TotalMilliseconds;
|
||||
socket.SendTimeout = (int)_clientOptions.Timeout.TotalMilliseconds;
|
||||
socket.NoDelay = _tcpOptions.NoDelay;
|
||||
|
||||
if (socket.LingerState != null)
|
||||
{
|
||||
socket.LingerState = _tcpOptions.LingerState;
|
||||
}
|
||||
|
||||
if (_tcpOptions.DualMode.HasValue)
|
||||
{
|
||||
// It is important to avoid setting the flag if no specific value is set by the user
|
||||
@@ -89,7 +97,7 @@ namespace MQTTnet.Implementations
|
||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||
try
|
||||
{
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
var sslOptions = new SslClientAuthenticationOptions
|
||||
{
|
||||
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
|
||||
@@ -101,12 +109,13 @@ namespace MQTTnet.Implementations
|
||||
|
||||
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol,
|
||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
#if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0
|
||||
#if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
await sslStream.DisposeAsync().ConfigureAwait(false);
|
||||
#else
|
||||
sslStream.Dispose();
|
||||
@@ -137,6 +146,30 @@ namespace MQTTnet.Implementations
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// When the stream is disposed it will also close the socket and this will also dispose it.
|
||||
// So there is no need to dispose the socket again.
|
||||
// https://stackoverflow.com/questions/3601521/should-i-manually-dispose-the-socket-after-closing-it
|
||||
try
|
||||
{
|
||||
#if !NETSTANDARD1_3
|
||||
_stream?.Close();
|
||||
#endif
|
||||
_stream?.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -219,42 +252,12 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// When the stream is disposed it will also close the socket and this will also dispose it.
|
||||
// So there is no need to dispose the socket again.
|
||||
// https://stackoverflow.com/questions/3601521/should-i-manually-dispose-the-socket-after-closing-it
|
||||
try
|
||||
{
|
||||
_stream?.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
#region OBSOLETE
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var certificateValidationCallback = _tcpOptions?.TlsOptions?.CertificateValidationCallback;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
if (certificateValidationCallback != null)
|
||||
{
|
||||
return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
|
||||
}
|
||||
#endregion
|
||||
|
||||
var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler;
|
||||
if (certificateValidationHandler != null)
|
||||
{
|
||||
var context = new MqttClientCertificateValidationCallbackContext
|
||||
var eventArgs = new MqttClientCertificateValidationEventArgs
|
||||
{
|
||||
Certificate = x509Certificate,
|
||||
Chain = chain,
|
||||
@@ -262,31 +265,10 @@ namespace MQTTnet.Implementations
|
||||
ClientOptions = _tcpOptions
|
||||
};
|
||||
|
||||
return certificateValidationHandler(context);
|
||||
return certificateValidationHandler(eventArgs);
|
||||
}
|
||||
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
||||
{
|
||||
if (_tcpOptions?.TlsOptions?.IgnoreCertificateRevocationErrors != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
||||
{
|
||||
if (_tcpOptions?.TlsOptions?.IgnoreCertificateChainErrors != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _tcpOptions?.TlsOptions?.AllowUntrustedCertificates == true;
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
}
|
||||
|
||||
X509CertificateCollection LoadCertificates()
|
||||
@@ -306,4 +288,4 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,38 +1,40 @@
|
||||
// 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 Windows.Networking.Sockets;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
|
||||
IMqttServerOptions _options;
|
||||
IMqttNetLogger _rootLogger;
|
||||
MqttNetSourceLogger _logger;
|
||||
|
||||
MqttServerOptions _options;
|
||||
StreamSocketListener _listener;
|
||||
|
||||
public MqttTcpServerAdapter(IMqttNetLogger logger)
|
||||
{
|
||||
_rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public async Task StartAsync(IMqttServerOptions options)
|
||||
public async Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_listener != null) throw new InvalidOperationException("Server is already started.");
|
||||
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
_rootLogger = logger;
|
||||
_logger = logger.WithSource(nameof(MqttTcpServerAdapter));
|
||||
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
_listener = new StreamSocketListener();
|
||||
@@ -42,7 +44,7 @@ namespace MQTTnet.Implementations
|
||||
_listener.Control.KeepAlive = true;
|
||||
_listener.Control.QualityOfService = SocketQualityOfService.LowLatency;
|
||||
_listener.ConnectionReceived += OnConnectionReceivedAsync;
|
||||
|
||||
|
||||
await _listener.BindServiceNameAsync(options.DefaultEndpointOptions.Port.ToString(), SocketProtectionLevel.PlainSocket);
|
||||
}
|
||||
|
||||
@@ -88,8 +90,12 @@ namespace MQTTnet.Implementations
|
||||
_logger.Warning(exception, "Unable to convert UWP certificate to X509Certificate2.");
|
||||
}
|
||||
}
|
||||
|
||||
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, _rootLogger))
|
||||
|
||||
var bufferWriter = new MqttBufferWriter(4096, 65535);
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||
var tcpChannel = new MqttTcpChannel(args.Socket, clientCertificate, _options);
|
||||
|
||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, null, _rootLogger))
|
||||
{
|
||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
@@ -112,7 +118,7 @@ namespace MQTTnet.Implementations
|
||||
args.Socket.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
{
|
||||
_logger.Error(exception, "Error while cleaning up client connection.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#if !WINDOWS_UWP
|
||||
// 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 MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server;
|
||||
@@ -9,37 +13,31 @@ using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
|
||||
MqttServerOptions _serverOptions;
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public MqttTcpServerAdapter(IMqttNetLogger logger)
|
||||
{
|
||||
_rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public bool TreatSocketOpeningErrorAsWarning { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
|
||||
|
||||
_serverOptions = options;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
RegisterListeners(options.DefaultEndpointOptions, null, _cancellationTokenSource.Token);
|
||||
RegisterListeners(options.DefaultEndpointOptions, null, logger, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
if (options.TlsEndpointOptions?.IsEnabled == true)
|
||||
@@ -55,7 +53,7 @@ namespace MQTTnet.Implementations
|
||||
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
||||
}
|
||||
|
||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, _cancellationTokenSource.Token);
|
||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
@@ -92,11 +90,11 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
|
||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions tcpEndpointOptions, X509Certificate2 tlsCertificate, IMqttNetLogger logger, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!options.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||
if (!tcpEndpointOptions.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, options, tlsCertificate, _rootLogger)
|
||||
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
||||
{
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
@@ -107,9 +105,9 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||
if (!tcpEndpointOptions.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, options, tlsCertificate, _rootLogger)
|
||||
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
||||
{
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#if !WINDOWS_UWP
|
||||
// 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 MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
@@ -12,7 +16,6 @@ using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -21,6 +24,7 @@ namespace MQTTnet.Implementations
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
readonly AddressFamily _addressFamily;
|
||||
readonly MqttServerOptions _serverOptions;
|
||||
readonly MqttServerTcpEndpointBaseOptions _options;
|
||||
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
||||
readonly X509Certificate2 _tlsCertificate;
|
||||
@@ -30,12 +34,14 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public MqttTcpServerListener(
|
||||
AddressFamily addressFamily,
|
||||
MqttServerTcpEndpointBaseOptions options,
|
||||
MqttServerOptions serverOptions,
|
||||
MqttServerTcpEndpointBaseOptions tcpEndpointOptions,
|
||||
X509Certificate2 tlsCertificate,
|
||||
IMqttNetLogger logger)
|
||||
{
|
||||
_addressFamily = addressFamily;
|
||||
_options = options;
|
||||
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||
_options = tcpEndpointOptions ?? throw new ArgumentNullException(nameof(tcpEndpointOptions));
|
||||
_tlsCertificate = tlsCertificate;
|
||||
_rootLogger = logger;
|
||||
_logger = logger.WithSource(nameof(MqttTcpServerListener));
|
||||
@@ -60,12 +66,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
_localEndPoint = new IPEndPoint(boundIp, _options.Port);
|
||||
|
||||
_logger.Info("Starting TCP listener for {0} TLS={1}.", _localEndPoint, _tlsCertificate != null);
|
||||
_logger.Info("Starting TCP listener (Endpoint='{0}', TLS={1}).", _localEndPoint, _tlsCertificate != null);
|
||||
|
||||
_socket = new CrossPlatformSocket(_addressFamily);
|
||||
|
||||
// Usage of socket options is described here: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.setsocketoption?view=netcore-2.2
|
||||
|
||||
if (_options.ReuseAddress)
|
||||
{
|
||||
_socket.ReuseAddress = true;
|
||||
@@ -76,9 +81,22 @@ namespace MQTTnet.Implementations
|
||||
_socket.NoDelay = true;
|
||||
}
|
||||
|
||||
if (_options.LingerState != null)
|
||||
{
|
||||
_socket.LingerState = _options.LingerState;
|
||||
}
|
||||
|
||||
_socket.Bind(_localEndPoint);
|
||||
|
||||
// Get the local endpoint back from the socket. The port may have changed.
|
||||
// This can happen when port 0 is used. Then the OS will choose the next free port.
|
||||
_localEndPoint = (IPEndPoint)_socket.LocalEndPoint;
|
||||
_options.Port = _localEndPoint.Port;
|
||||
|
||||
_socket.Listen(_options.ConnectionBacklog);
|
||||
|
||||
_logger.Verbose("TCP listener started (Endpoint='{0}'.", _localEndPoint);
|
||||
|
||||
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
|
||||
|
||||
return true;
|
||||
@@ -179,11 +197,11 @@ namespace MQTTnet.Implementations
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
using (var clientAdapter = new MqttChannelAdapter(
|
||||
new MqttTcpChannel(stream, remoteEndPoint, clientCertificate),
|
||||
new MqttPacketFormatterAdapter(new MqttPacketWriter()),
|
||||
null,
|
||||
_rootLogger))
|
||||
var tcpChannel = new MqttTcpChannel(stream, remoteEndPoint, clientCertificate);
|
||||
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||
|
||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, null, _rootLogger))
|
||||
{
|
||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client.Options;
|
||||
// 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.Channel;
|
||||
using MQTTnet.Internal;
|
||||
using System;
|
||||
using System.Net;
|
||||
@@ -7,6 +10,7 @@ using System.Net.WebSockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -172,7 +176,7 @@ namespace MQTTnet.Implementations
|
||||
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||
{
|
||||
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
|
||||
var context = new MqttClientCertificateValidationCallbackContext
|
||||
var context = new MqttClientCertificateValidationEventArgs
|
||||
{
|
||||
Certificate = certificate,
|
||||
Chain = chain,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -16,6 +20,8 @@ namespace MQTTnet.Implementations
|
||||
public static byte[] EmptyByteArray { get; } = Array.Empty<byte>();
|
||||
#endif
|
||||
|
||||
public static ArraySegment<byte> EmptyByteArraySegment { get; } = new ArraySegment<byte>(EmptyByteArray);
|
||||
|
||||
public static void Sleep(TimeSpan timeout)
|
||||
{
|
||||
#if !NETSTANDARD1_3 && !WINDOWS_UWP
|
||||
|
||||
Reference in New Issue
Block a user