Ho difficoltà a capire se esiste un modo per gestire potenziali problemi di connettività quando si utilizza la classe HttpWebRequest di .NET per chiamare un server remoto (in particolare un servizio Web REST). Dalle mie indagini il comportamento della classe WebClient è lo stesso, il che è in qualche modo previsto dal momento che sembra offrire solo un'interfaccia più semplice a HttpWebRequest.HttpWebRequest Come gestire (prematura) la chiusura della connessione TCP sottostante?
Per scopi di simulazione, ho scritto un server HTTP molto semplice che non si comporta in conformità con l'RFC HTTP 1.1. Quello che fa è che accetta una connessione client, quindi invia intestazioni HTTP 1.1 appropriate e un "Hello World!" payload al client e chiude la presa, il filo di accettare connessioni client sul lato server si presenta come segue:
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();
}
}
}
Dal momento che le HTTP 1.1 stati RFC che HTTP 1.1 di default mantiene viva connessioni e che un server deve inviare un'intestazione di risposta "Connessione: Chiudi" se vuole chiudere una connessione, questo è un comportamento imprevisto per il lato client. Il client utilizza HttpWebRequest nel seguente modo:
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();
}
}
il metodo di cui sopra è in coda tramite ThreadPool.QueueUserWorkItem (WaitCallback, stateObject). ManualResetEvent viene utilizzato per controllare il comportamento di accodamento in modo che non l'intero pool di thread venga riempito con attività di attesa (poiché HttpWebRequest utilizza implicitamente thread di lavoro perché funziona internamente in modo asincrono per implementare la funzionalità di timeout).
Il problema con tutto questo è che una volta che tutte le connessioni del ServicePoint sottostante di HttpWebRequest sono "esaurite" (cioè chiuse dal server remoto), non ne verranno aperte di nuove. Non importa se ConnectionLeaseTimeout di ServicePoint è impostato su un valore basso (10 secondi). Una volta che il sistema entra in questo stato, non funzionerà più correttamente perché non si ricollegherà automaticamente e tutti i successivi HttpWebRequests scadranno. Ora la domanda è davvero se c'è un modo per risolvere questo in qualche modo distruggendo un ServicePoint in determinate condizioni o chiudendo le connessioni underyling (non ho avuto fortuna con ServicePoint.CloseConnectionGroup() ancora, il metodo è anche piuttosto non documentato in termini di come per usarlo correttamente).
Qualcuno ha idea di come affrontare questo problema?