The Problem¶
If you’ve ever worked with HttpClient in C#, you might have encountered the infamous SocketException — usually when your application is under heavy load. The root cause? Port exhaustion from creating too many HttpClient instances.
// DON'T do this — creates a new HttpClient per request
using (var client = new HttpClient())
{
var response = await client.GetAsync("https://api.example.com/data");
}
Every time you new up an HttpClient and dispose it, the underlying TCP connection enters a TIME_WAIT state. Under high concurrency, you run out of available ports.
The Solution¶
Use a single shared instance of HttpClient, or better yet, use IHttpClientFactory (available since .NET Core 2.1):
// Register in Startup.cs / Program.cs
services.AddHttpClient("api", client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
Then inject and use it:
public class MyService
{
private readonly HttpClient _client;
public MyService(IHttpClientFactory factory)
{
_client = factory.CreateClient("api");
}
public async Task<string> GetDataAsync()
{
var response = await _client.GetAsync("/data");
return await response.Content.ReadAsStringAsync();
}
}
Why IHttpClientFactory?¶
- Manages handler lifetimes — rotates
HttpMessageHandlerinstances to avoid DNS issues - Prevents port exhaustion — reuses connections from a pool
- Supports Polly — add retry policies, circuit breakers, etc.
Key Takeaway¶
Never
newupHttpClientin ausingblock for repeated calls. UseIHttpClientFactoryor a shared static instance.
This is one of those .NET gotchas that has bitten many production systems. The fix is simple once you know the pattern.