2011-09-10 2 views
0

나는이 질문을하기 전에 질문했지만 해결책을 제시하고 질문을 완료하고 다른 질문을 할 것입니다. , 비동기 요청에서 시간 초과 콜백 사용

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

일단, 콜백 즉시 도달 실행 :

나는 비동기 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(); 
      } 
     } 
    } 
} 

을 그리고 난이 코드로 클래스를 테스트하고 있습니다 "Timeout : false"를 표시하고 더 이상 throw되지 않으므로 시간 초과가 작동하지 않습니다.

이 해결책은 original thread에서 제안되었지만, 알 수 있듯이이 코드는 그를 위해 작동합니다.

내가 뭘 잘못하고있어?

편집 :

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

사용이 개 다른 클래스 : 코드에서 사용하는 다른 클래스 당신은 그것을 알 수

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

예외 콜백은 어디에 두어야합니까? 어쨌든, 이것은 타임 아웃 문제에 대한 해결책이 아닙니다. –