HttpClient、WebClient 或 HttpWebRequest 首次尝试读取文件需要 90 秒,之后它会快速运行

Posted

技术标签:

【中文标题】HttpClient、WebClient 或 HttpWebRequest 首次尝试读取文件需要 90 秒,之后它会快速运行【英文标题】:HttpClient, WebClient or HttpWebRequest takes 90 seconds to read a file on the first attempt, after that it runs quickly 【发布时间】:2020-10-12 20:44:21 【问题描述】:

我有一个在 PC 上运行的应用程序,它从运行在嵌入式设备中的 Web 服务器下载文件,该嵌入式设备使用 RNDIS over USB 连接到 PC。 RNDIS 连接看起来像一个网络适配器,让我可以在 PC 和嵌入式设备之间运行 TCP/IP 连接。

设备通电后,我可以在几秒钟内使用网络浏览器查看设备上的内容。但是当我的应用程序运行时,对 HttpClient 的第一次调用需要 90 秒才能完成。在那之后。 HttpClient 运行正常。如果我关闭我的应用程序并再次打开它,那么 HttpClient 现在在第一次尝试时可以正常工作。应用启动时的 90 秒延迟对用户来说非常明显。

这个问题似乎只出现在某些电脑上,而不会出现在其他电脑上。我尝试过使用 HttpClient、WebClient 和 HttpWebRequest,但结果是一样的。在阅读 *** 上的其他主题后,我也尝试了 ServicePointManager 和 ServicePoint 中的设置,但没有这个问题。

我可以在控制台应用程序中使用这几行代码来演示问题:

static async Task Main(string[] args)

    using (HttpClient httpClient = new HttpClient())
    
        string url = "http://169.254.21.151/eventlog/2020-10-11.txt";
        for (; ; )
        
            DateTime t1 = DateTime.Now;
            try
            
                HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(url);
                DateTime t2 = DateTime.Now;
                string content = await httpResponseMessage.Content.ReadAsStringAsync();
                DateTime t3 = DateTime.Now;
                TimeSpan dt1 = t2 - t1;
                TimeSpan dt2 = t3 - t2;
                Console.WriteLine($"Http Read content.Length bytes after dt1.TotalMilliseconds:F0 ms + dt2.TotalMilliseconds:F0 ms");
            
            catch (HttpRequestException)
            
                DateTime t2 = DateTime.Now;
                TimeSpan dt = t2 - t1;
                Console.WriteLine($"Http Failed to read after dt.TotalMilliseconds:F0 ms");
            
            await Task.Delay(1000);
        
    

我这样测试了上面的代码:

    启动嵌入式设备 使用网络浏览器测试 HTTP 连接并下载文件 运行上面的代码

结果显示第一次调用 httpClient.GetAsync(url) 需要 90 秒才能完成。 90 秒让我觉得是一个可疑的数字,这表明我还没有找到某个地方的超时设置。 90 秒的测量延迟在多个测试中非常一致。

Http Read 1106623 bytes after 90449 ms + 0 ms
Http Read 1106623 bytes after 1080 ms + 0 ms
Http Read 1106623 bytes after 1070 ms + 0 ms
Http Read 1106623 bytes after 1110 ms + 0 ms

是否有我遗漏的设置?

【问题讨论】:

【参考方案1】:

我找到了一个蛮力解决方案。我认为问题出在 ServicePointManager 的某个地方,所以我的解决方案是通过使用 TcpClient 而不是 HttpClient 向该硬件发出 HTTP 请求时避免使用 ServicePointManager,然后自己构建 HTTP 请求标头。我知道这会由于为每个请求创建和销毁套接字而带来性能损失,但我认为在这种情况下这是可以接受的,因为对设备的请求量相当小。我欢迎提出更优雅的解决方案的建议。

我的代码相当于问题中的代码:

static void Main(string[] args)

    string host = "169.254.21.151";
    string file = "/eventlog/2020-10-11.txt";
    for (; ; )
    
        DateTime t1 = DateTime.Now;
        try
        
            string query = $"GET file HTTP/1.1\r\nConnection: close\r\nHost: host\r\n\r\n";
            byte[] queryBytes = Encoding.UTF8.GetBytes(query);
            using (TcpClient tcpClient = new TcpClient(host, 80))
            
                tcpClient.GetStream().Write(queryBytes, 0, queryBytes.Length);
                using (StreamReader streamReader = new StreamReader(tcpClient.GetStream()))
                
                    string response = streamReader.ReadToEnd();
                    int index = response.IndexOf("\r\n\r\n");
                    if (index > 0)
                    
                        string headers = response.Substring(0, index);
                        string content = response.Substring(index + 4);
                        if (headers.Contains("HTTP/1.1 200 OK"))
                        
                            DateTime t2 = DateTime.Now;
                            TimeSpan dt = t2 - t1;
                            Console.WriteLine($"Http Read content.Length bytes after dt.TotalMilliseconds:F0 ms");
                        
                    
                
            
        
        catch (Exception)
        
            DateTime t2 = DateTime.Now;
            TimeSpan dt = t2 - t1;
            Console.WriteLine($"Http Failed to read after dt.TotalMilliseconds:F0 ms");
        
        Thread.Sleep(1000);
    

【讨论】:

以上是关于HttpClient、WebClient 或 HttpWebRequest 首次尝试读取文件需要 90 秒,之后它会快速运行的主要内容,如果未能解决你的问题,请参考以下文章

WebClient、HttpWebRequest 和 HttpClient 的最大并发请求数

如何用 HttpClient 替换 WebClient?

如何在 .NET 6 中用 HttpClient 替换过时的 WebClient

WebClient, HttpClient, HttpWebRequest ,RestSharp之间的区别与抉择

WebClient, HttpClient, HttpWebRequest ,RestSharp之间的区别与抉择

WebClient和HttpClient, 以及webapi上传图片