【问题标题】:HttpClient.GetStringAsync blocking in console app控制台应用程序中的 HttpClient.GetStringAsync 阻塞
【发布时间】:2016-05-05 19:21:42
【问题描述】:

我已经写了一个使用HttpClient的方法:

private static async Task<string> DownloadString(string url)
{
    var uri = new Uri(url);

    using (var client = new HttpClient())
    {
        HttpRequestHeaders headers = client.DefaultRequestHeaders;
        headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        headers.AcceptEncoding.ParseAdd("gzip, deflate");
        headers.AcceptLanguage.ParseAdd("en-US,en;q=0.5");
        headers.Connection.ParseAdd("keep-alive");
        headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0");
        headers.Host = uri.Host;

        return await client.GetStringAsync(uri).ConfigureAwait(false);
    }
}

然后我在控制台应用程序的Main 方法中调用它,如下所示:

Console.WriteLine("Downloading");
string dom = DownloadString("http://www.shaanig.org/f8/index1.html").Result;
Console.WriteLine(dom);

如果我有互联网连接,或者它在我没有互联网连接时运行(在这种情况下,它会引发异常),则一切正常。但是,如果我在下载时断开互联网连接,它就会挂在那里。没有例外。它只是挂起。

有人可以向我解释这里发生了什么吗?我该如何解决这个问题?

【问题讨论】:

  • @DavidHaim,在尝试定义它时,我发现它在 100 秒后抛出。
  • @EmperorAiman 和当你关闭互联网时,请求开始后大约过了多少秒?
  • @DavidHaim 大约 1 秒后我将其关闭。

标签: c# dotnet-httpclient


【解决方案1】:

我认为你必须使用 HttpClient.Timeout 属性。

在HttpClient构造中,可以使用:

var client = new HttpClient() 
{ 
    Timeout = TimeSpan.FromMilliseconds(200) 
};

【讨论】:

  • http请求已经有.Net自己设置的默认超时时间了..这没有意义
  • @DavidHaim,看起来默认是 100 秒。没想到这么长...
【解决方案2】:

在尝试定义它时,我发现它会在 100 秒后抛出。

大约 1 秒后我将其关闭

所以,没有错误/挂起。
在 Windows 上,.Net 使用底层原生 WinHttp API 来发出 http 请求。

如果我们查看.Net Code which calls WinHttp,我们会看到

private TimeSpan _connectTimeout = TimeSpan.FromSeconds(60);
private TimeSpan _sendTimeout = TimeSpan.FromSeconds(30);
private TimeSpan _receiveHeadersTimeout = TimeSpan.FromSeconds(30);
private TimeSpan _receiveDataTimeout = TimeSpan.FromSeconds(30);

如果您在请求开始后大约一秒钟阻止互联网,大约需要 30~60 秒才能触发超时。

【讨论】:

  • 实际上是 100 秒。 The default value is 100,000 milliseconds (100 seconds).
【解决方案3】:

最好将它从静态类更改为实例类,然后在单独的线程上运行它。使用静态类和多线程时会遇到麻烦。

其次,你为什么在这里调用 ConfigureAwait:

return await client.GetStringAsync(uri).ConfigureAwait(false);

您正在使用 async/await 模式,因此您不需要使用它。

【讨论】:

  • 如果我不想捕获同步上下文,ConfigureAwait 是最佳实践。我怀疑使用实例方法是否可行。另外,如果它是异步的,我为什么要启动一个新线程?
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
相关资源
最近更新 更多