2011-09-10 9 views
0

私はこの質問を以前に聞いたことがありますが、解決策を提案して質問を完了し、別の質問をします。非同期リクエストでタイムアウトコールバックを使用する

私は非同期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(); 
     } 
    } 
} 

、コールバックは、すぐに達しています「Timeout:false」と表示され、それ以上のスローはないため、タイムアウトは機能しません。

これは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

falseを指定してコールバックが呼び出された場合は、オブジェクトが通知されたことを意味するはずです。 –

+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()がnullを返すことがわかります、すぐに例外が発生します

asyncTask.ContinueWith() --> 

GetResponseText(callbackState.ResponseStream)--> 

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

例外コールバックはどこに置くべきですか?とにかく、これはタイムアウトの問題に対する解決策ではありません。 –

関連する問題