必须复用httpclient,禁止单次请求新建实例,否则因连接池、dns缓存、ssl会话无法复用及time_wait端口堆积,导致socketexception 10048或addressalreadyinuse;.net 6+应使用ihttpclientfactory,非di场景用static readonly httpclient。

别每次请求都 new HttpClient(),否则跑几天就爆 SocketException 10048 或 AddressAlreadyInUse——这是最常被忽略、但后果最直接的错误。
HttpClient 必须复用,不能 new 一次用一次
每次 new HttpClient() 都会新建一个独立的连接池和 ServicePoint,而 Windows 默认每个 ServicePoint 最多维持 2 个空闲连接。高并发下短命实例快速堆积,导致:
- TCP 端口耗尽(
SocketException 10048) - DNS 解析不共享,重复查表拖慢首次请求
- HTTPS 握手无法复用,TLS 建连延迟翻倍
-
Dispose()后连接进TIME_WAIT,持续占端口约 4 分钟
正确做法只有两种:
- ASP.NET Core / Minimal API 项目:注册
IHttpClientFactory,走 DI 容器管理 - 控制台、WinForms、非 DI 场景:声明
private static readonly HttpClient,设好BaseAddress和Timeout
GET 请求别只用 GetStringAsync
GetStringAsync 看似方便,但它自动吞掉状态码和响应头,404、500 都算“成功”,调试时完全看不出问题。
生产代码应统一用 GetAsync + EnsureSuccessStatusCode():
var response = await _client.GetAsync("/users/123");
response.EnsureSuccessStatusCode(); // 显式抛出 4xx/5xx
var json = await response.Content.ReadAsStringAsync();
如果需要读取响应头(比如分页信息 X-Total-Count),必须用 GetAsync,GetStringAsync 根本拿不到。
POST 提交 JSON 或表单,Content 类型不能错
POST 不是拼字符串的事,Content-Type 错了后端根本收不到数据:
- 提交 JSON:
StringContent+"application/json",别漏 UTF8 编码 - 提交表单:
FormUrlEncodedContent,不是手动拼"key=value&key2=value2" - 别在
PostAsync后直接ReadAsStringAsync()——先调EnsureSuccessStatusCode()检查是否真成功
示例(JSON):
var json = JsonSerializer.Serialize(new { name = "Alice", age = 30 });
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _client.PostAsync("/api/users", content);
response.EnsureSuccessStatusCode();
IHttpClientFactory 和静态 HttpClient 怎么选
选哪个不看“高级不高级”,看项目结构是否支持 DI:
- 有
Program.cs调用AddHttpClient()的(.NET 6+ Web API / Minimal Host):必须用IHttpClientFactory,它能自动轮换 DNS、隔离超时、支持命名客户端 - 没 DI 容器的场景(如脚本、工具类库、老 WinForms):用
static readonly HttpClient最简单可靠,但得自己管BaseAddress、Timeout、UserAgent - 绝对不要混用:既注册了
IHttpClientFactory,又在某处手写new HttpClient()——连接池策略冲突,反而更难排查
真正容易被忽略的是:即使用了 IHttpClientFactory,也得记得在 CreateClient() 后设置 Timeout;默认值是 100 秒,很多 API 根本等不了那么久。











