HttpClient.GetStringAsync 在控制台应用程序中阻止

HttpClient.GetStringAsync blocking in console app

我用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);

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

谁能给我解释一下这是怎么回事?我该如何解决这个问题?

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

在HttpClient构造中,可以使用:

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

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

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

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

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

while trying to define that, I found that it throws after a whopping 100 seconds.

I shut it down after like 1 seconds

所以,没有bug/hang。
在 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 秒才能触发超时。