Мы разрабатываем приложение .NET CF 3.5 на платформе Windows Embedded CE 6. Мы пытаемся реализовать небольшой (HTTP 1.0) веб-сервер в .NET, который должен доставлять WebApp и отвечать на простые запросы REST.
Наша реализация следует шаблону, показанному в этой статье MSDN: http://msdn.microsoft.com/en-us/library/aa446537.aspx. Мы используем сокет прослушивания TCP, асинхронные обратные вызовы в сочетании с BeginAccept, EndAccept, BeginRecieve и EndReceive.
Входящее соединение на слушающем порту обрабатывается асинхронным обратным вызовом accept. (см. http://msdn.microsoft.com/en-us/library/5bb431f9.aspx). Вызывая метод EndAccept в этом асинхронном обратном вызове accept, мы говорим слушающему порту передать соединение новому сокету и освободить порт, чтобы новые входящие запросы на соединение могли быть приняты слушающим портом. Уже принятый запрос обрабатывается в собственном потоке с (потому что он обрабатывается в обратном вызове async).
Мы уже пытались минимизировать время между BeginAccept и EndAccept. Поскольку в течение этого периода времени между вызовами BeginAccept и EndAccept входящие запросы на соединение помещаются в очередь невыполненных работ прослушивающего сокета. Длину этой очереди можно настроить с помощью так называемого параметра backlog - этот параметр имеет максимум, зависящий от платформы. Если очередь невыполненных работ исчерпана, новые запросы TCP-соединения отклоняются во время трехстороннего рукопожатия (клиент / браузер получает RST в ответ на синхронизацию).
Теперь мы столкнулись с проблемой, заключающейся в том, что большинство современных браузеров, таких как Firefox, Chrome, Safari, например используйте до 15 (или более) одновременных подключений для загрузки данных с сервера (максимальное количество одновременных подключений на хост можно настроить в Firefox с помощью about: config -> network.http.max-connections-per-server). Когда страница загружается, браузер при необходимости устанавливает 15 соединений, в зависимости от количества ресурсов, которые необходимо загрузить (например, изображений, файлов javascript или css).
Метод .NET CF socket.listen (см. http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.listen.aspx) позволяет определять номер невыполненной работы.
Насколько мы понимаем, у нас должна быть очередь больше 15 например 20 или около того, потому что все запросы на подключение запускаются браузером одновременно, поэтому наш небольшой веб-сервер получил 15 одновременных запросов на подключение. Слишком маленький размер очереди невыполненных работ приводит к прерыванию TCP-соединений, потому что не все входящие соединения могут быть поставлены в очередь до тех пор, пока слушающий сокет не сможет их все принять. В Firebug или Chrome эти запросы отображаются как «прерванные». Таким образом, мы увеличили отставание в нашем случае на socket.listen (20) до 20 и надеялись, что все будет хорошо и готово противостоять даже самым жадным браузерам.
Проблема в том, что параметр невыполненной работы в вызове socket.listen () автоматически устанавливается на SOMAXXCON (в нашем случае не более 5 соединений). Установка числа выше этого значения не имеет. Когда браузер устанавливает, например, Подключается 16 одновременных сокетов, некоторые из них потерялись из-за того, что некоторые сокеты просто не помещаются в очередь невыполненных работ из 5, а TCP-соединение получает TCP-RST с веб-сервера - а некоторые ресурсы отсутствуют на веб-странице.
Есть ли способ изменить SOMAXXCON в Windows Embedded CE 6.0? (Мы можем изменить изображение платформы - используйте конструктор платформ). Или есть ошибка в нашем понимании этого вопроса?
Мы прикрепили исходный код, который мы сейчас используем:
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public void StartListening()
{
logger.Debug("Started Listening at : " + this.listeninghostIp + ":" + this.listeningport);
IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(this.listeninghostIp), Convert.ToInt32(this.listeningport));
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEP);
listener.Listen(10);
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForConnections) );
}
public void CheckForConnections()
{
try
{
logger.Debug("listening successfully started! Waiting for incoming connections...");
listener.BeginAccept(new AsyncCallback(acceptCallback), listener);
}
catch (Exception ex)
{
logger.Error("Exception Occured while starting Listening : " + ex.Message.ToString());
}
}
private void acceptCallback(IAsyncResult ar)
{
try
{
Socket listener = (Socket)ar.AsyncState;
listener.BeginAccept(new AsyncCallback(acceptCallback), listener);
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
logger.Debug("listening socket accepted connection...");
}
catch (Exception ex)
{
logger.Error("Error on acceptCallback. Error: " + ex.Message);
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
}
ClientConnectionFactory.createConnection(ar.AsyncState);
}