A socket on unix doesn't support reconnecting with the same file descriptor after a failed connection. This has created an issue for the .NET Socket design, whereby options can be set onto a Socket instance, and then that Socket instance can be connected to something that might actually require multiple attempts. To make this work, the Socket tracks known settings, and recreates the file descriptor under the covers upon a failed attempt. If an unknown setting is used, Socket refuses to connect to an endpoint that might possibly require multiple attempts, e.g. a DnsEndPoint. Unfortunately, we're not applying this consistently across such connect methods.
This:
using System.Net;
using System.Net.Sockets;
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 1);
await s.ConnectAsync(new DnsEndPoint("microsoft.com", 80));
throws the exception:
Unhandled exception. System.PlatformNotSupportedException: Sockets on this platform are invalid for use after a failed connection attempt.
at System.Net.Sockets.Socket.ThrowMultiConnectNotSupported()
at System.Net.Sockets.Socket.ConnectAsync(SocketAsyncEventArgs e, Boolean userSocket, Boolean saeaCancelable)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ConnectAsync(Socket socket)
at System.Net.Sockets.Socket.ConnectAsync(EndPoint remoteEP, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.ConnectAsync(EndPoint remoteEP)
at Program.<Main>$(String[] args)
But this:
using System.Net;
using System.Net.Sockets;
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 1);
await s.ConnectAsync(Dns.GetHostAddresses("microsoft.com"), 80);
completes successfully, even though they're logically the same, with the former just doing the DNS lookup and effectively then behaving as if that list was provided.
At this point, it'd likely be a significant breaking change to make the latter fail like the former.
We should reconsider the behavior for the former, and possibly revisit the whole retry mechanism, caching more aggressively such that few-to-no settings knock us off the golden path.
A socket on unix doesn't support reconnecting with the same file descriptor after a failed connection. This has created an issue for the .NET Socket design, whereby options can be set onto a Socket instance, and then that Socket instance can be connected to something that might actually require multiple attempts. To make this work, the Socket tracks known settings, and recreates the file descriptor under the covers upon a failed attempt. If an unknown setting is used, Socket refuses to connect to an endpoint that might possibly require multiple attempts, e.g. a DnsEndPoint. Unfortunately, we're not applying this consistently across such connect methods.
This:
throws the exception:
But this:
completes successfully, even though they're logically the same, with the former just doing the DNS lookup and effectively then behaving as if that list was provided.
At this point, it'd likely be a significant breaking change to make the latter fail like the former.
We should reconsider the behavior for the former, and possibly revisit the whole retry mechanism, caching more aggressively such that few-to-no settings knock us off the golden path.