0

I have written code for a server which accepts connections from different clients. Each client is serviced in different threads. Each thread accesses a database to get data and then updates this data to all the clients connected to server.

1) For the first time when UI asks data from server, it responds properly, but after that server does not read the socket i.e. Server's readyread() doesn't get invoked. Funnily enough, this works fine in mac and linux, this issue is seen only on windows

2) I was able to verify that when the DB module emits a signal which is caught by the threads, the hang occurs, Because everything worked fine when I removed the emit.

Here, I am attaching all the needed .h and .cpp codes

Defn.h

#ifndef DEFN_H
#define DEFN_H

struct PresetData{
    QString ID;
    QString name;
    QString value;
    QString source;
}; 

#endif // DEFN_H

main.cpp

#include <QCoreApplication>
#include "myserver.h"
#include "mydb.h"

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

    MyDB db;
    MyServer server(&db);
    server.startServer();

    return a.exec();
}

mydb.h

#ifndef MYDB_H
#define MYDB_H

#include <QObject>
#include <QtSql>

#include "Defn.h"

class MyDB : public QObject
{
    Q_OBJECT
  public:
    explicit MyDB(QObject *parent = 0);

  signals:
    void dataAvailable(QString ID, QString name, QString value, QString source);

  public slots:
    void onUpdateData(QString ID, QString name, QString value, QString source);
    void onGetData(QString ID, QString name, QString value, QString source);

  private:
    QSqlDatabase m_db;
};

#endif // MYDB_H

mydb.cpp

#include "mydb.h"

MyDB::MyDB(QObject *parent) :
QObject(parent)
{
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setConnectOptions();
    m_db.setDatabaseName("D:/MySimulator/New Folder/TCPServer1/DB.db");

    if (m_db.open()){
       qDebug() << "DB opened succesfully" ;
    }else{
       qDebug() << "DB Opening failed" ;
    }

    QStringList tables = m_db.tables();
    if (tables.contains("Presets", Qt::CaseInsensitive)){
         qDebug() << "DB Contains Data" ;
         return;
    }
}

void MyDB::onGetData(QString ID, QString name, QString value, QString source)
{
    qDebug() << "onGetData" ;
    QString queryString = "SELECT Value from 'Presets' where ID = \'" + ID + "\'";
    QSqlQuery q;

    bool result = q.exec(queryString);

    if (result){
       if (q.next()){
         value = q.value(q.record().indexOf("Value")).toString();
         qDebug() << " Retrieved Value = " << value ;
         emit dataAvailable(ID, name, value, source);
       }else{
         qDebug("Empty Result");
       }
   }else{
      qDebug("NO Result");
   }
}

void MyDB::onUpdateData(QString ID, QString name, QString value, QString source)
{
    qDebug() << "onUpdateData" ;
    QString queryString = "UPDATE 'Presets' SET Value = \'" + value + "'\ WHERE ID = \'" + ID + "\'";
    QSqlQuery q;

    QSqlDatabase::database().transaction();
    bool result = q.exec(queryString);

    if (result){
       QSqlDatabase::database().commit();
       onGetData(ID, name, "", "000");
    }else{
        qDebug("NO Result");
    }
}

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
#include "Defn.h"
#include "mydb.h"

class MyThread : public QThread
{
     Q_OBJECT
   public:
     explicit MyThread(int ID, MyDB* db, QObject * parent = 0);

     void run();
     void parseInput(QString string);

   signals:
     void error(QTcpSocket::SocketError socketError);
     void updateData(QString ID, QString name, QString value, QString source);
     void getData(QString ID, QString name, QString value, QString source);

   public slots:
     void readyRead();
     void disconnected();
     void onDataAvailable(QString ID, QString name, QString value, QString source);

   private:
     QTcpSocket* socket;
     int socketDescriptor;
     MyDB* db;
};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include "qtcpserver.h"
#include "qabstractsocket.h"

MyThread::MyThread(int ID, MyDB* db, QObject * parent ):
   QThread(parent)
{
    this->socketDescriptor = ID ;
    this->db = db;
}

void MyThread::run()
{
    // thread starts here.
    qDebug() << socketDescriptor << "Starting Thread" ;
    socket = new QTcpSocket();
    if (!socket->setSocketDescriptor(this->socketDescriptor)){
        emit error(socket->error());
        return;
    }

    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
    connect(this, SIGNAL(getData(QString, QString , QString , QString )), this->db, SLOT(onGetData(QString , QString , QString , QString )));
    connect(this, SIGNAL(updateData(QString , QString , QString , QString )), this->db, SLOT(onUpdateData(QString , QString , QString , QString )));
    connect(this->db, SIGNAL(dataAvailable(QString , QString , QString , QString )), this, SLOT(onDataAvailable(QString , QString , QString , QString )));

    qDebug() << socketDescriptor << "Client Connected" ;

    exec();
}

void MyThread::readyRead()
{
    QByteArray data = socket->readAll();
    qDebug() << socketDescriptor << "Data in: " << data;
    parseInput(data);
}

