TCP-сервер на С#, вызывающий 100% загрузку ЦП через 2-3 дня

Вот мой код для очень простого TCP-сервера (в основном пример примера сокета асинхронного сервера — http://goo.gl/Ix5C - немного изменено):

    public static void InitiateListener()
    {
        try
        {
            allDone = new ManualResetEvent(false);
            configFile = new XmlConfig();                
            StartListening();
        }
        catch (Exception exc)
        {
            LogsWriter f = new LogsWriter(configFile.ErrorLogsPath);
            f.WriteToFile(exc.Message);
            f.CloseFile();
        }

    }

    private static void StartListening()
    {      
        try
        {
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, configFile.Port);
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            listener.Bind(localEndPoint);
            listener.Listen(100);                

            while (true)
            {
                // Set the event to nonsignaled state.
                allDone.Reset();                    

                // Start an asynchronous socket to listen for connections.
                listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

                // Wait until a connection is made before continuing.
                allDone.WaitOne();
            }
        }
        catch (Exception exc)
        {
            throw exc;
        }
    }

    public static void AcceptCallback(IAsyncResult ar)
    {
        allDone.Set(); // Signal the main thread to continue.               

        // Get the socket that handles the client request.
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = handler;

        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
    }

    public static void ReadCallback(IAsyncResult ar)
    {
        string hexData = string.Empty;

        // Retrieve the state object and the handler socket from the asynchronous state object.
        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;

        try
        {
            // Read data from the client socket. 
            int bytesRead = handler.EndReceive(ar);                

            if (bytesRead > 0)
            {
                hexData = BitConverter.ToString(state.buffer);

                if (hexData.Contains("FA-F8")) //heartbeat - echo the data back to the client.
                {
                    byte[] byteData = state.buffer.Take(bytesRead).ToArray();
                    handler.Send(byteData);
                }
                else if (hexData.Contains("0D-0A")) //message
                {
                    state.AsciiData = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
                    state.ParseMessage(configFile);
                }
            }
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);                
        }
        catch (Exception)
        {
            handler.Shutdown(SocketShutdown.Both);
            handler.Close();
        }
    }

Это все в службе Windows. И процессор максимально загружается до 100% примерно через 2 с половиной дня работы вполне приемлемо. Это произошло уже 3 раза - служба Windows всегда работает нормально и функционирует так, как должна, почти не используя ресурсы ЦП, но где-то на 3-й день достигает 100% и остается там до тех пор, пока служба не будет перезапущена.

Я получаю очень простые CSV-пакеты, которые быстро анализирую и отправляю в базу данных через веб-службу следующим методом: state.ParseMessage(configFile); Даже когда ЦП загружен на 100%, база данных заполняется довольно надежно. Но я так понимаю, это может быть одно из мест, где мне нужно провести расследование?

Какие другие области кода могут быть причиной проблемы? Я новичок в асинхронном программировании, поэтому не знаю, нужно ли мне закрывать потоки вручную. Кроме того, это может быть еще одна проблема: handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
Вызов этой строки внутри самого ReadCallback. Я делаю это, чтобы поддерживать соединение с клиентом и продолжать получать данные, но, может быть, мне следует закрыть сокет и принудительно установить новое соединение?

Не могли бы вы предложить несколько предложений? Спасибо


person Sundance    schedule 04.06.2013    source источник
comment
Когда он достигает 100 %, сервис все еще работает с точки зрения прослушивания запросов? Интересно, умирает ли слушатель, из-за чего BeginAccept немедленно запускает ваш обратный вызов, а затем, когда вы вызываете EndAccept, вы получаете исключение.   -  person Jon Skeet    schedule 04.06.2013
comment
В мониторе ресурсов вашего компьютера вы можете отслеживать количество потоков, используемых приложением. Вы можете видеть, растет ли это число со временем, хотя я не думаю, что это может быть проблемой.   -  person C4stor    schedule 04.06.2013
comment
Привет, Джон, служба по-прежнему работает должным образом, когда она достигает 100%. Информация из новых пакетов заносится в базу, вроде все верно. Функциональность не кажется проблемой в любой момент.   -  person Sundance    schedule 04.06.2013
comment
C4stor, спасибо, обязательно посмотрю количество потоков в мониторе ресурсов. Так что, похоже, я не натворил никаких очевидных ляпов? Я надеялся, что они есть!   -  person Sundance    schedule 04.06.2013
comment
Я только что посмотрел, он застрял на 15 потоках, когда доходит до 100%. Хотя не уверен, что это значит...?   -  person Sundance    schedule 05.06.2013
comment
не отвечая прямо на вопрос, но почему вы не используете async/await?   -  person David Haim    schedule 27.03.2016
comment
@Sundance У меня точно такая же проблема на моем сервере, через несколько дней я начинаю сильно загружать процессор (~ 60%). Однако нормальная работа составляет 1-2%. После профилирования предполагает, что это функция System.net.Socket.beginReceive()   -  person Zapnologica    schedule 02.05.2017


Ответы (2)


Вместо цикла while(true) в методе startlistening нам нужно вызвать

listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

из метода AcceptCallback после завершения приема нового клиента.

person karthik koppewar    schedule 27.03.2016

Вам нужно обнаружить отключение через bytesRead == 0. Прямо сейчас вы этого не делаете и вместо этого выдаете еще одно чтение.

Похоже, вы скопировали образец кода MSDN. Я могу сказать это из-за бессмысленной комбинации асинхронного ввода-вывода и событий. Весь этот код становится проще, когда вы используете синхронный ввод-вывод.

person usr    schedule 27.03.2016