Search code examples
c#asp.netasynchronous.net-corehttpclient

.Net Core 3.1 HttpClient does not reuse sockets


I have the below piece of code which I ran on .Net 4.7.2 and .Net Core but I have got different behavior for each framework

public class Program
{


    private HttpClient Client = new HttpClient();
    public static async Task Main(string[] args)
    {
        Program example = new Program();

        Console.WriteLine("Starting connections");
        int numberofIterations = 10;
        Task<HttpResponseMessage>[] awaitableTasks = new Task<HttpResponseMessage>[numberofIterations];
        for (int i = 0; i < numberofIterations; i++)
        {
            var httpRequestMessage = new HttpRequestMessage();
            httpRequestMessage.RequestUri = new Uri("https://example.com");
            httpRequestMessage.Method = new HttpMethod("GET");

            awaitableTasks[i] = example.Client.SendAsync(httpRequestMessage);
            //Console.WriteLine(result.StatusCode);
        }

        Console.WriteLine("Connections done");
        await Task.WhenAll(awaitableTasks);

    }
}

With the .Net Core framework, the network traces shows a separate tcp connection for each request while with the.NEt 4.7.2 framework the sockets get reused.

Network Trace .Net Core

enter image description here

Network Trace .Net 4.7.2

enter image description here

Appreciate your thoughts to understand the differences, to explain this behavior and the best way to overcome this issue.


Solution

  • Short Answer, the below modification to the code will force my .net core app to not create more than two sockets and reuse them.

         var socketsHandler = new SocketsHttpHandler
                {
                    PooledConnectionLifetime = TimeSpan.FromSeconds(60),
                    PooledConnectionIdleTimeout = TimeSpan.FromMinutes(20),
                    MaxConnectionsPerServer = 2
                };
    
         HttpClientHandler handler = new HttpClientHandler() { MaxConnectionsPerServer = 2 };
    
         var Client = new HttpClient(handler);
    

    Updated Answer


    More Details:

    The .Net Framework 4.x.x HttpClient implementation is built on top of HttpWebRequest and ServicePoint which can be managed by the ServicePointManager. The ServicePointManager has a default connection limit set to 10 for ASP.NET hosted applications and 2 for all others, and that is why in my above example (the one in the question) the 2 sockets were opened and reused as the application were prevented to create more than 2 connections(sockets) per service point.

    See the references below for a bit more understanding

    https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.defaultconnectionlimit?view=net-6.0#system-net-servicepointmanager-defaultconnectionlimit

    https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager?view=net-6.0

    https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepoint?view=net-6.0

    C# Does Each Instance of HttpClient Get it's Own ServicePoint

    In .Net Core, the implementation has been changed multiple times, no longer managed by the ServicePointManager and has no default connection limit. The below article has the full story of the HttpClient Class.

    https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core

    Thanks to @user700390 and @PanagiotisKanavos and @JeremyLakeman for their help and guidance toward getting the answer to this question.