diff --git a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Unix.cs b/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Unix.cs
deleted file mode 100644
index 88e5af18ba215c..00000000000000
--- a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Unix.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Net.Sockets;
-
-namespace System.Net.Internals
-{
- internal static partial class SocketExceptionFactory
- {
- public static SocketException CreateSocketException(SocketError errorCode, int platformError)
- {
- return new ExtendedSocketException(errorCode, platformError);
- }
- }
-}
diff --git a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Windows.cs b/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Windows.cs
deleted file mode 100644
index 705157bcdd8b4b..00000000000000
--- a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.Windows.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Net.Sockets;
-
-namespace System.Net.Internals
-{
- internal static partial class SocketExceptionFactory
- {
- public static SocketException CreateSocketException(SocketError errorCode, int platformError)
- {
- return new SocketException((int)errorCode);
- }
- }
-}
diff --git a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.cs b/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.cs
deleted file mode 100644
index b3e160c13d47ad..00000000000000
--- a/src/libraries/Common/src/System/Net/Internals/SocketExceptionFactory.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Net.Sockets;
-
-namespace System.Net.Internals
-{
- internal static partial class SocketExceptionFactory
- {
- private sealed class ExtendedSocketException : SocketException
- {
- private readonly EndPoint? _endPoint;
-
- public ExtendedSocketException(int errorCode, EndPoint? endPoint)
- : base(errorCode)
- {
- _endPoint = endPoint;
- }
-
- public ExtendedSocketException(SocketError socketError, int platformError)
- : base((int)socketError)
- {
- HResult = platformError;
- }
-
- public override string Message =>
- (_endPoint == null) ? base.Message : base.Message + " " + _endPoint.ToString();
- }
-
- public static SocketException CreateSocketException(int socketError, EndPoint? endPoint)
- {
- return new ExtendedSocketException(socketError, endPoint);
- }
- }
-}
diff --git a/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Unix.cs b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Unix.cs
new file mode 100644
index 00000000000000..ae503310856720
--- /dev/null
+++ b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Unix.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net.Sockets
+{
+ internal static partial class SocketExceptionFactory
+ {
+ public static SocketException CreateSocketException(int socketError, EndPoint endPoint)
+ {
+ int nativeErr = (int)socketError;
+
+ // If an interop error was not found, then don't invoke Info().RawErrno as that will fail with assert.
+ if (SocketErrorPal.TryGetNativeErrorForSocketError((SocketError)socketError, out Interop.Error interopErr))
+ {
+ nativeErr = interopErr.Info().RawErrno;
+ }
+
+ return new SocketException(socketError, CreateMessage(nativeErr, endPoint));
+ }
+ }
+}
diff --git a/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Windows.cs b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Windows.cs
new file mode 100644
index 00000000000000..ceb51ddf46fc34
--- /dev/null
+++ b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.Windows.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net.Sockets
+{
+ internal static partial class SocketExceptionFactory
+ {
+ public static SocketException CreateSocketException(int socketError, EndPoint endPoint)
+ {
+ // Windows directly maps socketError to native error code.
+ return new SocketException(socketError, CreateMessage(socketError, endPoint));
+ }
+ }
+}
diff --git a/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.cs b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.cs
new file mode 100644
index 00000000000000..1c408e8c45b20b
--- /dev/null
+++ b/src/libraries/Common/src/System/Net/Sockets/SocketExceptionFactory.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+namespace System.Net.Sockets
+{
+ internal static partial class SocketExceptionFactory
+ {
+ private static string CreateMessage(int nativeSocketError, EndPoint endPoint)
+ {
+ return Marshal.GetPInvokeErrorMessage(nativeSocketError) + " " + endPoint.ToString();
+ }
+ }
+}
diff --git a/src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs b/src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs
index 00250e07d8f328..6f2dca85a1b6d0 100644
--- a/src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs
+++ b/src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs
@@ -471,6 +471,7 @@ public partial class SocketException : System.ComponentModel.Win32Exception
{
public SocketException() { }
public SocketException(int errorCode) { }
+ public SocketException(int errorCode, string? message) { }
protected SocketException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { }
public override int ErrorCode { get { throw null; } }
public override string Message { get { throw null; } }
diff --git a/src/libraries/System.Net.Primitives/src/System/Net/SocketException.cs b/src/libraries/System.Net.Primitives/src/System/Net/SocketException.cs
index 3e5540419d03b1..46f4d4305124b3 100644
--- a/src/libraries/System.Net.Primitives/src/System/Net/SocketException.cs
+++ b/src/libraries/System.Net.Primitives/src/System/Net/SocketException.cs
@@ -30,12 +30,23 @@ public SocketException(int errorCode) : this((SocketError)errorCode)
// but that's the least bad option right now.
}
+ /// Initializes a new instance of the class with the specified error code and optional message.
+ public SocketException(int errorCode, string? message) : this((SocketError)errorCode, message)
+ {
+ }
+
/// Creates a new instance of the class with the specified error code as SocketError.
internal SocketException(SocketError socketError) : base(GetNativeErrorForSocketError(socketError))
{
_errorCode = socketError;
}
+ /// Initializes a new instance of the class with the specified error code as SocketError and optional message.
+ internal SocketException(SocketError socketError, string? message) : base(GetNativeErrorForSocketError(socketError), message)
+ {
+ _errorCode = socketError;
+ }
+
public override string Message => base.Message;
public SocketError SocketErrorCode => _errorCode;
diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketExceptionTest.cs b/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketExceptionTest.cs
new file mode 100644
index 00000000000000..920df922cf0d65
--- /dev/null
+++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketExceptionTest.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Sockets;
+using Xunit;
+
+namespace System.Net.Primitives.Functional.Tests
+{
+ public static class SocketExceptionTest
+ {
+ [Fact]
+ public static void Create_AllErrorCodes_Success()
+ {
+ foreach (SocketError error in Enum.GetValues(typeof(SocketError)))
+ {
+ SocketException e = new SocketException((int)error);
+ Assert.Equal(error, e.SocketErrorCode);
+ Assert.Null(e.InnerException);
+ Assert.NotNull(e.Message);
+ }
+ }
+
+ [Fact]
+ public static void Create_ExceptionWithMessage_Success()
+ {
+ const string Message = "Hello World";
+ SocketException e = new SocketException((int)SocketError.AccessDenied, Message);
+ Assert.Equal(SocketError.AccessDenied, e.SocketErrorCode);
+ Assert.Null(e.InnerException);
+ Assert.Equal(Message, e.Message);
+ Assert.Contains(Message, e.ToString());
+ }
+ }
+}
diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj b/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj
index 4e2e897b0a6419..d31b916f781f33 100644
--- a/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj
+++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj
@@ -25,7 +25,7 @@
-
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
index 7f9c5ae276aa97..4b3af09ab75f95 100644
--- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
+++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
@@ -78,8 +78,8 @@
Link="Common\System\Net\Internals\IPEndPointExtensions.cs" />
-
+
+
@@ -278,6 +280,8 @@
Link="Common\Interop\Unix\System.Native\Interop.Pipe.cs" />
+
diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
index f479d5d37ddccd..7f9fb007e74e7b 100644
--- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
+++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
@@ -218,6 +219,28 @@ public ConnectApm(ITestOutputHelper output) : base(output) {}
public sealed class ConnectTask : Connect
{
public ConnectTask(ITestOutputHelper output) : base(output) {}
+
+ [OuterLoop]
+ [Fact]
+ public static void Connect_ThrowSocketException_Success()
+ {
+ using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ int anonymousPort = socket.BindToAnonymousPort(IPAddress.Loopback);
+ IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, anonymousPort);
+ Assert.ThrowsAsync(() => socket.ConnectAsync(ep));
+ try
+ {
+ socket.Connect(ep);
+ Assert.Fail("Socket Connect should throw SocketException in this case.");
+ }
+ catch (SocketException ex)
+ {
+ Assert.Contains(Marshal.GetPInvokeErrorMessage(ex.NativeErrorCode), ex.Message);
+ Assert.Contains(ep.ToString(), ex.Message);
+ }
+ }
+ }
}
public sealed class ConnectEap : Connect