Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QTcpSocket in QThread does not emit signals



  • Hi,

    I am working on a Kahoot like app (Just some question / answers, nothing really out of the ordinary) and at first glances, it seems to be working fine. With two differents projects, the server and the client(s), I use the QTcpSocket class as well as the QTcpServer and QThread in my server-side code.
    From what I have read (even though I am not completely sure), to make multiples clients connect to the server, I needed to add QThread to my project and then put inside my previous code about the QTcpSocket.
    At first my code resemble something llike this :

    • Mainwindow, with labels about the questions, the time remaining etc... ; container of MyServer (subclass of QTcpServer)
    • MyServer, a QTcpServer with QVector<QTcpSocket> clients

    At best, I could test this with only another computer, but now I have access to 3 different Windows 10 computer to test my code.
    It was when I could test it with more than one client that I saw something I can't solve now : I am not able to connect more than one client to my server.
    After some Googling, I found out that I needed to make differents thread to connect multiple clients to one server, so I did.
    Now my code is more something like this :

    • Mainwindow, with MyServer server
    • MyServer with QVector <MyThread*> clients
    • MyThread (subclass of QThread), with QTcpSocket * socket

    Here is my code below :

    • mainwindow.h
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    #include <QMainWindow>
    #include "myserver.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    public slots:
        void slotDataReceivedFromMyServer(const QByteArray Data, MyThread * source);
        // More stuff
    private:
        Ui::MainWindow * ui;
        MyServer * server;
       // More stuff
    };
    #endif // MAINWINDOW_H
    
    • mainwindow.cpp
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        this->setWindowTitle("Random Titre");
    
        server = new MyServer;
        connect(server, SIGNAL(emitDataReceivedFromMyServer(QByteArray,MyThread *)),this, SLOT(slotDataReceivedFromMyServer(QByteArray, MyThread *)));
        server->StartServer();
        qDebug() << "MainWindow : Done";
    
    }
    void MainWindow::slotDataReceivedFromMyServer(const QByteArray Data, MyThread * source)
    {
        // Here is where I get information like which answer has the client selected (A,B,C,D)
    }
    
    • MyServer.h
    #ifndef MYSERVER_H
    #define MYSERVER_H
    #include <QTcpServer>
    #include <QObject>
    #include <QDebug>
    #include <mythread.h>
    
    class MyServer : public QTcpServer
    {
        Q_OBJECT
    
    public:
        MyServer(QObject *parent=nullptr);
    
        QVector<MyThread*> clients; // Is public for testing, NOTE : do setter and getter
        void StartServer();
    
    signals:
        void emitDataReceivedFromMyServer(const QByteArray, MyThread * source);
        void emitSendMessageFromMyServer(QByteArray);
    
    public slots:
        void slotDataReceivedFromThread(QByteArray, MyThread * source);
        void slotRemoveClient(MyThread* thisThread);
        void slotLostConnectionOnThread();
    
    protected:
        void incomingConnection(qintptr);   //This is where we deal with incoming connections // Internet
    
    private:
        qintptr socketDescriptor; // What is that for ?
    
    };
    #endif // MYSERVER_H
    
    • MyServer.cpp
    #include "myserver.h"
    
    MyServer::MyServer(QObject *parent):
      QTcpServer(parent)
    {
    
    }
    
    void MyServer::StartServer()
    {
        // listen() start the server, 2222, Wifi and Ethernet
        if (!listen(QHostAddress::Any,2222))
        {
            qDebug() << "Something's wrong";
        }
        else
        {
            qDebug() << "MyServeur : Done";
        }
    }
    
    void MyServer::slotRemoveClient(MyThread * thisThread)
    {
        qDebug() << "Deleting  : " << thisThread->getSocketAdresse();
        clients.remove(clients.indexOf(thisThread));
    }
    
    void MyServer::slotLostConnectionOnThread()
    {
        // WIP
    }
    
    void MyServer::slotDataReceivedFromThread(QByteArray Data, MyThread * source)
    {
        emit emitDataReceivedFromMyServer(Data, source); // It goes to Mainwindow
    }
    
    void MyServer::incomingConnection(qintptr socketDescriptor)
    {
        qDebug() << "New Connection incoming, ID : " + QString::number(socketDescriptor);
        MyThread * thread = new MyThread(socketDescriptor,this);
        clients.append(thread); // Keep the new client in an array
        connect(thread,SIGNAL(lostConnection()),this,SLOT(slotLostConnectionOnThread()));
        connect(thread, SIGNAL(emitDataReceivedFromThread(QByteArray,MyThread*)),this, SLOT(slotDataReceivedFromThread(QByteArray,MyThread*)));
        connect(thread, SIGNAL(emitRemoveClient(MyThread*)),this, SLOT(slotRemoveClient(MyThread*)));
        thread->start();    // Which will cause the run() function ??
    }
    
    • MyThread.h
    #ifndef MYTHREAD_H
    #define MYTHREAD_H
    #include <QThread>
    #include <QTcpSocket>
    #include <QDebug>
    #include <QAbstractSocket>
    
    class MyThread : public QThread
    {
        Q_OBJECT
    
    public:
        explicit MyThread(qintptr ID, QObject *parent = nullptr); // Added ID to the constructor (For the socket ID number) // Internet
        void run() override; // NOTE : Override : since run already exist, this one take place and not the original from QTcpSocket
        QString getSocketAdresse();
        void writeData(QByteArray);  
        QString nickname; // Custom name
    
    signals:
        void error(QTcpSocket::SocketError socketerror); 
        void emitDataReceivedFromThread(QByteArray Data, MyThread* fromClient);
        void emitRemoveClient(MyThread*);
        void lostConnection();
    
    public slots:
        void readyRead(); // Everything we receive goes here
        void disconnected();
        void errorCommunication(QAbstractSocket::SocketError);
        void newState(QAbstractSocket::SocketState);
    
    private:
        QTcpSocket * socket; // Here is now QtcpSocket
        qintptr socketDescriptor; // Still don't know what that is // Internet
    };
    #endif // MYTHREAD_H
    

    -MyThread.cpp

    #include "mythread.h"
    
    MyThread::MyThread(qintptr ID, QObject *parent):
      QThread(parent)
    {
        socket = new QTcpSocket(this);
        socket->setSocketDescriptor(ID);
        socket->setSocketOption(QAbstractSocket::KeepAliveOption,true); // Internet
        this->socketDescriptor = ID; // Get the socket ID number // Internet
        qDebug() << "New thread, ID : " + QString::number(ID);
        // setParent(0); // ???
        // moveToThread(this); /// ???
    }
    
    void MyThread::run() // Override : priority
    {
        qDebug() << "Thread starting, ID : " << this->socketDescriptor;
        if (socket->socketDescriptor()!=socketDescriptor) // Checking I guess
        {
            emit emitDataReceivedFromThread("Error", this);
            return;
        }
    
        connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); 
        connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
        connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(erreurComm(QAbstractSocket::SocketError)));
        connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(changementEtat(QAbstractSocket::SocketState)));
        qDebug() << "Connects are done";
    
        // KEEP THIS, Internet
        // This function will cause the thread to stay alive until we tell it to close
        // Otherwise the run() function will end and the thread will be dropped / destroyed
        exec();
    }
    
    QString MyThread::getSocketAdresse()
    {
        return socket->peerAddress().toString();
    }
    
    void MyThread::writeData(QByteArray Data) // Keep that public
    {
        socket->write(Data);
        socket->waitForBytesWritten(); // Is this working ?
        msleep(15); // Just to be sure
    }
    
    void MyThread::readyRead() // We receive data
    {
        QByteArray Data = socket->readAll();
        qDebug() << Data; // Seems fine
        emit emitDataReceivedFromThread(Data, this); // Goes to MyServer
    }
    
    void MyThread::disconnected()
    {
        // emit emitRemoveClient(this);
        emit lostConnection(); // Flag this Thread as disconnected, but don't delete it (keeping scores)
        // socket->deleteLater(); // No idea what it does
        // exit(0); // ?????
    }
    
    void MyThread::errorCommunication(QAbstractSocket::SocketError error)
    {
        qDebug() << "Error occurred : " << erreur;
    }
    
    void MyThread::newState(QAbstractSocket::SocketState state)
    {
        qDebug() << "New state : " << etat;
    }
    

    My problem now is that the signals disconnected, errorOccurred and stateChanged does not seem to be emited at all.
    When a client connect to the server, and the send for exemple the string "This is a test", the slot readyRead work fine and emitDataReceivedFromThread is being done without any kind of issue.
    Same goes if from MainWindow I do writeData("Something"), the client get this string without any kind of issue.
    When I check the debug console, I get this :

    • QObject: Cannot create children for a parent that is in a different thread.
    • (Parent is QNativeSocketEngine(0x11d16d0), parent's thread is MyThread(0x11b3de0), current thread is QThread(0x1d90e0)
    • QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

    I don't konw what is happening and also don't know how to solve it. Is there an issue with my code or the way I'm thinking that is not compatible with QT?
    Also, when one of the client, which is connected to my Wifi, suddently disconnect from the Wlan, the server does not seem to detect that a client is not here anymore, and so when I call writeData(), it crashes everything.

    Thanks for everybody who will answer this, if you have a solution, or some ready-to-use code or some explaination on what I'm doing wrong, I will gladly take.



  • @Sasageyo said in QTcpSocket in QThread does not emit signals:

    to make multiples clients connect to the server, I needed to add QThread to my project

    Common misconception.
    see https://wiki.qt.io/WIP-How_to_create_a_simple_chat_application for an example of how to manage it without threads (and with them if you really need it)

    You are also are making a very common mistake. QThread is not a thread it's a container for the thread. only run() is the new thread. A slot of QThread will be executed in the thread that created the QThread object. see https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/



  • Hi again.

    After multiple test and reading from the link you provided, I managed to create the app I wanted, and it work just fine.
    Multiples clients are now connecting just fine, and I can distinctively know which one's which.
    The "simple chat application" was very usefull, I will get it somewhere in a notepad.

    Thanks for your help, I will now make this post as solved.


Log in to reply