You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# HttpClient GetAsync仅首次调用生效 后续调用挂死问题

问题根因
  • 最核心的触发点:HttpClient的GetAsync方法传入空字符串作为请求路径时,不会重新发起指向BaseAddress的新请求,而是会尝试读取当前关联连接上的下一个未被消费的响应帧。第一次调用时连接刚建立,响应帧正常返回;后续调用时连接上没有待读取的新响应,方法会无限等待数据传入直接挂死。
  • 写法违背HttpClient设计规范:每次请求都新建HttpClient和HttpClientHandler实例,会导致底层TCP连接池状态混乱,连接无法被正常释放和复用,进一步放大挂死问题。你之前尝试的单例复用方案没生效,大概率是没有正确释放响应对象,导致连接池占满,新请求排队等待可用连接最终挂死。
  • 请求头格式非法:你代码里的user-agent请求头被手动拆成了两行,实际发送的头里包含了非法的换行空白字符,会触发目标站点WAF的恶意请求检测规则,直接丢弃后续请求的响应包,导致客户端一直等不到返回数据。
正确可运行实现

核心调整点:全局单例复用HttpClient、明确传入请求路径不使用空字符串、修正非法请求头、及时释放响应资源、增加超时和异常校验。

using Newtonsoft.Json.Linq;
using System.Net;

// 全局单例初始化HttpClient,整个应用生命周期内只初始化一次
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler
{
    UseCookies = false,
    // 自动支持gzip/deflate解压,适配大部分站点的响应压缩配置
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
})
{
    BaseAddress = new Uri("https://www.mywebsite.com/api/"),
    Timeout = TimeSpan.FromSeconds(10) // 配置合理超时,避免无限挂起
};

static async Task Main(string[] args)
{
    // 默认请求头只初始化一次,不需要每次请求清空重设
    _httpClient.DefaultRequestHeaders.Clear();
    _httpClient.DefaultRequestHeaders.Add("Cookie", "session=zgbEqIjfSC7M7QdTTpHDkpWLt");
    // user-agent保持单行,不要插入换行
    _httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36");
    _httpClient.DefaultRequestHeaders.Add("x-requested-with", "XMLHttpRequest");
    _httpClient.DefaultRequestHeaders.Add("referer", "https://www.mywebsite.com/");

    // 测试连续调用
    await GetUserInfo("srmilton");
    await GetUserInfo("srmilton");

    while (true)
    {
        var res = await GetUserInfo("srmilton");
        Console.WriteLine($"获取用户信息成功,id: {res.id}, secId: {res.sec_id}");
        await Task.Delay(1000); // 增加合理请求间隔,避免被站点限流拦截
    }
}

public static async Task<(string id, string sec_id)> GetUserInfo(string username)
{
    // 明确传入完整接口路径,禁止传空字符串
    using var response = await _httpClient.GetAsync("user/detail");
    // 主动校验HTTP状态码,请求失败直接抛出明确异常,不会一直卡着
    response.EnsureSuccessStatusCode();
    
    string responseString = await response.Content.ReadAsStringAsync();
    JObject jsonresponse = JObject.Parse(responseString);
    string id = jsonresponse["userInfo"]["user"]["id"].ToString();
    string sec_id = jsonresponse["userInfo"]["user"]["secUid"].ToString();
    return (id, sec_id);
}
额外避坑说明
  • 禁止在每次请求时用using新建HttpClient实例,该类设计为全局单例复用,频繁创建销毁会导致套接字耗尽、连接状态异常等问题。
  • 所有自定义请求头不要插入无意义的换行、多余空白字符,符合HTTP协议规范,避免被WAF误拦截。
  • 所有HttpResponseMessage对象要用using包裹,确保响应资源被及时释放,连接能正常归还连接池供后续请求复用。
  • 不要用.Result、.Wait()等方式阻塞异步调用,在控制台场景下直接用await即可,不会出现上下文同步导致的死锁。

内容的提问来源于stack exchange,提问作者SrMilton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:48:15