C# High CPU usage on Listener thread, sleeping misses disconnect

独自空忆成欢 提交于 2019-11-28 02:18:43

I had the same problema than you but I found that the best way to solve this problem is:

Not Blocking the Socket with sleeps and thread.

UPGRADE: If you use threads and sleeps into your server, it will suffer a low performance to receive and answer each message by each connection.

If you want an High Performance App you must not use sleeps or create thread for each connection that you accept. The best way is using the Asyncronous methods that NetworkStream provides, using BeginRead and EndRead, for example:

    public void run()
    {
        server = new TcpListener(IPAddress.Any, port);
        server.Start();

        log.Info("Starting SocketServer on Port [" + port + "]");

        while (keepRunning)
        {
            try
            {
                TcpClient socket = server.AcceptTcpClient();
                if (keepRunning)
                    RequestManager.createRequestForEvalue(socket, idLayout);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
                log.Error(ex.StackTrace);
            }
        }

        log.Info("Server Stoped.");
    }

    public static bool createRequestForEvalue(TcpClient socket, int idLayout)
    {
        Request req = null;
        req = new Request(socket,idLayout);

        registerRequest(req.ID,req); //Registra el Request, para su posterior uso.

        // DO NOT CREATE THREADS FOR ATTEND A NEW CONNECTION!!!
        //Task.Factory.StartNew(req.RunForIVR);
        //ThreadPool.QueueUserWorkItem(req.RunForIVR);

        req.startReceiveAsync(); //Recive data in asyncronus way.
        return true;
    }

    public void startReceiveAsync()
    {
        try
        {
            log.Info("[" + id + "] Starting to read the Request.");
            requestBuffer = new byte[BUFFER_SIZE];
            NetworkStream nst = socket.GetStream();
            nst.BeginRead(requestBuffer, 0,BUFFER_SIZE, this.requestReceived, nst);
        }catch(Exception ex)
        {
            log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
            RequestManager.removeRequest(id);
            closeSocket();
        }
    }

    public void requestReceived(IAsyncResult ar)
    {

        try
        {   
        NetworkStream nst = socket.GetStream();
        int bread = nst.EndRead(ar); //Block the socket until all the buffer has been available.
        message = Encoding.UTF8.GetString(requestBuffer, 0, BUFFER_SIZE);
            log.Info("[" + id + "] Request recived: [" + message +"]");
            RunForIVR();
        }
        catch (Exception ex)
        {
            log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
            RequestManager.removeRequest(id);
            closeSocket();
        }

    }

    public void SendResponse(String Response)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(Response);
        sb.Append('\0', BUFFER_SIZE - Response.Length);
        string message = sb.ToString();

        log.Info("[" + id + "] ivrTrans CMD: [" + idCMD + "] RESPONSE: [" + Response + "]");

        NetworkStream nst = socket.GetStream();
        byte[] buffer = new byte[BUFFER_SIZE];
        for (int i = 0; i < BUFFER_SIZE; i++)
            buffer[i] = (byte)message.ElementAt(i);

        nst.BeginWrite(buffer, 0, BUFFER_SIZE, this.closeSocket, nst);
    }

    public void closeSocket(IAsyncResult ar = null)
    {

        try
        {
            if (ar != null) //Since 4.24
            {
                NetworkStream nst = socket.GetStream();
                nst.EndWrite(ar);
            }

            socket.Close();
            socket = null;
        }catch(Exception ex)
        {
            log.Warn("[" + id + "] There was a problem to close the socket. Error: " + ex.Message + Environment.NewLine + ex.StackTrace);
        }
        log.Info("[" + id + "] Socket closed.");
    }

Upgrade I use the EndRead to be sure that the request has been arrived at all.

By Other way, you can use BeginWrite and EndWrite to know when the socket has been finished of write to close the connection

In this way you are attended the connection in a continues way and as soon as possible. In my case i reduce the CPU usage from 30% to 0%, for an a mount of 15K request per hour.

The proper way to communicate over a socket is:

  1. Continuously read. These reads will block until data comes in or until the socket is gracefully disconnected (detectable by a read completing with 0 bytes read).
  2. Periodically write. These writes are required to ensure the connection is still viable.

A proper threading approach requires two threads per connection. I'm not convinced that it's simpler than an asynchronous approach.

P.S. If your code uses Connected, then it has a bug. Proper solutions never need to use Connected.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!