8

Innerhalb einer Portable Class Library habe ich die folgende Methode, die Daten an eine bestimmte URL senden. Die Methode funktioniert großartig. Ich möchte jedoch ein aggressiveres Timeout angeben (der Standardwert ist 100 Sekunden).Wie definiert man ein aggressiveres Timeout für HttpWebRequest?

Wenn in der HttpWebRequest-Klasse aus der Portable Class Library keine Timeout-Eigenschaft vorhanden ist, wie kann ich sicherstellen, dass der Aufruf abgebrochen wird, wenn er länger als ein paar Sekunden dauert?

public async Task<HttpResponse> PostAsync(Uri uri, string data) 
{ 
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 
    request.Method = "POST"; 
    request.ContentType = "application/x-www-form-urlencoded"; 

    using (Stream requestStream = await request.GetRequestStreamAsync()) 
    { 
     byte[] postBytes = Encoding.UTF8.GetBytes(data); 
     requestStream.Write(postBytes, 0, postBytes.Length); 
    } 

    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); 
    return new HttpResponse(response.StatusCode, await new StreamReader(response.GetResponseStream()).ReadToEndAsync()); 
} 
+0

HttpWebRequest.Timeout = 20000; ? Das ist schlau genug? –

Antwort

20

Unter dem Code wird entweder eine HttpWebResponse zurückgegeben oder Null, wenn das Zeitlimit überschritten wurde.

HttpWebResponse response = await TaskWithTimeout(request.GetResponseAsync(), 100); 
if(response != null) 
{ 
    .... 
} 

Task<HttpWebResponse> TaskWithTimeout(Task<WebResponse> task, int duration) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     bool b = task.Wait(duration); 
     if (b) return (HttpWebResponse)task.Result; 
     return null; 
    }); 
} 

--EDIT--

eine Erweiterungsmethode erstellen wäre noch besser

public static class SOExtensions 
{ 
    public static Task<T> WithTimeout<T>(this Task<T> task, int duration) 
    { 
     return Task.Factory.StartNew(() => 
     { 
      bool b = task.Wait(duration); 
      if (b) return task.Result; 
      return default(T); 
     }); 
    } 
} 

Verwendung wäre:

var response = (HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000); 

--edit 2--

Ein anderer Weg, es zu tun

public async static Task<T> WithTimeout<T>(this Task<T> task, int duration) 
{ 
    var retTask = await Task.WhenAny(task, Task.Delay(duration)) 
          .ConfigureAwait(false); 

    if (retTask is Task<T>) return task.Result; 
    return default(T); 
} 
+0

Wow! Interessante Idee! Ich mag es, weil es elegant und relativ einfach zu verstehen ist. Aber ich frage mich, ob task.Wait eine blockierende Operation ist (d. H. Ein Thread wird in die Warteschleife gesetzt, bis er zurückkehrt). – Martin

+1

@Martin Ja, es ist eine blockierende Operation, aber es wird in einer anderen Task ausgeführt (siehe: 'Task.Factory.StartNew') und wird nicht anders sein als das Aufrufen von' await request.GetResponseAsync() ' –

+0

Ich stimme zu! Es ist nur noch ein Thread auf dem ThreadPool. Ich frage mich, ob wir es nicht blockieren könnten. Blockieren oder nicht, das ist eine sehr clevere Lösung. – Martin

0
// Abort the request if the timer fires. 
private static void TimeoutCallback(object state, bool timedOut) { 
    if (timedOut) { 
     HttpWebRequest request = state as HttpWebRequest; 
      if (request != null) { 
       request.Abort(); 
     } 
    } 
} 

Ja, es liegt in der Verantwortung der Client-Anwendung einen eigenen Time-out-Mechanismus zu implementieren. Sie können dies über den obigen Code tun, der das Zeitlimit setzt und die Methode ThreadPool.RegisterWaitForSingleObject verwendet. Vollständige Details finden Sie unter http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx. Im Wesentlichen wird ein Abbruch von GetResponse, BeginGetResponse, EndGetResponse, GetRequestStream, BeginGetRequestStream oder EndGetRequestStream durchgeführt.

Verwandte Themen