2017-08-09 153 views
3

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

這是如何被在控制檯應用程序的主方法中使用的方法。

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

這是在控制檯窗口中輸出的樣子:

Response code: OK 
--searchRequestBoundary 

即使客戶端的超時時間設置爲無窮大,連接超時後大約5分鐘(這是不在第一個輸出後,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代碼粗略適配(參見源部分)。

爲什麼客戶端超時,我該如何防止這種情況發生?

我的最終目標是撥打電話並無限期地等待迴應。當答案確實出現時,我不希望關聯,因爲未來可能會有更多答覆。不幸的是,我不能在服務器端改變任何東西。

+1

避免使用'async void'。這個方法應該用一個'Task'來定義。同時顯示如何調用此方法。 – Nkosi

+1

這似乎是[XY問題](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。你試圖達到的最終目標是什麼? – Nkosi

+0

@Nkosi該方法在控制檯應用程序的主要方法中調用,如下所示:'ListenForSearchQueries(resourceId); Console.ReadKey();' –

回答

1

雖然Stream.CanTimeoutis false,返回通過response.Content.ReadAsStreamAsync()流的默認值給出其中CanTimeout屬性返回true流。

此流的默認讀取和寫入超時is 5 minutes。這是5分鐘不活動後,該流將拋出異常。與問題中顯示的異常非常相似。

要更改此行爲,可以調整流的ReadTimeout和/或WriteTimeout屬性。

下面是將ReadTimeout更改爲無限的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拋出的異常。

2

使該方法返回一個Task

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

更新控制檯的主要方法WaitTask來完成。

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

不幸的是,這個問題在相同的異常消息中仍然存在。 –

+0

@AliZahid我很好奇。你爲什麼使用HttpClient來偵聽查詢?我再次提到這是一個XY問題。 – Nkosi

+0

基本上我想撥打電話並無限期地等待迴應。當迴應出現時,我也不想關閉連接,因爲將來可能會有更多的反應。不幸的是,我無法在服務器端更改任何內容。 –

相關問題