runtime: Socket.Select on linux gives wrong result when fails to connect

Description

When running Socket.Select against failing port it gives different result on Windows and on Linux. Futhermore, it seems on Linux the result does not make any sense.

This code can be used to reproduce the issue:

        private static void Main()
        {   
            using var socket = new Socket( SocketType.Stream, ProtocolType.Tcp)
            {
                Blocking = false
            };
            
            try
            {
                socket.Connect("127.0.0.1", 41324); 
            }
            catch (SocketException socketException) when (socketException.SocketErrorCode==SocketError.WouldBlock)
            {
                IList lst = new List<Socket> {socket};
                IList lst2 = new List<Socket> {socket};
                IList lst3 = new List<Socket> {socket};
                Socket.Select(lst,lst2,lst3,10000000);
                Console.WriteLine($"Result: {socket.Connected} {lst.Count} {lst2.Count} {lst3.Count}");
            }
        }

On Linux it says

Result: True 1 1 1

But on windows (looks like correct)

Result: False 0 0 1

Configuration

  • netcoreapp3.1
  • Ubuntu 18 and Windows 10

Other information

If I try to connect with Blocking = true, then it gives SocketException both on Windows and Linux

Looks like related to https://github.com/dotnet/runtime/issues/920

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19 (13 by maintainers)

Most upvoted comments

I will try to chat with @geoffkizer on his opinion regarding being platform compatible VS consistent across multiple platforms (& breaking). May reopen this if we conclude that we can do a better job here.

we want to know that we can connect or we can not connect to an endpoint, and that must be done within a timeout.

This works cross-platform on all versions of .NET:

var checkWrite = new List<Socket> { socket };
var checkError = new List<Socket> { socket };
Socket.Select(null, checkWrite, checkError, timeout);
if (checkError.Count > 0)
{
    Console.WriteLine("Connect failed");
}
else if (checkWrite.Count > 0)
{
    Console.WriteLine("Connect successful");
}
else
{
    Console.WriteLine("Timeout");
}

When Connect fails, I see no reason to check any values there.

The OP has a case using nonblocking socket, connect “fails” with SocketError.WouldBlock.

The 1 1 1 is what we get back from Linux, so this is a platform difference. I don’t think we can/should change this.

The True is the result of:

https://github.com/dotnet/runtime/blob/5b0c6dd5756eea63a57b1f69b182f7386da0f631/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs#L435-L440

This is missing reading out the actual result from the async connect using getsockopt with SO_ERROR.

The next operation that gets performed on the socket will throw (because the socket is actually not connected) and update the state to not connected. So this issue gets masked by that.