que tienen un tiempo difícil averiguar si hay una manera de manejar los posibles problemas de conectividad cuando se utiliza la clase HttpWebRequest de .NET para llamar a un servidor remoto (en concreto un servicio web REST). Según mis investigaciones, el comportamiento de la clase WebClient es el mismo, algo que se espera, ya que parece ofrecer solo una interfaz más simple para HttpWebRequest.HttpWebRequest ¿Cómo manejar el cierre (prematuro) de la conexión TCP subyacente?
Para fines de simulación, he escrito un servidor HTTP muy simple que no se comporta de acuerdo con el RFC HTTP 1.1. Lo que hace es aceptar una conexión de cliente, luego envía encabezados de HTTP 1.1 apropiados y un "¡Hola mundo!" de carga útil de vuelta al cliente y cierra el socket, el hilo de aceptar conexiones de cliente en el servidor se ve de la siguiente manera:
private const string m_defaultResponse = "<html><body><h1>Hello World!</h1></body></html>";
private void Listen()
{
while (true)
{
using (TcpClient clientConnection = m_listener.AcceptTcpClient())
{
NetworkStream stream = clientConnection.GetStream();
StringBuilder httpData = new StringBuilder("HTTP/1.1 200 OK\r\nServer: ivy\r\nContent-Type: text/html\r\n");
httpData.AppendFormat("Content-Length: {0}\r\n\r\n", m_defaultResponse.Length);
httpData.AppendFormat(m_defaultResponse);
Thread.Sleep(3000); // Sleep to simulate latency
stream.Write(Encoding.ASCII.GetBytes(httpData.ToString()), 0, httpData.Length);
stream.Close();
clientConnection.Close();
}
}
}
Desde los HTTP 1.1 estados RFC que HTTP 1.1 por defecto mantiene conexiones con vida y que un servidor debe enviar un encabezado de respuesta "Conexión: cerrar" si desea cerrar una conexión, este es un comportamiento inesperado para el lado del cliente. El cliente utiliza HttpWebRequest de la siguiente manera:
private static void SendRequest(object _state)
{
WebResponse resp = null;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.0.32:7070/asdasd");
request.Timeout = 50 * 1000;
DateTime requestStart = DateTime.Now;
resp = request.GetResponse();
TimeSpan requestDuration = DateTime.Now - requestStart;
Console.WriteLine("OK. Request took: " + (int)requestDuration.TotalMilliseconds + " ms.");
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
{
Console.WriteLine("Timeout occurred");
}
else
{
Console.WriteLine(ex);
}
}
finally
{
if (resp != null)
{
resp.Close();
}
((ManualResetEvent)_state).Set();
}
}
El método anterior se pone en cola a través de ThreadPool.QueueUserWorkItem (waitCallback, stateObject). ManualResetEvent se utiliza para controlar el comportamiento de cola para que no todo el grupo de subprocesos se llene con tareas en espera (ya que HttpWebRequest utiliza implícitamente subprocesos de trabajo porque funciona de forma asincrónica internamente para implementar la funcionalidad de tiempo de espera).
El problema con todo esto es que una vez que todas las conexiones del ServicePoint subyacente de HttpWebRequest se "agotan" (es decir, se cierran por el servidor remoto), no se abrirán nuevas. Tampoco importa si ConnectionLeaseTimeout de ServicePoint está configurado en un valor bajo (10 segundos). Una vez que el sistema entre en este estado, ya no funcionará correctamente porque no se reconectará automáticamente y todas las HttpWebRequests subsiguientes agotarán el tiempo de espera. Ahora la pregunta realmente es si hay una manera de resolver esto mediante la destrucción de alguna manera una ServicePoint bajo ciertas condiciones o cerrar conexiones underyling (yo no tenía ninguna suerte con ServicePoint.CloseConnectionGroup(), sin embargo, el método también es bastante indocumentado en términos de cómo para usarlo apropiadamente).
¿Alguien tiene alguna idea de cómo podría abordar este problema?