2

i've try to make a simple tcp server with poco. I've use the Poco::Net::TCPServer class, which is a multithreaded server, and technically it works. But one thing is super strange. If i start the server, get one or more incoming connection(s), i got 100% cpu usage after 3 seconds, but i can't figure out why.

Here is my simple code.

#include <iostream>
#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerParams.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/Socket.h"

#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"

#include <string>
using namespace std;

class newConnection: public Poco::Net::TCPServerConnection {
public:
    newConnection(const Poco::Net::StreamSocket& s) :
        Poco::Net::TCPServerConnection(s) {
    }

    void run() {
        cout << "New connection from: " << socket().peerAddress().host().toString() <<  endl << flush;
        const auto ct = Poco::Thread::current();
        cout << "thread-id: " << ct->id() << endl;
        bool isOpen = true;
        Poco::Timespan timeOut(10,0);
        unsigned char incommingBuffer[1000];
        while(isOpen)
        {
            if (socket().poll(timeOut,Poco::Net::Socket::SELECT_READ) == false)
            {
                //cout << "TIMEOUT!" << endl << flush;
            }
            else
            {
                //cout << "RX EVENT!!! ---> "   << endl << flush;
                int nBytes = -1;

                std::vector<char> bytes;
                try
                {
                    do// recive all bytes, if the buffer is to small for the whole data
                    {
                        nBytes = socket().receiveBytes(incommingBuffer, sizeof(incommingBuffer));
                        for(int i = 0; i < nBytes; i++)
                        {
                            bytes.push_back(incommingBuffer[i]);
                        }
                    }while(socket().available() > 0);
                }
                catch (Poco::Exception& exc)
                {
                    //Handle your network errors.
                    cerr << "Network error: " << exc.displayText() << endl;
                    isOpen = false;
                }

                if (nBytes==0)
                {
                    cout << "Client closes connection!" << endl << flush;
                    isOpen = false;
                }
                else
                {
                    bytes.push_back('\0');
                    std::string line(&bytes[0]);
                    cout << line << endl;
                    const auto answer = std::string("you send me: ") + line;
                    socket().sendBytes(answer.c_str(),answer.size());
                }
            }
        }
        cout << "Connection finished!" << endl << flush;
    }
};



using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;

class MyServer: public Poco::Util::ServerApplication
{
public:
    MyServer(): _helpRequested(false)
    {
    }

    ~MyServer()
    {
    }

protected:
    void initialize(Application& self)
    {
        loadConfiguration(); // load default configuration files, if present
        ServerApplication::initialize(self);
    }

    void uninitialize()
    {
        ServerApplication::uninitialize();
    }

    void defineOptions(OptionSet& options)
    {
        ServerApplication::defineOptions(options);

        options.addOption(
            Option("help", "h", "display help information on command line arguments")
                .required(false)
                .repeatable(false));
    }

    void handleOption(const std::string& name, const std::string& value)
    {
        ServerApplication::handleOption(name, value);

        if (name == "help")
            _helpRequested = true;
    }

    void displayHelp()
    {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.setHeader("An echo server implemented using the Reactor and Acceptor patterns.");
        helpFormatter.format(std::cout);
    }

    int main(const std::vector<std::string>& args)
    {
        if (_helpRequested)
        {
            displayHelp();
        }
        else
        {
            // get parameters from configuration file
            const auto port = (unsigned short) config().getInt("MyServer.port", 1234);

            //const int port = 1234;
            Poco::Net::ServerSocket svs(port);
            //Configure some server params.
            Poco::Net::TCPServerParams* pParams = new Poco::Net::TCPServerParams();
            pParams->setMaxThreads(4);
            pParams->setMaxQueued(4);
            pParams->setThreadIdleTime(100);

            //Create your server
            Poco::Net::TCPServer myServer(new Poco::Net::TCPServerConnectionFactoryImpl<newConnection>(), svs, pParams);
            cout << "start server on " << svs.address().host().toString() << ":" << svs.address().port() << endl;
            myServer.start();

            waitForTerminationRequest();
        }
        return Application::EXIT_OK;
    }

private:
    bool _helpRequested;
};

int main(int argc, char** argv)
{
    MyServer app;
    return app.run(argc, argv);
}

Client code in python 3

import socket

TCP_IP = '127.0.0.1'
TCP_PORT = 1234
BUFFER_SIZE = 1024
message = "Hello, World!"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
try:
    message = input()
    while message != "quit":
        s.send(message.encode('utf-8'))
        #data = s.recv(BUFFER_SIZE)
        #print(data.decode('utf-8'))
        message = input()

except BaseException:
    pass

s.close()

Does anybody know why that could happen?

I've using MacOS X and poco 1.7.5

Greetings Tonka

Michael Aigner
  • 4,820
  • 4
  • 21
  • 33
  • 1
    Where you read recomendation to write such peace of code `while(1);`? This will use cpu 100% with do not any useful work. Remove it and try again. – oklas Mar 11 '17 at 20:46
  • I was a sample code from stackoverflow. Oh, that's right. I've tried to solve the problem in the run and totally forget about the main function. Now i have to find out what's that correct way to wait for the application end. – Michael Aigner Mar 11 '17 at 20:50
  • I've fix my first 100% usage thanks to the event loop from an poco application. But know i have the problem in the run method of my thread, i think it is also the while loop, but i have no plan how to do it correct. – Michael Aigner Mar 11 '17 at 21:31
  • For quick look `run()` method of class `newConnection` is fine. (Generally class names begin from apper case letter i.e. `NewConnection`). Connection handler funtional generally receive data and send answer and do same again if connection is keep alive. You need to check client side. Try to debug and write logs, read docs about poco. May be you need to ask another question. – oklas Mar 11 '17 at 22:10
  • hey. my client is a simple python script. it seems to happend when it terminate the connection from python code. – Michael Aigner Mar 11 '17 at 22:29
  • it seems that when there is no connection left 100% cpu usage will rise – Michael Aigner Mar 11 '17 at 22:48
  • 1
    it is definitely the server. The telnet client from MacOS create the same problem if i quit the last connection. – Michael Aigner Mar 11 '17 at 22:57
  • Try to write logs. If you have count of "New connection from..." messages same number of "Connection finished..." this mean your `run()` method works fine. That mean main event loop improperly configured or it is normal working of its pooling process for case where no connection available. Else if count of messages is not equal then debug `run()` method togather with client that must close connection at the end of communication. – oklas Mar 12 '17 at 09:24

1 Answers1

2

I know it's an old question, anyway I've found that the issue is related to the following setting: pParams->setThreadIdleTime(100);

As a note, this is my guess on what happens:

  1. setThreadIdleTime wants a Poco::Timespan as a parameter
  2. Poco::Timespan constructor with single parameter exists and wants the number of microseconds ("converting constructor")
  3. TcpServerDispatcher uses totalMilliseconds as an idle time
 void TCPServerDispatcher::run() {
     ...
     int idleTime = (int) _pParams->getThreadIdleTime().totalMilliseconds(); `
Simone-Cu
  • 1,109
  • 8
  • 21