2015-12-21 213 views
2

我們正在看到一個問題,即客戶在完成API之前取消了請求並取消了請求。我們相信他們也正在關閉連接,所以請求沒有寫入IIS日誌。我試圖創建一個跑步者來複制這種行爲。我想不出的是我該如何強制關閉連接,以便服務器不能發回數據?C#HttpClient - 我可以強制關閉連接嗎?

這裏是我當前的代碼:

private static async Task RunMe() 
    { 

     var cts = new CancellationTokenSource(); 
     cts.CancelAfter(5000); 

     using (var client = new HttpClient()) 
     { 
      try 
      { 
       client.DefaultRequestHeaders.ConnectionClose = true; 
       client.DefaultRequestHeaders.Add("x-apikey", "some key"); 
       Console.WriteLine("making request"); 
       using (var response = await client.GetAsync("https://foo.com/api/apitest/testcancel", cts.Token)) 
       { 
        var result = await response.Content.ReadAsStringAsync(); 
        Console.WriteLine($"Request completed: {result}"); 
       } 
      } 
      finally 
      {      
       client.CancelPendingRequests(); 
       Console.WriteLine("about to dispose the client"); 
       client.Dispose(); 
      } 
     } 
    } 
} 

代碼正確取消後5秒的請求,但IIS似乎仍然在API調用完成(設置爲10秒)後發回的數據。

如何關閉連接,以便IIS在5秒超時後無法發回任何數據?

+0

只需使用一個插座並在發送請求後砰的一聲門。 – CodeCaster

+0

@CodeCaster這似乎是一個很好的計劃。我在這裏嘗試了一個例子,但它沒有發送任何數據。你有一個與https協同工作的樣本嗎? https://gist.github.com/jayhilden/ea6603adb22c36daeae6 – jhilden

回答

2

我終於可以通過使用原始套接字來複制問題,這是很多工作。讓IIS記錄的取消請求的方法是做在下面的代碼如下:

  1. 不發送Connection: Close
  2. 不要來電Socket.Dispose()

這裏是最後的工作代碼,希望別人會發現這個有用的:

private static void SocketTime(int readTimeout, Random random) 
     { 
      var ip = IPAddress.Parse("127.0.0.1"); 
      const int hostPort = 80; 

      var hostep = new IPEndPoint(ip, hostPort); 
      var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

      sock.Connect(hostep); 
      var uniqueID = random.Next().ToString(); 
      var get = $"GET http://localhost/api/apitest/testcancel/{uniqueID} HTTP/1.1\r\n" + 
         "x-apikey: somekey\r\n" + 
         "Host: foo.com\r\n" + 
         "Connection: Close\r\n" + 
         "\r\n"; 


      var getBytes = Encoding.UTF8.GetBytes(get); 
      sock.ReceiveTimeout = readTimeout; 
      sock.Send(getBytes); 

      var responseData = new byte[1024]; 
      try 
      { 
       var bytesRead = sock.Receive(responseData); 
       var responseString = new StringBuilder(); 
       while (bytesRead != 0) 
       { 
        responseString.Append(Encoding.ASCII.GetChars(responseData), 0, bytesRead); 
        bytesRead = sock.Receive(responseData); 
       } 
       Console.WriteLine($"SUCCESS => Unique ID = {uniqueID}, Timeout = {readTimeout}"); 
      } 
      catch (SocketException e) 
      { 
       if (e.SocketErrorCode == SocketError.TimedOut) 
       { 
        Console.WriteLine($"FAIL => Unique ID = {uniqueID}, Timeout = {readTimeout}"); 
       } 
       else 
       { 
        Console.WriteLine("UNHNALDED ERROR: " + e.Message); 
       } 

      } 
      finally 
      { 
       sock.Dispose(); 
      } 
     } 
1

你可能是由一個非常低的超時設置命中。如果將其設置爲足夠短的時間,客戶端將關閉連接,儘管服務器仍嘗試返回數據。

如果這不能重現問題,您還可以查看併發性以及servicepoint管理器上的屬性'UseNagleAlgorithm'。這個屬性是真實的,試圖在同一個TCP包中啄食幾個小的http請求。它是一個帶有保護程序的樂隊,但隨着os延遲通過套接字發送數據,直到它收集到足夠的數據,導致客戶端超時。

+0

_「在同一個TCP包中有幾個小的http請求」__n,Nagle沒有應用協議的概念。 Nagle可以讓一個消息的頭部和正文結束在同一個數據包中,例如,只需等待更多數據,直到足夠發送完整數據包 - 或者直到發生預配置的超時爲止。 – CodeCaster

+0

我不確定我是否瞭解其中的差異。我沒有聲稱它知道http,但我已經看到與您描述的確切行爲有關的問題,因爲客戶端計算從應用程序啓動時的超時時間,但實際的網絡通信是在延遲等待更多數據之後開始的。由於這對應用程序是透明的,因此如果您查看網絡流量,它會報告網絡超時情況並不明顯。 – faester

+1

在接收到上一個響應之前,HTTP 1/1客戶端不會通過同一連接發送下一個請求。:)因此,如果請求1仍然在發送緩衝區中,應用程序將不會收到響應,因此不會發出請求2.您對Nagle的評論已被發現,我不認爲它會導致兩個HTTP請求在一個數據包中。 :) – CodeCaster