Увеличить максимальное отставание в Windows CE, изменив SOMAXCONN?

Мы разрабатываем приложение .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);
 }

person Chris    schedule 13.04.2012    source источник


Ответы (1)


Я думаю, вы идете по неправильному пути - вы определенно можете справиться с этим сценарием, не меняя номер невыполненного запроса.

Клиентские запросы поступают на порт 80, но ответы не возвращаются клиенту на порт 80. Это означает, что вы можете использовать асинхронную обработку сокетов для получения запроса, а затем передать его на синтаксический анализ и ответить таким образом. последующие запросы не ждут полной обработки предыдущих запросов. Мы используем эту технику на нашем веб-сервере Padarn, и у нас нет проблем с обработкой нескольких запросов от одного клиентского браузера или даже нескольких запросов от нескольких одновременных клиентов.

person ctacke    schedule 13.04.2012
comment
Привет, Крис, спасибо за быстрый ответ, я обновил свой пост, чтобы дать больше информации и показать / подтвердить наше понимание этого конкретного вопроса! Насколько я понимаю, мы уже используем асинхронную обработку сокетов. - person Chris; 14.04.2012
comment
Мне нужно будет пойти и посмотреть на исходный код Padarn, чтобы увидеть, как именно выглядит шаблон, который мы используем, но я точно знаю, что мы не корректируем отставание и что у нас нет проблем с потерянными запросами. , даже от нескольких клиентов и когда на странице используется много включений, требующих множества запросов к серверу для визуализации страницы. Я посмотрю, смогу ли я добраться до него в ближайшее время, но, скорее всего, это будет в понедельник. - person ctacke; 14.04.2012
comment
Пожалуйста, сначала ответьте на вопрос, затем вы сможете дать совет, только совет хорош только в качестве комментария. - person Krzysztof Cichocki; 07.07.2017