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

.NET Framework 4.8中HttpClient处理SSE时60秒超时求助

解决.NET Framework 4.8中SSE流读取60秒超时问题

问题根源

在.NET Framework 4.8中,Stream.ReadTimeout仅对同步读取操作生效,异步的ReadAsync不会遵循该设置。同时,HttpClient底层依赖的HttpWebRequest默认有60秒的读写超时,即便你设置了client.Timeout,未覆盖底层的ReadWriteTimeout仍会导致流读取超时。

解决方案

方案一:直接使用HttpWebRequest(推荐,更易控制超时)

直接操作HttpWebRequest,可以明确设置整个请求超时和流读写超时,两者对异步操作均生效:

private async Task<string> WaitForQueuePass(string bearer, string token)
{
    string url = "https://blah.com";
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "GET";
    // 设置整个请求的超时(2小时)
    request.Timeout = (int)TimeSpan.FromHours(2).TotalMilliseconds;
    // 设置流读写的超时(10分钟)
    request.ReadWriteTimeout = (int)TimeSpan.FromMinutes(10).TotalMilliseconds;
    request.Accept = "text/event-stream";
    request.Headers.Add("Authorization", $"Bearer {bearer}");

    using (var response = (HttpWebResponse)await request.GetResponseAsync())
    using (var stream = response.GetResponseStream())
    using (var reader = new StreamReader(stream, Encoding.UTF8))
    {
        string line;
        // 按SSE规范逐行读取(SSE以行作为基本传输单元)
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (line.StartsWith("data:"))
            {
                string data = line.Substring(5).Trim();
                if (data.Contains("passingToken"))
                {
                    // 提取目标token
                    return data.Split(new[] { "\"passingToken\":\"" }, StringSplitOptions.None)[1].Split('"')[0];
                }
            }
            // 忽略SSE的空行或其他字段(如event:),可根据需求扩展处理
        }
    }

    return string.Empty;
}

方案二:配置HttpClient底层的HttpWebRequest超时

若坚持使用HttpClient,可通过自定义HttpClientHandler拦截并设置底层HttpWebRequest的读写超时:

private async Task<string> WaitForQueuePass(string bearer, string token)
{
    string url = "https://blah.com";
    using (var handler = new HttpClientHandler())
    using (var client = new HttpClient(handler))
    {
        client.Timeout = TimeSpan.FromHours(2);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearer}");
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream"));

        // 拦截底层HttpWebRequest,设置读写超时
        handler.HttpWebRequestCallback = request =>
        {
            if (request is HttpWebRequest webRequest)
            {
                webRequest.ReadWriteTimeout = (int)TimeSpan.FromMinutes(10).TotalMilliseconds;
            }
        };

        using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
        {
            response.EnsureSuccessStatusCode();
            
            using (var stream = await response.Content.ReadAsStreamAsync())
            using (var reader = new StreamReader(stream, Encoding.UTF8))
            {
                string line;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    if (line.StartsWith("data:"))
                    {
                        string data = line.Substring(5).Trim();
                        if (data.Contains("passingToken"))
                        {
                            return data.Split(new[] { "\"passingToken\":\"" }, StringSplitOptions.None)[1].Split('"')[0];
                        }
                    }
                }
            }
        }
    }

    return string.Empty;
}

额外注意事项

  • 按SSE规范逐行读取比直接读缓冲区更可靠,可避免消息被缓冲区截断的问题;
  • 需确保服务器端未设置过短的连接超时,否则服务器主动断开连接会导致客户端读取中断;
  • 若服务器发送心跳消息(如data: \n\n),可在代码中处理心跳以维持连接,进一步避免超时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:05:31