Skip to content

Commit

Permalink
[Client] Fix endpoint handling (#1999)
Browse files Browse the repository at this point in the history
* Fix endpoint handling

* Fix different behavior for Mono framework.

* Restore old properties to avoid breaking changes
  • Loading branch information
chkr1011 authored May 19, 2024
1 parent 32b6146 commit 290419b
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 12 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* [Client] Fix _None of the discovered or specified addresses match the socket address family._ (#1997).
* [Client] Remove the obsolete attribute from the _WithConnectionUri_ methods (#1979).

* All nuget packages are now signed.
7 changes: 4 additions & 3 deletions Source/MQTTnet.Tests/Internal/CrossPlatformSocket_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -17,7 +18,7 @@ public class CrossPlatformSocket_Tests
[TestMethod]
public async Task Connect_Send_Receive()
{
var crossPlatformSocket = new CrossPlatformSocket();
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
await crossPlatformSocket.ConnectAsync("www.google.de", 80, CancellationToken.None);

var requestBuffer = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\nHost: www.google.de\r\n\r\n");
Expand All @@ -36,7 +37,7 @@ public async Task Connect_Send_Receive()
[ExpectedException(typeof(OperationCanceledException))]
public async Task Try_Connect_Invalid_Host()
{
var crossPlatformSocket = new CrossPlatformSocket();
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);

var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5));
cancellationToken.Token.Register(() => crossPlatformSocket.Dispose());
Expand Down Expand Up @@ -65,7 +66,7 @@ public async Task Try_Connect_Invalid_Host()
[TestMethod]
public void Set_Options()
{
var crossPlatformSocket = new CrossPlatformSocket();
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);

Assert.IsFalse(crossPlatformSocket.ReuseAddress);
crossPlatformSocket.ReuseAddress = true;
Expand Down
28 changes: 24 additions & 4 deletions Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,26 @@ public MqttClientOptions Build()
{
if (_port.HasValue)
{
_remoteEndPoint = new DnsEndPoint(dns.Host, _port.Value);
_remoteEndPoint = new DnsEndPoint(dns.Host, _port.Value, dns.AddressFamily);
}
else
{
_remoteEndPoint = new DnsEndPoint(dns.Host, tlsOptions?.UseTls == false ? MqttPorts.Default : MqttPorts.Secure);
_remoteEndPoint = new DnsEndPoint(dns.Host, tlsOptions?.UseTls == false ? MqttPorts.Default : MqttPorts.Secure, dns.AddressFamily);
}
}
}

if (_remoteEndPoint is IPEndPoint ip)
{
if (ip.Port == 0)
{
if (_port.HasValue)
{
_remoteEndPoint = new IPEndPoint(ip.Address, _port.Value);
}
else
{
_remoteEndPoint = new IPEndPoint(ip.Address, tlsOptions?.UseTls == false ? MqttPorts.Default : MqttPorts.Secure);
}
}
}
Expand Down Expand Up @@ -352,13 +367,18 @@ public MqttClientOptionsBuilder WithSessionExpiryInterval(uint sessionExpiryInte
return this;
}

public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
public MqttClientOptionsBuilder WithTcpServer(string host, int? port = null, AddressFamily addressFamily = AddressFamily.Unspecified)
{
if (host == null)
{
throw new ArgumentNullException(nameof(host));
}

_tcpOptions = new MqttClientTcpOptions();

// The value 0 will be updated when building the options.
// This a backward compatibility feature.
_remoteEndPoint = new DnsEndPoint(server, port ?? 0);
_remoteEndPoint = new DnsEndPoint(host, port ?? 0, addressFamily);
_port = port;

return this;
Expand Down
7 changes: 7 additions & 0 deletions Source/MQTTnet/Client/Options/MqttClientTcpOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.Net;
using System.Net.Sockets;

Expand All @@ -20,6 +21,12 @@ public sealed class MqttClientTcpOptions : IMqttClientChannelOptions
/// </summary>
public bool? DualMode { get; set; }

[Obsolete("Use RemoteEndpoint or MqttClientOptionsBuilder instead.")]
public int? Port { get; set; }

[Obsolete("Use RemoteEndpoint or MqttClientOptionsBuilder instead.")]
public string Server { get; set; }

public LingerOption LingerState { get; set; } = new LingerOption(true, 0);

/// <summary>
Expand Down
27 changes: 23 additions & 4 deletions Source/MQTTnet/Implementations/CrossPlatformSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public CrossPlatformSocket(AddressFamily addressFamily, ProtocolType protocolTyp
#endif
}

public CrossPlatformSocket()
public CrossPlatformSocket(ProtocolType protocolType)
{
// Having this constructor is important because avoiding the address family as parameter
// will make use of dual mode in the .net framework.
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket = new Socket(SocketType.Stream, protocolType);

#if !NET5_0_OR_GREATER
_socketDisposeAction = _socket.Dispose;
Expand Down Expand Up @@ -202,9 +202,28 @@ public async Task ConnectAsync(EndPoint endPoint, CancellationToken cancellation
using (cancellationToken.Register(_socketDisposeAction))
{
#if NET452 || NET461
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, endPoint, null).ConfigureAwait(false);
// This is a fix for Mono which behaves differently than dotnet.
// The connection will not be established when the DNS endpoint is used.
if (endPoint is DnsEndPoint dns && dns.AddressFamily == AddressFamily.Unspecified)
{
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, dns.Host, dns.Port, null).ConfigureAwait(false);
}
else
{
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, endPoint, null).ConfigureAwait(false);
}
#else
await _socket.ConnectAsync(endPoint).ConfigureAwait(false);

// This is a fix for Mono which behaves differently than dotnet.
// The connection will not be established when the DNS endpoint is used.
if (endPoint is DnsEndPoint dns && dns.AddressFamily == AddressFamily.Unspecified)
{
await _socket.ConnectAsync(dns.Host, dns.Port).ConfigureAwait(false);
}
else
{
await _socket.ConnectAsync(endPoint).ConfigureAwait(false);
}
#endif
}
#endif
Expand Down
26 changes: 25 additions & 1 deletion Source/MQTTnet/Implementations/MqttTcpChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;

namespace MQTTnet.Implementations
{
Expand Down Expand Up @@ -63,7 +64,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (_tcpOptions.AddressFamily == AddressFamily.Unspecified)
{
socket = new CrossPlatformSocket();
socket = new CrossPlatformSocket(_tcpOptions.ProtocolType);
}
else
{
Expand Down Expand Up @@ -98,6 +99,29 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
socket.DualMode = _tcpOptions.DualMode.Value;
}

// This block is only for backward compatibility.
if (_tcpOptions.RemoteEndpoint == null && !string.IsNullOrEmpty(_tcpOptions.Server))

Check warning on line 103 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 103 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 103 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 103 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'
{
int port;
if (_tcpOptions.Port.HasValue)

Check warning on line 106 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 106 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 106 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 106 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'
{
port = _tcpOptions.Port.Value;

Check warning on line 108 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 108 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 108 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 108 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Port' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'
}
else
{
if (_tcpOptions.TlsOptions?.UseTls == true)
{
port = MqttPorts.Secure;
}
else
{
port = MqttPorts.Default;
}
}

_tcpOptions.RemoteEndpoint = new DnsEndPoint(_tcpOptions.Server, port, AddressFamily.Unspecified);

Check warning on line 122 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 122 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 122 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'

Check warning on line 122 in Source/MQTTnet/Implementations/MqttTcpChannel.cs

View workflow job for this annotation

GitHub Actions / build

'MqttClientTcpOptions.Server' is obsolete: 'Use RemoteEndpoint or MqttClientOptionsBuilder instead.'
}

await socket.ConnectAsync(_tcpOptions.RemoteEndpoint, cancellationToken).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();
Expand Down

0 comments on commit 290419b

Please sign in to comment.