🚀 HttpClient vs HttpClientFactory in .NET — Explained Simply and Deeply

🚀 HttpClient vs HttpClientFactory in .NET — Explained Simply and Deeply

If you’ve ever made an API call in .NET — whether to a third-party API (like a payment gateway, SMS service, or weather API) or an internal microservice — you’ve probably used HttpClient.

It seems simple, right? 👇

Article content

But behind this single line, there’s a whole world happening —

🌐 DNS lookups, 🔌 TCP connections, 🔢 ports, and 🧠 connection reuse.

Understanding this can help you write faster, more reliable, and production-grade .NET applications.


⚙️ What Really Happens During an HTTP Request

Before diving into HttpClientFactory, let’s first understand what’s going on behind the scenes 👇

🌍 1. DNS Lookup

When you hit a URL like https://api.example.com, your computer doesn’t know what “example.com” means. So it asks the DNS system — the “phonebook of the internet” — to translate it into an IP address, e.g. 203.0.113.45.

🔌 2. TCP Connection

Once the IP is known, your system opens a TCP connection — a reliable communication channel between your app and the server. Think of TCP as a handshake ensuring your data arrives safely and in order.

🔢 3. Ports

Each TCP connection uses two ports:

  • Server port (e.g., 443 for HTTPS) — the entry gate on the server.
  • Client (ephemeral) port — a temporary, random port chosen by your OS (usually between 49152–65535).

💡 The Operating System maintains an internal table to keep track of all active ports — which ones are open, in use, or waiting to close. This table defines how many concurrent connections your machine can handle. For example, Windows and Linux each have their own maximum limit on available ephemeral ports (usually in the tens of thousands).

These ephemeral ports are short-lived and get reused after they close — but if you open too many too quickly, you can run out temporarily, leading to socket exhaustion.

⏱️ 4. TIME_WAIT

When a TCP connection closes, your OS keeps that port reserved in a state called TIME_WAIT. This ensures old data doesn’t interfere with new connections. The duration varies by OS — sometimes 30 seconds, sometimes a few minutes. (So it’s not always 2–4 minutes as you may see online.)


❌ The Classic HttpClient Mistake

Many developers start like this:

Article content

Seems fine at first… until traffic increases ⚠️

Every call above creates a new TCP connection, and each one lingers in TIME_WAIT after closing. Under heavy load, your app can quickly run out of available ephemeral ports — a problem known as Socket Exhaustion.

You might even see errors like:

“Only one usage of each socket address (protocol/network address/port) is normally permitted.”

The fix people usually try next is using a singleton HttpClient (shared instance), which helps by reusing existing TCP connections.

But… that introduces another hidden problem 👇


⚠️ The DNS Staleness Problem

When you use a single HttpClient for your entire app, the TCP connections stay open for a long time. Each TCP connection remembers the IP it was connected to when it was created.

So, if the DNS record for api.example.com changes (e.g., the API provider moves servers or does a failover), your existing connections still point to the old IP address 💀 — even though the domain itself now resolves to a new one.

This issue happens because the long-lived TCP connections inside your singleton HttpClient don’t automatically re-check DNS — leading to DNS staleness.


💡 Enter HttpClientFactory — Smarter, Safer Networking

Introduced in .NET Core 2.1, IHttpClientFactory solves both issues — ✅ Socket ExhaustionDNS Staleness

Here’s how it works under the hood 👇

  • It manages SocketsHttpHandler instances.
  • Each handler maintains its own TCP connection pool.
  • When the handler’s lifetime expires (default: 2 minutes):

  1. The old handler and its pool are disposed.
  2. A new handler is created.
  3. The next request triggers a fresh DNS resolution.

So, your app keeps reusing connections efficiently but also gets periodic DNS refreshes — all automatically 🎯

Example:

Article content



🧩 What Happens When the Handler is Disposed?

When a handler’s lifetime ends:

  • Active requests finish normally.
  • Idle TCP connections are closed gracefully.
  • The OS cleans up those connections and reclaims their ephemeral ports once TIME_WAIT expires.

This ensures your app keeps running smoothly — no port exhaustion, no broken connections, no stale DNS entries.

Article content



🚀 Final Thoughts

HttpClientFactory isn’t just a nice-to-have — it’s an essential feature for building stable and scalable .NET applications.

It handles:

  • 🌀 Connection pooling
  • 🧠 Automatic DNS refresh
  • ⏳ Lifecycle management

So you can focus on your business logic, not on debugging weird network issues at 3 a.m. 😅

If you’re still using plain HttpClient, it’s time to upgrade your code to IHttpClientFactory. Your future self (and your production server) will thank you 🙌


💬 What’s your experience with HttpClientFactory? Have you ever faced socket exhaustion or DNS issues in production? Let’s discuss 👇

#dotnet #dotnetcore #csharp #aspnetcore #webapi #httpclien t#httpclientfactory #softwaredevelopment #cleanarchitecture #microservices #restapi #scalability #performance #networking #programming #coding #developers #backenddevelopment #softwareengineering #techcommunity


Great insights. Really liked how you explained the concept clearly and connected it with real-world use cases.

To view or add a comment, sign in

More articles by Hamza Javaid

Others also viewed

Explore content categories