1
\$\begingroup\$

I'm creating a Qt TCP (with SSL) server to which multiple clients should connect and use the DB, and this is the main task. So, I think I can use this architecture: each client has its own thread, and clients use DB via DB connection pool.

Client connection part

main.cpp:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyServer server;

    server.listen(QHostAddress::Any, 1234);

    return a.exec();
}

myserver.h:

class MyServer : public QTcpServer
{
    Q_OBJECT

    explicit MyServer(QObject *parent = 0);
    ~MyServer();

protected:
    void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;

signals:
    void stopAll();
};

myserver.cpp:

MyServer::MyServer(QObject *parent) : QTcpServer(parent)
{
    //...
}

MyServer::~MyServer(){
    emit stopAll();
}

void MyServer::incomingConnection(qintptr socketDescriptor){
    QThread* clientThread = new QThread;
    MyClient *client = new MyClient(socketDescriptor, this);

    client->moveToThread(clientThread);

    connect(clientThread, SIGNAL(started()), client, SLOT(process()));

    connect(client, SIGNAL(finished()), clientThread, SLOT(quit()));
    connect(this, SIGNAL(stopAll()), client, SLOT(stopFromServer()));
    connect(client, SIGNAL(finished()), client, SLOT(deleteLater()));
    connect(clientThread, SIGNAL(finished()), clientThread, SLOT(deleteLater()));

    clientThread->start();
}

myclient.h:

class MyClient : public QObject
{
    Q_OBJECT
public:
    explicit MyClient(int socketDescriptor, MyServer *server);
    ~MyClient();

private:
    QSslSocket* socket = NULL;

public slots:
    void process();
}

myclient.cpp:

void MyClient::process(){
    //typical connection things
    connect(this->socket, SIGNAL(encrypted()), this, SLOT(ready()));
}

void MyClient::ready(){
    connect(socket,SIGNAL(readyRead()),this, SLOT(newData()));
}

DB connection pool part

DAO which will be executed in the client threads:

.cpp:

//DAO method

QList<QMap<QString, QString>> sqlResult = DatabaseService::executeQuery(sqlCommand);

//sqlResult processing

DatabaseService:

.h:

class DatabaseService
{
public:
    //...
    static QMap<QString, QString> executeQuery(QString command);

private:
    static QThreadStorage<QSqlDatabase> mDatabasePool;
    static QSqlDatabase getDatabase();

};

.cpp:

//...
QThreadStorage<QSqlDatabase> DatabaseService::mDatabasePool;

QSqlDatabase DatabaseService::getDatabase()
{
    if(DatabaseService::mDatabasePool.hasLocalData()) {
        return DatabaseService::mDatabasePool.localData();
    } else {
        auto database = QSqlDatabase::addDatabase("QPSQL", QUuid::createUuid().toString());
        database.setHostName("hostName");
        database.setDatabaseName("databaseName");
        database.setUserName("user");
        database.setPassword("password");
        database.open();
        DatabaseService::mDatabasePool.setLocalData(database);
        return database;
    }
}

QMap<QString,QString> DatabaseService::executeQuery(QString command){

    QSqlQuery query (DatabaseService::getDatabase());
    query.exec(command);

    //...

    return result;
}

//...

This is all the code related to the subject.

You also can check my SO question about DB connection pool (please note that the code is not exactly the same).

So, how good is this architecture? What could be fixed and improved and how?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I am not sure how QThreads work:

  • Is it a wrapper around an OS thread?
  • Are QThreads pooled?

Assuming a QThread is a wrapper and not pooled.

Then this technique is actually very limiting. The trouble is that threads are relatively expensive to create, in reality you can only run a handful of them simultaneously without cost (creating more than 2x the processors is probably a bad idea), In socket programming most of the threads are idle (network is very slow compared to the speed of the processor).

There is a famous paper on the top c10k. Basically given current network speeds and processor speeds you should easily be able to support 10 thousand simultaneous connections. But using this model you will top out supporting a couple of hundred simultaneous connections.

Basically you want to design your server around a single thread model and use pselect() (though this is now outdated by better options poll(), epoll() and finally LibEvent). Don't bother experimenting with the older versions just jump to LibEvent.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for your answer. And can I use standart Qt signals and slots for these purposes? \$\endgroup\$ Commented Nov 26, 2017 at 7:13
  • \$\begingroup\$ @don-prog I am not an expert on QT. But I found this on github. A QT event dispatcher qt_eventdispatcher_libevent \$\endgroup\$ Commented Nov 26, 2017 at 7:30
  • \$\begingroup\$ [1/2] I found this wrapper too, but I still don't understand what is the difference between LibEvent and signals and slots in Qt, maybe it will be better to use native Qt solution? Is these things for one or for different purposes? What will be better to use to manage a large number of network connections and how? Can I just use signals and slots? \$\endgroup\$ Commented Nov 28, 2017 at 1:31
  • \$\begingroup\$ [2/2] I asked question on SO about this, so, you can check it. I also find this article which describe signals and slots in Qt, maybe this will clarify what is the difference between these subjects for you, because I and others on SO can't yet answer this question. \$\endgroup\$ Commented Nov 28, 2017 at 1:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.