TcpListener ставит соединения в очередь быстрее, чем я могу очистить их

Насколько я понимаю, TcpListener поставит соединения в очередь, после того как Вы звоните Start(). Каждый раз Вы звоните AcceptTcpClient (или BeginAcceptTcpClient), это исключит один объект из очереди от очереди.

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

Мы звоним BeginAcceptTcpListener и затем сразу передача к a ThreadPool распараллельте, чтобы на самом деле сделать работу, затем звоня BeginAcceptTcpClient снова. Включенная работа, кажется, не оказывает давления на машину, это - в основном просто 3-секундный сон, сопровождаемый поиском по словарю и затем 100 побайтовыми записями к TcpClientпоток.

Вот TcpListener код мы используем:

    // Thread signal.
    private static ManualResetEvent tcpClientConnected = new ManualResetEvent(false);

    public void DoBeginAcceptTcpClient(TcpListener listener)
    {
        // Set the event to nonsignaled state.
        tcpClientConnected.Reset();

        listener.BeginAcceptTcpClient(
            new AsyncCallback(DoAcceptTcpClientCallback),
            listener);

        // Wait for signal
        tcpClientConnected.WaitOne();
    }

    public void DoAcceptTcpClientCallback(IAsyncResult ar)
    {
        // Get the listener that handles the client request, and the TcpClient
        TcpListener listener = (TcpListener)ar.AsyncState;
        TcpClient client = listener.EndAcceptTcpClient(ar);

        if (inProduction)
            ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client, serverCertificate));  // With SSL
        else
            ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client));  // Without SSL

        // Signal the calling thread to continue.
        tcpClientConnected.Set();
    }

    public void Start()
    {
        currentHandledRequests = 0;
        tcpListener = new TcpListener(IPAddress.Any, 10000);
        try
        {
            tcpListener.Start();

            while (true)
                DoBeginAcceptTcpClient(tcpListener);
        }
        catch (SocketException)
        {
            // The TcpListener is shutting down, exit gracefully
            CheckBuffer();
            return;
        }
    }

Я предполагаю, что ответ будет связан с использованием Sockets вместо TcpListener, или по крайней мере использование TcpListener.AcceptSocket, но я задался вопросом, как мы пойдем о выполнении этого?

Одна идея, которую мы имели, состояла в том, чтобы звонить AcceptTcpClient и сразу Enqueue TcpClient в одного из нескольких Queue<TcpClient> объекты. Тем путем мы могли опросить те очереди на отдельных потоках (одна очередь на поток), не сталкиваясь с мониторами, которые могли бы заблокировать поток при ожидании другого Dequeue операции. Каждый поток очереди мог затем использовать ThreadPool.QueueUserWorkItem сделать работу в a ThreadPool распараллельте и затем перейдите на исключение из очереди следующего TcpClient в его очереди. Вы рекомендовали бы этот подход или являетесь нашей проблемой, которую мы используем TcpListener и никакой объем быстрого исключения из очереди не собирается зафиксировать это?

9
задан Matt Brindley 30 April 2010 в 14:55
поделиться

5 ответов

Я создал код, который использует сокеты напрямую, но у меня нет возможности провести нагрузочный тест с 1000 клиентами. Не могли бы вы попытаться проверить, как этот код сравнится с вашим текущим решением? Мне были бы очень интересны результаты, так как сейчас я создаю сервер, который также должен принимать большое количество соединений.

static WaitCallback handleTcpRequest = new WaitCallback(HandleTcpRequest);

static void Main()
{
    var e = new SocketAsyncEventArgs();
    e.Completed += new EventHandler<SocketAsyncEventArgs>(e_Completed);

    var socket = new Socket(
        AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Bind(new IPEndPoint(IPAddress.Loopback, 8181));
    socket.Listen((int)SocketOptionName.MaxConnections);
    socket.AcceptAsync(e);

    Console.WriteLine("--ready--");
    Console.ReadLine();
    socket.Close();
}

static void e_Completed(object sender, SocketAsyncEventArgs e)
{
    var socket = (Socket)sender;
    ThreadPool.QueueUserWorkItem(handleTcpRequest, e.AcceptSocket);
    e.AcceptSocket = null;
    socket.AcceptAsync(e);
}

static void HandleTcpRequest(object state)
{
    var socket = (Socket)state;
    Thread.Sleep(100); // do work
    socket.Close();
}
3
ответ дан 4 December 2019 в 21:48
поделиться

Первое, что нужно задать себе, это «1000 одновременных подключений разумны». Лично я думаю, что вы вряд ли попадете в такую ​​ситуацию. Скорее всего, у вас будет 1000 подключений за короткий период времени.

У меня есть тестовая программа TCP, которую я использую для тестирования моей серверной инфраструктуры, она может делать такие вещи, как X соединений в целом в пакетах по Y с промежутком в Z мс между каждым пакетом; который я лично считаю более реальным, чем «огромное количество сразу». Это бесплатно, это может помочь, вы можете получить его отсюда: http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html

Как говорили другие, увеличьте время ожидания прослушивания, быстрее обрабатывайте соединения, по возможности используйте асинхронное принятие ...

1
ответ дан 4 December 2019 в 21:48
поделиться

Если я чего-то не упускаю, вы вызываете BeingAcceptTcpClient, который является асинхронным, но затем вы вызываете WaitOne (), чтобы дождаться завершения асинхронного кода, что фактически делает процесс синхронным. Ваш код может одновременно принимать только одного клиента. Или я совсем сумасшедший? По крайней мере, это кажется бесполезным переключением контекста.

2
ответ дан 4 December 2019 в 21:48
поделиться

Просто предложение: почему бы не принимать клиентов синхронно (используя AcceptTcpClient вместо BeginAcceptTcpClient), а затем обрабатывать клиента в новом потоке? Таким образом, вам не придется ждать, пока клиент будет обработан, прежде чем вы сможете принять следующего.

1
ответ дан 4 December 2019 в 21:48
поделиться

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


    public void Start()
    {
        currentHandledRequests = 0;
        tcpListener = new TcpListener(IPAddress.Any, 10000);
        try
        {
            tcpListener.Start(1100);  // This is the backlog parameter

            while (true)
                DoBeginAcceptTcpClient(tcpListener);
        }
        catch (SocketException)
        {
            // The TcpListener is shutting down, exit gracefully
            CheckBuffer();
            return;
        }
    }

По сути, этот параметр устанавливает, сколько "ожидающих" TCP-соединений разрешено, которые ожидают вызова Accept. Если вы не принимаете соединения достаточно быстро, и эта очередь заполняется, TCP-соединения будут автоматически отклонены, и у вас даже не будет возможности их обработать.

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

2
ответ дан 4 December 2019 в 21:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: