HttpWebResponse ReadAsync超时

问题描述 投票:1回答:1

我尝试使用await / async读取HttpWebResponse的流:

    async static Task testHttpWebClientAsync()        
    {

        string url = "http://localhost/1.txt";
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.Method = "GET";
        HttpWebResponse resp = (HttpWebResponse)await req.GetResponseAsync();            
        Stream stream = resp.GetResponseStream();
        stream.ReadTimeout = 10 * 1000;
        byte[] buffer = new byte[1024];
        while (await stream.ReadAsync(buffer, 0, buffer.Length) > 0)                           
        {
            //time out exception never thrown
        }
    }

但它不起作用,它永远不会超时ReadAsync。为了进行比较,非异步版本与同一个localhost测试服务器完美配合:

    static void testHttpWebClient()
    {            
        string url = "http://localhost/1.txt";
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.Method = "GET";
        HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
        Stream stream = resp.GetResponseStream();
        stream.ReadTimeout = 10 * 1000;
        byte[] buffer = new byte[1024];
        while (stream.Read(buffer, 0, buffer.Length) > 0)
        {
            //time out exception thrown here
        }
    }

以上代码在控制台应用程序中进行测试:

    static void Main(string[] args)
    {
        testHttpWebClient();
        MainAsync(args).GetAwaiter().GetResult();
    }
    async static Task MainAsync(string[] args)
    {
        await testHttpWebClientAsync();            
    }

但这与问题无关,事实上我在WinForms项目中发现问题并创建控制台项目来测试问题。

作为参考,测试服务器代码类似于:

                    int c = 10;
                    byte[] ba = new byte[1024];
                    SendHeader(sHttpVersion, sMimeType,(int) ba.Length*c, " 200 OK", ref mySocket);
                    for (int k = 0; k < c; k++)
                    {
                        //set break point here
                        SendToBrowser(ba, ref mySocket);
                    }

SO上有几个类似的主题,但似乎没有一个能解决这个问题。从API设计的角度来看,显然没有理由说ReadAsync()没有像Read()那样超时,ReadAsync只需要同时监视套接字和内部定时器事件,这就是Task.Delay()的工作原理。这与CancellationToken等无关,因为我们不需要取消任何内容,即使ReadAsync有一个接受CancellationToken的版本。

所以这个问题既是问题的解决方案,也是为什么ReadAsync不会按预期超时。

c# async-await stream timeout
1个回答
1
投票

HttpWebRequest上的异步API(以及WebClient,因为它在内部使用HttpWebRequest)不在内部使用超时。虽然我无法真正解释它背后的原因,但这是设计的。

这尤其明显in the Write logic of the ConnectStream (used internally by HttpWebResponse)

if (async) {
    m_Connection.BeginMultipleWrite(buffers, m_WriteCallbackDelegate, asyncResult);
}
else {
    SafeSetSocketTimeout(SocketShutdown.Send);
    m_Connection.MultipleWrite(buffers);
}

SafeSetSocketTimeout是负责在底层套接字上设置超时的方法。正如您所看到的,它是故意在异步路径上跳过的。读操作也是一样,但代码更复杂,因此更难显示。

因此,您真的只有两个解决方案:

  1. 自己实现超时(通常使用在.Abort()上调用HttpWebRequest的计时器)
  2. 请改用HttpClient
© www.soinside.com 2019 - 2024. All rights reserved.