Мне сложно определить, есть ли способ справиться с потенциальными проблемами подключения при использовании класса .NET HttpWebRequest для вызова удаленного сервера (в частности, веб-службы REST). Из моих исследований поведение класса WebClient является одним и тем же, что несколько ожидалось, поскольку он, кажется, предлагает более простой интерфейс для HttpWebRequest.
В целях моделирования я написал очень простой HTTP-сервер, который не ведет себя в соответствии с HTTP 1.1 RFC. Что он делает, так это принятие клиентского соединения, а затем отправляет соответствующие заголовки HTTP 1.1 и "Hello World!". обратная загрузка обратно клиенту и закрывает сокет, потоки, принимающие клиентские соединения на стороне сервера, выглядят следующим образом:
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();
}
}
}
Так как HTTP 1.1 RFC заявляет, что HTTP 1.1 по умолчанию сохраняет соединения живыми и сервер должен отправить заголовок ответа "Соединение: Закрыть", если он хочет закрыть соединение, это непредвиденное поведение для клиентской стороны. Клиент использует HttpWebRequest следующим образом:
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();
}
}
Вышеуказанный метод помещается в очередь через ThreadPool.QueueUserWorkItem(waitCallback, stateObject). ManualResetEvent используется для управления поведением в очереди, так что не весь пул потоков заполняется задачами ожидания (поскольку HttpWebRequest неявно использует рабочие потоки, потому что он функционирует асинхронно для реализации функции тайм-аута).
Проблема заключается в том, что после того, как все соединения HttpWebRequest, лежащие в основе ServicePoint, "израсходованы" (т.е. закрыты удаленным сервером), новых открытий не будет. Также не имеет значения, установлено ли ConnectionLeaseTimeout из ServicePoint на низкое значение (10 секунд). Как только система попадет в это состояние, она перестанет функционировать должным образом, потому что она не будет автоматически подключаться автоматически, и все последующие HttpWebRequests будут отключены. Теперь на самом деле вопрос заключается в том, есть ли способ решить эту проблему, как-то уничтожить ServicePoint при определенных условиях или закрыть подключающиеся соединения (мне еще не повезло с ServicePoint.CloseConnectionGroup(), этот метод также довольно не документирован с точки зрения того, как для правильного использования).
Кто-нибудь знает, как я мог бы подойти к этой проблеме?