2011-09-10 3 views
0

Я задал этот вопрос раньше, но я собираюсь завершить вопрос с предложенным решением и задать другой вопрос.Использование обратного вызова таймаута в async-запросах

Я использую этот класс, чтобы сделать асинхра WebRequest:

class HttpSocket 
{ 
    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback) 
    { 
     WebRequest request = WebRequest.Create(uri); 
     request.Proxy = null; 

     Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null); 
     ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, 1000, true); 
     asyncTask.ContinueWith(task => 
      { 
       WebResponse response = task.Result; 
       Stream responseStream = response.GetResponseStream(); 
       responseCallback(new RequestCallbackState(response.GetResponseStream())); 
       responseStream.Close(); 
       response.Close(); 
      }); 
    } 

    private static void TimeoutCallback(object state, bool timedOut) 
    { 
     Console.WriteLine("Timeout: " + timedOut); 
     if (timedOut) 
     { 
      Console.WriteLine("Timeout"); 
      WebRequest request = (WebRequest)state; 
      if (state != null) 
      { 
       request.Abort(); 
      } 
     } 
    } 
} 

И я буду тестировать класс с этим кодом:

class Program 
{ 
    static void Main(string[] args) 
    { 
     // Making a request to a nonexistent domain. 
     HttpSocket.MakeRequest(new Uri("http://www.google.comhklhlñ"), callbackState => 
      { 
       if (callbackState.Exception != null) 
        throw callbackState.Exception; 
       Console.WriteLine(GetResponseText(callbackState.ResponseStream)); 
      }); 
     Thread.Sleep(100000); 
    } 

    public static string GetResponseText(Stream responseStream) 
    { 
     using (var reader = new StreamReader(responseStream)) 
     { 
      return reader.ReadToEnd(); 
     } 
    } 
} 

После выполнения обратного вызова немедленно достигнут, показывая «Тайм-аут: ложь», и нет больше бросков, поэтому тайм-аут не работает.

Это решение предлагается в original thread, но, как вы могли видеть, код работает для него.

Что я делаю неправильно?

EDIT: Другие классы, используемые в коде:

class RequestCallbackState 
{ 
    public Stream ResponseStream { get; private set; } 
    public Exception Exception { get; private set; } 

    public RequestCallbackState(Stream responseStream) 
    { 
     ResponseStream = responseStream; 
    } 

    public RequestCallbackState(Exception exception) 
    { 
     Exception = exception; 
    } 
} 

class RequestState 
{ 
    public byte[] RequestBytes { get; set; } 
    public WebRequest Request { get; set; } 
    public Action<RequestCallbackState> ResponseCallback { get; set; } 
} 
+0

Что такое 'RequestCallbackState'? – BrokenGlass

+0

Если обратный вызов вызывается с ложью, это означает, что объект сигнализируется. –

+0

@BrokenGlass Спасибо за ответ. Я добавил эти классы к сообщению –

ответ

6

Этот подход работает. Я бы рекомендовал переключить это, чтобы явно обрабатывать исключения (включая ваш тайм-аут, а также неправильные имена доменов и т. Д.) Несколько иначе. В этом случае я разделил это на отдельное продолжение.

Кроме того, для того, чтобы сделать это очень четко, я замкнута время тайм-аута, поставить «реальный», но медленный домен в, а также добавил явное состояние ожидания вы можете увидеть:

using System; 
using System.IO; 
using System.Net; 
using System.Threading; 
using System.Threading.Tasks; 

class HttpSocket 
{ 
    private const int TimeoutLength = 100; 

    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback) 
    { 
     WebRequest request = WebRequest.Create(uri); 
     request.Proxy = null; 

     Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null); 
     ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, TimeoutLength, true); 
     asyncTask.ContinueWith(task => 
      { 
       WebResponse response = task.Result; 
       Stream responseStream = response.GetResponseStream(); 
       responseCallback(new RequestCallbackState(response.GetResponseStream())); 
       responseStream.Close(); 
       response.Close(); 
      }, TaskContinuationOptions.NotOnFaulted); 
     // Handle errors 
     asyncTask.ContinueWith(task => 
      { 
       var exception = task.Exception; 
       var webException = exception.InnerException; 

       // Track whether you cancelled or not... up to you... 
       responseCallback(new RequestCallbackState(exception.InnerException, webException.Message.Contains("The request was canceled."))); 
      }, TaskContinuationOptions.OnlyOnFaulted); 
    } 

    private static void TimeoutCallback(object state, bool timedOut) 
    { 
     Console.WriteLine("Timeout: " + timedOut); 
     if (timedOut) 
     { 
      Console.WriteLine("Timeout"); 
      WebRequest request = (WebRequest)state; 
      if (state != null) 
      { 
       request.Abort(); 
      } 
     } 
    } 
} 

class RequestCallbackState 
{ 
    public Stream ResponseStream { get; private set; } 
    public Exception Exception { get; private set; } 

    public bool RequestTimedOut { get; private set; } 

    public RequestCallbackState(Stream responseStream) 
    { 
     ResponseStream = responseStream; 
    } 

    public RequestCallbackState(Exception exception, bool timedOut = false) 
    { 
     Exception = exception; 
     RequestTimedOut = timedOut; 
    } 
} 

class RequestState 
{ 
    public byte[] RequestBytes { get; set; } 
    public WebRequest Request { get; set; } 
    public Action<RequestCallbackState> ResponseCallback { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     // Making a request to a nonexistent domain. 
     HttpSocket.MakeRequest(new Uri("http://www.tanzaniatouristboard.com/"), callbackState => 
      { 
       if (callbackState.RequestTimedOut) 
       { 
        Console.WriteLine("Timed out!"); 
       } 
       else if (callbackState.Exception != null) 
        throw callbackState.Exception; 
       else 
        Console.WriteLine(GetResponseText(callbackState.ResponseStream)); 
      }); 
     Thread.Sleep(100000); 
    } 

    public static string GetResponseText(Stream responseStream) 
    { 
     using (var reader = new StreamReader(responseStream)) 
     { 
      return reader.ReadToEnd(); 
     } 
    } 
} 

Это запустится и отобразит соответствующий тайм-аут.

+0

Большое спасибо. Все работает хорошо сейчас;) –

1

использования 2 различных классов:

class RequestCallbackException : Exception 
{ 
    public RequestCallbackException(Stream responseStream, Exception exception) : base(exception) 
    { 
    } 
} 

и

class RequestCallbackStream 
{ 
    public Stream ResponseStream { get; private set; } 

    public RequestCallbackState(Stream responseStream) 
    { 
     ResponseStream = responseStream; 
    } 
} 

Вы заметите, что иногда GetResponseStream() возвращает нуль , который немедленно вызывает исключение в

asyncTask.ContinueWith() --> 

GetResponseText(callbackState.ResponseStream)--> 

using (var reader = new StreamReader(responseStream)) // responseStream is null 
{ 
} 
+0

Где я должен поместить обратный вызов Exception? В любом случае, это не решение проблемы тайм-аута, не так ли? –

Смежные вопросы