具有无限超时的HttpClient会抛出异常

时间:2017-08-09 02:39:12

标签: c# http dotnet-httpclient

我的HttpClient使用摘要式身份验证连接到服务器,并期望搜索查询。这些搜索查询可以随时进入,因此客户应始终保持连接处于打开状态。

使用以下代码建立连接:

public static async void ListenForSearchQueries(int resourceId)
{
    var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";

    var httpHandler = new HttpClientHandler { PreAuthenticate = true };

    using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
    using (var client = new HttpClient(digestAuthMessageHandler))
    {
        client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);

        var request = new HttpRequestMessage(HttpMethod.Get, url);

        var tokenSource = new CancellationTokenSource();
            tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));

        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
        {
            Console.WriteLine("\nResponse code: " + response.StatusCode);

            using (var body = await response.Content.ReadAsStreamAsync())
            using (var reader = new StreamReader(body))
                while (!reader.EndOfStream)
                    Console.WriteLine(reader.ReadLine());
         }
    }
}

这是在控制台应用程序的Main方法中使用该方法的方法。

private static void Main(string[] args)
{
   const int serviceId = 128;
   .
   .
   .
   ListenForSearchQueries(resourceId);
   Console.ReadKey();
}

这是控制台窗口上的输出:

Response code: OK
--searchRequestBoundary

即使客户端的超时设置为无穷大,连接在第一次输出后大约五分钟(这不是HttpClient的默认超时)后超时,抛出以下异常。

System.IO.IOException occurred
  HResult=0x80131620
  Message=The read operation failed, see inner exception.
  Source=System.Net.Http
  StackTrace:
   at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Http.DelegatingStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer()
   at System.IO.StreamReader.get_EndOfStream()
   at ConsoleTester.Program.<ListenSearchQueriesDigestAuthMessageHandler>d__10.MoveNext() in C:\Users\xyz\ProjName\ConsoleTester\Program.cs:line 270

Inner Exception 1:
WebException: The operation has timed out.

用于身份验证的DelegateHandler是对this代码的粗略修改(请参阅源代码部分)。

为什么客户端超时,我该如何防止这种情况?

我的最终目标是打个电话并无限期地等待回应。当响应确实到来时,我不希望关闭连接,因为将来可能会有更多响应。不幸的是,我无法在服务器端更改任何内容。

3 个答案:

答案 0 :(得分:4)

虽然Stream.CanTimeout is false的默认值,但是通过response.Content.ReadAsStreamAsync()返回一个流会给出一个CanTimeout属性返回true的流。

此流is 5 minutes的默认读写超时时间。这是在五分钟不活动之后,流将抛出异常。与问题中显示的例外非常相似。

要更改此行为,可以调整流的ReadTimeout和/或WriteTimeout属性。

以下是将{Time}更改为无限的ListenForSearchQueries方法的修改版本。

public static async void ListenForSearchQueries(int resourceId)
{
    var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";

    var httpHandler = new HttpClientHandler { PreAuthenticate = true };

    using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
    using (var client = new HttpClient(digestAuthMessageHandler))
    {
        client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);

        var request = new HttpRequestMessage(HttpMethod.Get, url);

        var tokenSource = new CancellationTokenSource();
            tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));

        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
        {
            Console.WriteLine("\nResponse code: " + response.StatusCode);

            using (var body = await response.Content.ReadAsStreamAsync())
            {
                body.ReadTimeout = Timeout.Infinite;

                using (var reader = new StreamReader(body))
                    while (!reader.EndOfStream)
                        Console.WriteLine(reader.ReadLine());
            }
         }
    }
}

这修复了实际被流抛出的异常,但似乎被HttpClient抛出了。

答案 1 :(得分:2)

让方法返回try_lock

Task

将控制台的主要方法更新为public static async Task ListenForSearchQueries(int resourceId) { //...code removed for brevity } 上的Wait以完成。

Task

答案 2 :(得分:0)

我通过以下方式解决了这个问题:

var stream = await response.Content.ReadAsStreamAsync();
    while (b == 1)
    { 
        var bytes = new byte[1];
        try
        {
            var bytesread = await stream.ReadAsync(bytes, 0, 1);
            if (bytesread > 0)
            {
                text = Encoding.UTF8.GetString(bytes);

                Console.WriteLine(text);
                using (System.IO.StreamWriter escritor = new System.IO.StreamWriter(@"C:\orden\ConSegu.txt", true))
                {
                    if (ctext == 100)
                    {
                        escritor.WriteLine(text);
                        ctext = 0;
                    }
                    escritor.Write(text);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("error");
            Console.WriteLine(ex.Message);
        }
    }

通过这种方式,我得到了逐字节的答案并将其保存在txt中 稍后,我阅读了txt,然后又将其擦除。目前,这是我发现从持久性HTTP连接接收服务器发送给我的通知的解决方案。