Skip to content

Enable SO_REUSE_UNICASTPORT when connecting sockets without an explicit bind #48219

@antonfirsov

Description

@antonfirsov

The problem

When SocketsHttpHandler or other middleware building on top of System.Net.Sockets establishes high (>= 16k) number of TCP connections, new connection attempts will fail with socket error 10055 (WSAENOBUFS) because of port exhaustion.

On Windows 10+, this can be avoided by enabling SO_REUSE_UNICASTPORT (SocketOptionName.ReuseUnicastPort). When enabled, socket.Bind() will not allocate a local port, instead, actual port allocation will be deferred until the ConnectEx call, allowing local port reuse and a much higher scale of outbound connections.

Currently the only way to enable SO_REUSE_UNICASTPORT for HttpClient is to override ConnectCallback as following:
https://gist.github.com/antonfirsov/e86ddc9ac287b7dd63cd85f78ca125f6

We had 1 customer hitting this in production, causing port exhaustion.

Proposal

If no explicit bind has been done before connecting a socket, we are doing a wildcard bind:

partial void WildcardBindForConnectIfNecessary(AddressFamily addressFamily)
{
if (_rightEndPoint != null)
{
return;
}
// The socket must be bound before using ConnectEx.
CachedSerializedEndPoint csep;
switch (addressFamily)
{
case AddressFamily.InterNetwork:
csep = IsDualMode ?
s_cachedMappedAnyV6EndPoint ??= new CachedSerializedEndPoint(s_IPAddressAnyMapToIPv6) :
s_cachedAnyEndPoint ??= new CachedSerializedEndPoint(IPAddress.Any);
break;
case AddressFamily.InterNetworkV6:
csep = s_cachedAnyV6EndPoint ??= new CachedSerializedEndPoint(IPAddress.IPv6Any);
break;
default:
return;
}
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, csep.IPEndPoint);
DoBind(csep.IPEndPoint, csep.SocketAddress);
}

We should enable SocketOptionName.ReuseUnicastPort before binding (by default). This is a non-breaking change, since it is scoped to the internal auto-binding logic.

Metadata

Metadata

Assignees

Labels

area-System.Net.SocketsenhancementProduct code improvement that does NOT require public API changes/additions

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions