0
votes

Basically I want to achive a simple and basic design. I have a client socket which sends and reads settings to a socket server. To avoid blocking in the main thread, the whole socket handling should be done in a diffrent thread. So I created the class MySocket:

MySocket.h (only important parts)

class MySocket : public QThread {
Q_OBJECT

public:

    int port;
    QHostAddress address;
    bool running;

    MySocket(QHostAddress addr, int port);

public slots:
    void sendMessage(QByteArray data);

protected:
    QTcpSocket* socket;    
    virtual void run();

signals:
    void onDataReady(const QByteArray &data);

private slots:
    void onReadyRead();
    void newConnection();
    void disconnected();
};

MySocket.cpp (only important parts)

#include "MySocket.h"

MySocket::MySocket(QHostAddress addr, int port) : port(port), address(addr), running(true)
{
}

void MySocket::onReadyRead()
{
    QByteArray datas = socket->readAll();
    //Send data to mainThread or somewhere else
    emit onDataReady(datas);
}

void MySocket::run()
{
    socket = new QTcpSocket();

    QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    QObject::connect(socket,SIGNAL(connected()),this,SLOT(newConnection()));
    QObject::connect(socket,SIGNAL(disconnected()),this,SLOT(disconnected()));
    socket->connectToHost(address, port);
    while(running)
    {
        QApplication::processEvents();
        QThread::msleep(10);
    }

}

void MySocket::sendMessage(QByteArray data){
    qDebug()<< "writing Data ...";
  
    if(socket->state() == QAbstractSocket::ConnectedState)
    {
       std::string message(data.constData(), data.length());
       qDebug() << "SendingData: " <<  QString::fromStdString(message);
       socket->write(data);
    }
    else{
        qDebug() << "Socket not ready for writing";
    }
}

The main thread (mainwindow.cpp) just creates and starts the new thread.

mySocket = new MySocket(QHostAddress("127.0.0.1"), 3333);
controlSocket->start();

It also connects the slot and signal for writing.

connect(this, SIGNAL(write(QByteArray)), mySocket, SLOT(sendMessage(QByteArray)));

Whenever I emit the signal using emit(write("some message")); I receive the following error/notifiaciton:

QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

So first of all, how can I solve this problem? I have created a new thread and I am also using slots/signals as mentioned in many other topics facing similar problems. Is the socket still sending those messages anyway?

1
- In Qt, it is not needed to put the sockets in a thread as everything is managed in a non-blocking way. - However, if you still want to do this to balance the load on a different CPU, it is possible. In that case, I would read the data in the thread and emit a signal when the data are read, so that everything related to the socket is inside the thread. In your connection, "this" is in the main thread ! - I think you should use a QEventLoop instead of QApplication::processEvent in the thread.Alexandre
@Alexandre: I receive the warning whenever I want to send data (emit(write()) --> MySocket::sendMessage()). Is "this" in this context really wrong? The socket is actually connecting and MySocket::newConnection is called.FredFloete

1 Answers

1
votes

The object of class MySocket is a QThread, but the instance of this object itself lives in the main thread. Only objects created in the run() function live in the other thread. So you should avoid accessing "socket" from the main thread (i.e. from the MySocket class).

2 solutions:

  • The easiest is to not use threads.
  • With threads, my advice is to create a wrapper class which encapsulate the socket (which would look very much like MySocket), with signals for dataReceived(QByteArray) and slots for sendData(QByteArray). Then, you create another class extending QThread, also with the same signal and slot and in the run() you create the wrapping class locally and connect its signals and slots to those of the class extending QThread, and then run an event loop forever (QEventLoop::exec()). Or, you can use a standard QThread class (no need to extend) and the method moveToThread() on your wrapper class. This avoids the need to create a class extending QThread.