c# - termine - Usar una devolución de llamada de tiempo de espera en solicitudes asíncronas
programacion asincrona javascript (2)
Usa 2 clases diferentes:
class RequestCallbackException : Exception
{
public RequestCallbackException(Stream responseStream, Exception exception) : base(exception)
{
}
}
y
class RequestCallbackStream
{
public Stream ResponseStream { get; private set; }
public RequestCallbackState(Stream responseStream)
{
ResponseStream = responseStream;
}
}
Notarás que a veces GetResponseStream () devuelve null, lo que inmediatamente genera una excepción en
asyncTask.ContinueWith() -->
GetResponseText(callbackState.ResponseStream)-->
using (var reader = new StreamReader(responseStream)) // responseStream is null
{
}
Hice esta pregunta antes, pero voy a completar la pregunta con una solución propuesta y hacer otra pregunta.
Estoy usando esta clase para hacer una WebRequest asincrónica:
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();
}
}
}
}
Y estoy probando la clase con este código:
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();
}
}
}
Una vez ejecutado, la devolución de llamada se alcanza de inmediato, mostrando "Tiempo de espera: falso" y no hay más tiradas, por lo que el tiempo de espera no funciona.
Esta es una solución propuesta en el hilo original pero, como se puede ver, el código funciona para él.
¿Qué estoy haciendo mal?
EDITAR: Otras clases utilizadas por el código:
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; }
}
Este enfoque funciona Recomendaría cambiar esto para manejar explícitamente las excepciones (incluido el tiempo de espera, pero también los nombres de dominio incorrectos, etc.) de forma ligeramente diferente. En este caso, he dividido esto en una continuación separada.
Además, para hacer esto muy explícito, reduje el tiempo de espera, agregué un dominio "real" pero lento, y agregué un estado de tiempo de espera explícito que puede ver:
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();
}
}
}
Esto se ejecutará y mostrará un tiempo de espera de manera apropiada.