diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 312186a7..d974a7ba 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -17,6 +17,7 @@ * [Client] Added method to trigger PING/PONG manually (connection check etc.). * [Client] Added support for certificate validation callback when using Web Sockets (requires netstandard2.1+). * [Client] Fixed a memory leak when web socket based connections trying to reconnect with an offline server. +* [Client] Fixed a memory leak when TCP based connections trying to reconnect with an offline server. * [Client] Fixed an issue when connecting to an invalid host (format). * [ManagedClient] Added method to trigger PING/PONG manually (connection check etc.). * [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1. diff --git a/Source/MQTTnet/Implementations/CrossPlatformSocket.cs b/Source/MQTTnet/Implementations/CrossPlatformSocket.cs index 28df8b26..2b3bed2f 100644 --- a/Source/MQTTnet/Implementations/CrossPlatformSocket.cs +++ b/Source/MQTTnet/Implementations/CrossPlatformSocket.cs @@ -143,7 +143,7 @@ namespace MQTTnet.Implementations { _networkStream?.Dispose(); - // Workaround for: workaround for https://github.com/dotnet/corefx/issues/24430 + // Workaround for: https://github.com/dotnet/corefx/issues/24430 using (cancellationToken.Register(() => _socket.Dispose())) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index 4449f202..a3ffab17 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -46,56 +46,63 @@ namespace MQTTnet.Implementations public async Task ConnectAsync(CancellationToken cancellationToken) { - CrossPlatformSocket socket; - - if (_options.AddressFamily == AddressFamily.Unspecified) + CrossPlatformSocket socket = null; + try { - socket = new CrossPlatformSocket(); - } - else - { - socket = new CrossPlatformSocket(_options.AddressFamily); - } - - socket.ReceiveBufferSize = _options.BufferSize; - socket.SendBufferSize = _options.BufferSize; - socket.NoDelay = _options.NoDelay; - - if (_options.DualMode.HasValue) - { - // It is important to avoid setting the flag if no specific value is set by the user - // because on IPv4 only networks the setter will always throw an exception. Regardless - // of the actual value. - socket.DualMode = _options.DualMode.Value; - } - - await socket.ConnectAsync(_options.Server, _options.GetPort(), cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - var networkStream = socket.GetStream(); - - if (_options.TlsOptions?.UseTls == true) - { - var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); - try + if (_options.AddressFamily == AddressFamily.Unspecified) { - await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); + socket = new CrossPlatformSocket(); } - catch + else { - sslStream.Dispose(); - throw; + socket = new CrossPlatformSocket(_options.AddressFamily); } - _stream = sslStream; - } - else - { - _stream = networkStream; - } + socket.ReceiveBufferSize = _options.BufferSize; + socket.SendBufferSize = _options.BufferSize; + socket.NoDelay = _options.NoDelay; - Endpoint = socket.RemoteEndPoint?.ToString(); + if (_options.DualMode.HasValue) + { + // It is important to avoid setting the flag if no specific value is set by the user + // because on IPv4 only networks the setter will always throw an exception. Regardless + // of the actual value. + socket.DualMode = _options.DualMode.Value; + } + + await socket.ConnectAsync(_options.Server, _options.GetPort(), cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + var networkStream = socket.GetStream(); + + if (_options.TlsOptions?.UseTls == true) + { + var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); + try + { + await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); + } + catch + { + sslStream.Dispose(); + throw; + } + + _stream = sslStream; + } + else + { + _stream = networkStream; + } + + Endpoint = socket.RemoteEndPoint?.ToString(); + } + catch (Exception) + { + socket?.Dispose(); + throw; + } } public Task DisconnectAsync(CancellationToken cancellationToken)