Socket.SendAsync занимает несколько секунд, чтобы завершить - PullRequest
2 голосов
/ 01 октября 2010

Я пытаюсь оптимизировать оболочку TCP-сокета, которая борется с большим количеством входящих соединений.Я тестирую его на обычном чат-сервере и в небольшом клиентском приложении, чтобы отправлять на него клиентов.Оба приложения находятся на отдельном сервере W2k3, подключенном гигабитным коммутатором.

Методом проб и ошибок я уточнил тестирование до 10 клиентов, подключающихся с интервалами 100 мс, затем, после того как все 10 подключены, они отправляют по одному "enterномер "сообщение на сервер, снова с интервалом 100 мс.Когда сервер получает сообщение, он отвечает отправителю со списком всех в комнате, а также отправляет сообщение всем остальным в комнате, чтобы сообщить о новом прибытии.

Каждая отправка принимает 1второй для завершения (это длится 3-4 секунды для 100+ клиентов), и с помощью регистрации я установил, что задержка находится между Socket.SendAync и соответствующим событием, которое вызывается.Использование процессора остается низким на всем протяжении.

Я перепробовал все, что мог придумать, и потратил дни на поиски подсказок в Интернете, и я в полном недоумении.Это не может быть нормальным, не так ли?

Редактировать: Код по запросу.Я немного прибрался, удалил ненужные счетчики, ведение журналов и т. Д., В настоящее время он имеет хак поверх kludge и хакерских, поскольку я пытаюсь сузить проблему.

    private void DoSend(AsyncUserToken token, String msg)
    {
        SocketAsyncEventArgs writeEventArgs = new SocketAsyncEventArgs();
        writeEventArgs.Completed += ProcessSend;
        writeEventArgs.UserToken = token;
        Byte[] sendBuffer = Encoding.UTF8.GetBytes(msg + LineTerminator);
        writeEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);

        Interlocked.Add(ref m_totalBytesAttemptedSend, sendBuffer.Length);
        Logger2.log(Logger2.Debug5, token.ConnectionId, "Total Bytes attempted: " + m_totalBytesAttemptedSend);

        bool willRaiseEvent = true;
        try
        {
            willRaiseEvent = token.Socket.SendAsync(writeEventArgs);
        }
        catch (Exception e)
        {
            Logger2.log(Logger2.Debug2, token.ConnectionId, e.Message);
            writeEventArgs.Dispose();
        }
        if (!willRaiseEvent)
        {
            ProcessSend(null, writeEventArgs);
        }
    }


    private void ProcessSend(Object sender, SocketAsyncEventArgs e)
    {
        AsyncUserToken token = (AsyncUserToken)e.UserToken;
        Logger2.log(Logger2.Debug5, token.ConnectionId, "Send Complete");
        if (e.SocketError == SocketError.Success)
        {
            Interlocked.Add(ref m_totalBytesSent, e.BytesTransferred);
            Logger2.log(Logger2.Debug5, ((AsyncUserToken)e.UserToken).ConnectionId, "Total Bytes sent: " + m_totalBytesSent);

        }
        else
        {
            if (token.Connected)
            {
                CloseClientSocket(token);
            }
        }
        e.Dispose();
    }

Ответы [ 2 ]

1 голос
/ 17 марта 2012

Попробуйте использовать socket.NoDelay=True;.По умолчанию No означает, что базовый стек пытается накапливать передачи до фактической отправки.

1 голос
/ 02 октября 2010

Сколько данных вы отправляете по каждому соединению и как быстро вы его отправляете?

Обычно причина того, что асинхронная отправка занимает много времени, состоит в том, что вы заполнили окно TCP (см. здесь для подробной информации) и локальный стек TCP не может отправлять больше данных, пока не получит ACK от однорангового узла.Если вы продолжаете отправлять данные, вы просто ставите их в локальную очередь в сетевой подсистеме, поскольку стек не может их отправлять.Это может быть усугублено перегрузкой и потерей пакетов, поскольку транзитным данным требуется больше времени, чтобы добраться до однорангового узла, а ACK требуется больше времени для возврата ...

Если это так (и такой инструмент, какWireShark должен позволить вам видеть обновления размера окна, ситуации с нулевым окном и т. Д.), Тогда может помочь увеличение размера окна соединения (см. здесь ).

Если ваш протокол не имеетявное управление потоком, тогда вышеописанная ситуация еще более вероятна.Чтобы избежать этого, лучше включить некоторую форму явного управления потоком, IMHO, в свой дизайн протокола.

Также целесообразно включить некоторую защиту от отправляющей стороны, поскольку неограниченный отправитель может очень быстро прожевать память.Один подход, который я использовал, который работает хорошо, - это иметь очередь, в которую могут быть помещены исходящие данные, и данные из этой очереди фактически отправляются на основе завершений отправки более ранних отправлений (см. здесь ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...