HttpClient、WebClient 或 HttpWebRequest 第一次尝试读取文件需要 90 秒,之后它运行很快
HttpClient, WebClient or HttpWebRequest takes 90 seconds to read a file on the first attempt, after that it runs quickly
我有一个应用程序,它 运行 在 PC 上运行,并从 Web 服务器下载文件 运行 在嵌入式设备中,该设备通过 USB 使用 RNDIS 连接到 PC。 RNDIS 连接看起来像一个网络适配器,让我 运行 TCP/IP PC 和嵌入式设备之间的连接。
设备启动后,我可以在几秒钟内使用网络浏览器查看设备上的内容。但是当我的应用 运行s 时,对 HttpClient 的第一次调用需要 90 秒才能完成。在那之后。 HttpClient 运行 正常。如果我关闭我的应用程序并再次打开它,那么 HttpClient 现在可以在第一次尝试时正常工作。应用程序启动时的 90 秒延迟对用户来说非常明显。
这个问题似乎只发生在某些 PC 上,而在其他 PC 上没有。我尝试过使用 HttpClient、WebClient 和 HttpWebRequest,但结果是一样的。在阅读 Whosebug 上的其他主题后,我还尝试了 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
是否有我遗漏的设置?
我找到了一个蛮力解决方案。我认为问题出在 ServicePointManager 的某处,所以我的解决方案是在通过使用 TcpClient 而不是 HttpClient 向该硬件发出 HTTP 请求时避免使用 ServicePointManager,然后自己构建 HTTP 请求 headers。我知道这会因为为每个请求创建和销毁套接字而带来性能损失,但我认为在这种情况下这是可以接受的,因为对设备的请求量相当小。我欢迎提出更优雅的解决方案的建议。
我的代码相当于问题中的代码是:
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);
}
}
我有一个应用程序,它 运行 在 PC 上运行,并从 Web 服务器下载文件 运行 在嵌入式设备中,该设备通过 USB 使用 RNDIS 连接到 PC。 RNDIS 连接看起来像一个网络适配器,让我 运行 TCP/IP PC 和嵌入式设备之间的连接。
设备启动后,我可以在几秒钟内使用网络浏览器查看设备上的内容。但是当我的应用 运行s 时,对 HttpClient 的第一次调用需要 90 秒才能完成。在那之后。 HttpClient 运行 正常。如果我关闭我的应用程序并再次打开它,那么 HttpClient 现在可以在第一次尝试时正常工作。应用程序启动时的 90 秒延迟对用户来说非常明显。
这个问题似乎只发生在某些 PC 上,而在其他 PC 上没有。我尝试过使用 HttpClient、WebClient 和 HttpWebRequest,但结果是一样的。在阅读 Whosebug 上的其他主题后,我还尝试了 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
是否有我遗漏的设置?
我找到了一个蛮力解决方案。我认为问题出在 ServicePointManager 的某处,所以我的解决方案是在通过使用 TcpClient 而不是 HttpClient 向该硬件发出 HTTP 请求时避免使用 ServicePointManager,然后自己构建 HTTP 请求 headers。我知道这会因为为每个请求创建和销毁套接字而带来性能损失,但我认为在这种情况下这是可以接受的,因为对设备的请求量相当小。我欢迎提出更优雅的解决方案的建议。
我的代码相当于问题中的代码是:
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);
}
}