diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs index c201d247f9edf3..816a6cab8c70f8 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs @@ -239,7 +239,10 @@ private void InternalConnectCallback(object? sender, SocketAsyncEventArgs args) SocketAsyncEventArgs args = _internalArgs!; args.RemoteEndPoint = new IPEndPoint(attemptAddress, _endPoint!.Port); - if (!attemptSocket.ConnectAsync(args)) + bool pending = attemptSocket.ConnectAsync(args); + // Copy the socket address so we can use it in exception message. + _userArgs!._socketAddress = _internalArgs!._socketAddress; + if (!pending) { InternalConnectCallback(null, args); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index cde90ae0931cc5..9fc5fbf961f4b8 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Net.Internals; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; @@ -1140,7 +1141,10 @@ private void ThrowException(SocketError error, CancellationToken cancellationTok private Exception CreateException(SocketError error, bool forAsyncThrow = true) { - Exception e = new SocketException((int)error); + // When available, include the IP endpoint information in connection exceptions: + Exception e = LastOperation == SocketAsyncOperation.Connect && _socketAddress != null ? + SocketExceptionFactory.CreateSocketException((int)error, _socketAddress.GetIPEndPoint()) : + new SocketException((int)error); if (forAsyncThrow) { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index d4bdcfed80a0f6..e8b8cd665b0348 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -82,6 +82,20 @@ public async Task Connect_MultipleIPAddresses_Success(IPAddress listenAt) } } + [OuterLoop("Slow on Windows")] + [Theory] + [InlineData(false)] + [InlineData(true)] + public virtual async Task Connect_WhenFails_ThrowsSocketExceptionWithIPEndpointInfo(bool useDns) + { + // Port 288 is unassigned: + // https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + EndPoint badEndpoint = useDns ? (EndPoint)new DnsEndPoint("localhost", 288) : new IPEndPoint(IPAddress.Loopback, 288); + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + SocketException ex = await Assert.ThrowsAnyAsync(() => ConnectAsync(client, badEndpoint)); + Assert.Contains("127.0.0.1:288", ex.Message); + } + [Fact] public async Task Connect_OnConnectedSocket_Fails() { @@ -115,7 +129,7 @@ public async Task Connect_AfterDisconnect_Fails() } else { - SocketException se = await Assert.ThrowsAsync(() => ConnectAsync(client, new IPEndPoint(IPAddress.Loopback, port))); + SocketException se = await Assert.ThrowsAnyAsync(() => ConnectAsync(client, new IPEndPoint(IPAddress.Loopback, port))); Assert.Equal(SocketError.IsConnected, se.SocketErrorCode); } } @@ -204,5 +218,8 @@ public ConnectTask(ITestOutputHelper output) : base(output) {} public sealed class ConnectEap : Connect { public ConnectEap(ITestOutputHelper output) : base(output) {} + + // We should skip this since the exception creation logic is defined in SocketHelperEap. + public override Task Connect_WhenFails_ThrowsSocketExceptionWithIPEndpointInfo(bool useDns) => Task.CompletedTask; } }