具有无限超时的HttpClient会抛出超时exception

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

使用以下代码建立连接:

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的默认超时)后超时,抛出以下exception。

 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.d__10.MoveNext() in C:\Users\xyz\ProjName\ConsoleTester\Program.cs:line 270 Inner Exception 1: WebException: The operation has timed out. 

用于身份validation的DelegateHandler是对此代码的粗略调整(请参阅源代码部分)。

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

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

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

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

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

下面是ListenForSearchQueries方法的修改版本, ListenForSearchQueries ReadTimeout更改为Infinite。

 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()); } } } } 

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

使方法返回一个Task

 public static async Task ListenForSearchQueries(int resourceId) { //...code removed for brevity } 

更新控制台的主要方法以Wait Task完成。

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