Open okarpov opened 2 years ago
Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.
Author: | okarpov |
---|---|
Assignees: | - |
Labels: | `area-System.Net` |
Milestone: | - |
it seems like that API is Windows specific. cc: @tmds @antonfirsov
You can probably use SocketOptionName.ReuseAddress
on Linux.
It will set both...
strace -e network -f /tmp/socket/bin/Debug/net6.0/linux-x64/publish/socket
....
[pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0
[pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
[pid 46509] setsockopt(40, SOL_TCP, TCP_NODELAY, [1], 4) = 0
[pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
[pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
it seems like that API is Windows specific. cc: @tmds @antonfirsov You can probably use
SocketOptionName.ReuseAddress
on Linux. It will set both...strace -e network -f /tmp/socket/bin/Debug/net6.0/linux-x64/publish/socket .... [pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0 [pid 46509] setsockopt(40, SOL_TCP, TCP_NODELAY, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
Thx, I will check it
it seems like that API is Windows specific. cc: @tmds @antonfirsov You can probably use
SocketOptionName.ReuseAddress
on Linux. It will set both...strace -e network -f /tmp/socket/bin/Debug/net6.0/linux-x64/publish/socket .... [pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0 [pid 46509] setsockopt(40, SOL_TCP, TCP_NODELAY, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 [pid 46509] setsockopt(40, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
does not work :(
System.AggregateException: One or more errors occurred. (Sockets on this platform are invalid for use after a failed connection attempt. )
---> System.Net.Http.HttpRequestException: Sockets on this platform are invalid for use after a failed connection attempt.
---> 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)
// Default socket creation logic
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;
// Enable SO_REUSE_UNICASTPORT:
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 1);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
return new NetworkStream(socket, ownsSocket: true);
}
catch (Exception ex)
{
if (Log.IsEnabled)
{
Log._Log(this, ex.Message, ex);
}
socket.Dispose();
throw;
}
This is not what I see @okarpov. Can you pose complete stand alone repro? (instead of just fragment)
Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.
Author: | okarpov |
---|---|
Assignees: | - |
Labels: | `area-System.Net.Sockets`, `untriaged` |
Milestone: | - |
This issue has been marked needs-author-action
and may be missing some important information.
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-29-generic x86_64)
Documentation: https://help.ubuntu.com
Management: https://landscape.canonical.com
Support: https://ubuntu.com/advantage
System information as of Mon 15 Aug 2022 08:11:09 PM CEST
System load: 0.17 Processes: 118 Usage of /: 4.7% of 137.27GB Users logged in: 1 Memory usage: 3% IPv4 address for eth0: xx.xx.xx.xx Swap usage: 0%
Super-optimized for small spaces - read how we shrank the memory footprint of MicroK8s to make it the smallest full K8s around.
65 updates can be applied immediately. 25 of these updates are standard security updates. To see these additional updates run: apt list --upgradable
New release '22.04.1 LTS' available. Run 'do-release-upgrade' to upgrade to it.
This is not what I see @okarpov. Can you pose complete stand alone repro? (instead of just fragment)
will create repro
static void Main(string[] args)
{
//repro
var coo = new CookieContainer();
var socketsHandler = new System.Net.Http.HttpClientHandler()
{
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.Brotli,
AllowAutoRedirect = false,
SslProtocols = System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13,
ServerCertificateCustomValidationCallback = (s, e, a, b) =>
{
return true;
},
CookieContainer = coo,
UseCookies = true,
MaxAutomaticRedirections = 50,
MaxConnectionsPerServer = int.MaxValue,
PreAuthenticate = false,
};
var field = typeof(System.Net.Http.HttpClientHandler).GetField("_underlyingHandler", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
var sockHandler = (System.Net.Http.SocketsHttpHandler)field?.GetValue(socketsHandler);
sockHandler.ConnectCallback = async (context, cancellationToken) =>
{
// Default socket creation logic
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;
// Enable SO_REUSE_UNICASTPORT:
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 1);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
return new NetworkStream(socket, ownsSocket: true);
}
catch (Exception ex)
{
Console.WriteLine(ex);
socket.Dispose();
throw;
}
};
sockHandler.EnableMultipleHttp2Connections = true;
sockHandler.UseCookies = true;
sockHandler.CookieContainer = coo;
System.Net.Http.HttpClient wr = new System.Net.Http.HttpClient(socketsHandler, true);
wr.DefaultRequestHeaders.Clear();
wr.DefaultRequestHeaders.Accept.TryParseAdd("*/*");
wr.DefaultRequestHeaders.AcceptEncoding.TryParseAdd("gzip, deflate, br");
wr.DefaultRequestHeaders.AcceptLanguage.TryParseAdd("en-US,en;q=0.9");
wr.DefaultRequestHeaders.ConnectionClose = false;
wr.DefaultRequestHeaders.Connection.Add("keep-alive");
wr.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue() { NoCache = true };
wr.DefaultRequestHeaders.Pragma.TryParseAdd("no-cache");
wr.DefaultRequestHeaders.Add("Upgrade-Insecure-Requests", "1");
wr.DefaultVersionPolicy = System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher;
wr.Timeout = TimeSpan.FromSeconds(10);
System.Net.Http.HttpRequestMessage req = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, "https://www.google.com")
{
Version = System.Net.HttpVersion.Version30,
VersionPolicy = System.Net.Http.HttpVersionPolicy.RequestVersionOrLower
};
req.Headers.ConnectionClose = false;
req.Headers.UserAgent.TryParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36");
req.Content = new System.Net.Http.StringContent("", null, null);
req.Content.Headers.ContentType = null;
try
{
var r = wr.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseContentRead).Result;
string response = r.Content.ReadAsStringAsync().Result;
Console.WriteLine(response);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
return;
}
Linux:
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 ConsoleApp1.Program.<>c.<<Main>b__0_0>d.MoveNext() in \ConsoleApp1\Program.cs:line 53
System.AggregateException: One or more errors occurred. (Sockets on this platform are invalid for use after a failed connection attempt. (www.google.com:443))
---> System.Net.Http.HttpRequestException: Sockets on this platform are invalid for use after a failed connection attempt. (www.google.com:443)
---> 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 ConsoleApp1.Program.<>c.<<Main>b__0_0>d.MoveNext() in \ConsoleApp1\Program.cs:line 53
--- End of stack trace from previous location ---
at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)
at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttp2ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at ConsoleApp1.Program.Main(String[] args) in \ConsoleApp1\Program.cs:line 98
Windows:
It seems like the socket is failing to connect - and not failing on socket options as originally reported. I think you should try it without any HTTP - just getting to socket nailed down first. And the code above is still just a fragment referencing wr
. (with that I cannot use it ASIS without further modifications)
its complete console main method - why you can not use it?
it works on Windows 11 with no errors and returns google page html
changing the socket ConnectCallback to this - works on linux and windows with no errors
sockHandler.ConnectCallback = async (context, cancellationToken) =>
{
var entry = await Dns.GetHostEntryAsync(context.DnsEndPoint.Host, AddressFamily.InterNetwork, cancellationToken);
var s = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
s.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(this.ipaddress), 0));
s.NoDelay = true;
await s.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
return new System.Net.Sockets.NetworkStream(s, ownsSocket: true);
}
commenting out that two lines returns google html page on linux as well
// Enable SO_REUSE_UNICASTPORT:
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 1);
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
dotnet --info
Host (useful for support): Version: 6.0.6 Commit: 7cca709db2
.NET SDKs installed: No SDKs were found.
.NET runtimes installed: Microsoft.AspNetCore.App 6.0.6 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.6 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs: https://aka.ms/dotnet-download
have just found this: https://github.com/dotnet/runtime/issues/24917
System.PlatformNotSupportedException: Sockets on this platform are invalid for use after a failed connection attempt.
at System.Net.Sockets.Socket.ThrowMultiConnectNotSupported()
is because you're calling Socket.ConnectAsync
with a DnsEndPoint
.
You need to resolve the hostname, and try to connect to each IPEndPoint
creating a new Socket
after each failed attempt (cfr https://github.com/dotnet/runtime/issues/73920#issuecomment-1215743144).
i took this example from here: https://github.com/dotnet/runtime/issues/48219 where the similar issue (Reuse Port) seems was resolved
also it works on windows, so this is not obvious why it does not work in linux
main issue is that i'm trying to achieve is making our application to Reuse Port to avoid port exhausting on sending a lot of outbound connections.
so, could you, please, provide any affordable solution?
Triage: not critical for 7.0, moving to Future
setting this
sysctl net.ipv4.tcp_tw_reuse=1
seems resolve the issue partially and now i can set
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
but this still throws exception
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 1);
Description
Enabling SO_REUSEPORT throws exception on Ubuntu Server 20.04
Reproduction Steps
Expected behavior
should reuse ports as per SO_REUSEPORT feature
Actual behavior
Regression?
No response
Known Workarounds
No response
Configuration
Ubuntu server 20.04 .Net Core 6
Other information
No response