void MyThread::disconnected()
{
    qDebug() << socketDescriptor << "Disconnected" ;
    socket->deleteLater();
    exit(0);
}

void MyThread::parseInput(QString dataFromTCP)
{
    qDebug() << socketDescriptor << ":" <<"parseInput  "  << dataFromTCP;

    if (dataFromTCP.isEmpty())
       return;

    QStringList list1 = dataFromTCP.split("\n", QString::SkipEmptyParts);

    qDebug() << socketDescriptor << ":" << "list1 BEGIN";
    for (int i = 0 ; i < list1.count(); i++)
    {
        qDebug() << i<< ":" << list1.at(i);
    }
    qDebug() << socketDescriptor << ":" << "list1 END";

    if (list1.count() < 1){
         return;
    }

    QString strMessage = "";
    for (int i = 0 ; i < list1.count() ; i++)
    {
        strMessage = list1[i];
        QStringList list2 = strMessage.split(" ", QString::SkipEmptyParts);

        qDebug() << socketDescriptor << ":" << "list2 BEGIN";
        for (int i = 0 ; i < list2.count(); i++)
        {
            qDebug() << i<< ":" << list2.at(i);
        } 
        qDebug() << socketDescriptor << ":" << "list2 END";

        if (list2.count() < 1){
            break;
        }

        QString ID = list2[1];
        QString source = QString::number(socketDescriptor) ;
        if (list2[0] == "GET"){
           emit getData(ID, "", "", source);
        } 
        else if (list2[0] == "UPD"){
            QString value = list2[2];
            emit updateData(ID, "", value, source);
        }
    }
}

void MyThread::onDataAvailable(QString ID, QString name, QString value, QString source)
{
    if( (QString::number(socketDescriptor) == source) || ("000" == source ) ) {
       qDebug() << socketDescriptor << " : On Data Available " << ID << name << value ;
       QString data = "DATA " + ID + " " + value + " " + "\n" ;
       QByteArray ba;
       ba.append(data);
       socket->write(ba);
    }
}

myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QDebug>
#include <QObject>
#include <QTCPServer>
#include <QTCPSocket>

#include "mythread.h"
#include "mydb.h"

class MyServer: public QTcpServer
{
      Q_OBJECT
   public:
      explicit MyServer(MyDB* pdb, QObject* parent = 0);
      void startServer();

   signals:

   public slots:

   protected:
      void incomingConnection(qintptr socketDescriptor);

   private:
      MyDB* pdb ;
};

#endif // MYSERVER_H

myserver.cpp

#include "myserver.h"

MyServer::MyServer(MyDB* pdb, QObject* parent ):
   QTcpServer(parent)
{
    this->pdb = pdb;
}

void MyServer::startServer()
{
    if (!this->listen(QHostAddress::Any, 1234)){
        qDebug() << "Could not Start Server " << this->errorString();
    }
    else{
        qDebug() << " Server Running... ";
    }
}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << socketDescriptor << " Connecting... ";
    MyThread *thread = new MyThread(socketDescriptor, pdb, this);

    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    thread->start();
}

Here the signal about which I mentioned above is dataAvailable from "mydb.cpp". If I comment out that line then server responds to client messages. But if that signal is emitted then after the initial response, the server seems to hang and no longer reacts to incoming messages from the client. The same code is working perfectly fine in mac and linux. But it is having this problem in Windows only. Could someone let me know what is it that I am doing wrong that it is failing only in Windows? Thanks in advance for helping me out.

EDIT:

The objective of this code is that whenever a thread causes an update call to the database, EVERY thread including the one that called the update gets informed about the change. So it is EXPECTED that other thread that runs at that time also receives a signal.

This is what is expected of the server:

  1. Be able to allow TCP connections from multiple clients simultaneously.

  2. If any client requests info, it gets the required data over the TCP connection.

  3. If any client updates info, all clients including the updating client, gets a notifications over the TCP connection.

AmarSneh
  • 103
  • 1
  • 6

1 Answers1

0

Well, for starters, your code is completely not thread-safe. You create a single instance of MyDB in your main() function, then call it from threads without protecting its data member. Also, signals get emitted, updating data without any protection. What if two threads happen to be running at the same time?

Secondly, and this is more important: whenever you emit dataAvailable() you call functions in other thread objects in your own thread. This is the code path when data arrives:

  1. MyThread::parseInput() emits
  2. MyThread::getData(), which is connected to
  3. MyDB::onGetData(), which emits
  4. MyDb::dataAvailable, which is connected to (drumroll....)
  5. MyThread::onDataAvailable, which eventually calls
  6. socket->write()

So if data arrives in thread #1, you're going to send data from MyThread object #2, #3, #4, etc from .... thread #1. Depending on the OS, this is bad news. I don't know enough about Windows threads but I do know this code is terminally broken.

If all you want to do is update a database and relay the data you can dispense with the threads and use a sequential program that handles sockets using the regular Qt signals and slots just fine.

JvO
  • 3,036
  • 2
  • 17
  • 32