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

QTcpServer / QTcpSocket help



  • I'm writing and application that uses QTcpServer and another that uses QTcpSocket. The server application inherits from QTcpServer:

    class clsSocketServer : public QTcpServer {
    Q_OBJECT
    
    protected:
        void incomingConnection(qintptr sfd);
    
    public:
        static const QString mscstrCRLF;
        static const quint16 mscuint16port;
    
        explicit clsSocketServer(QObject* pParent = nullptr);
        ~clsSocketServer();
    
    public slots:
        void discardClient();
        void readClient();
    };
    

    Implementation:

    const QString clsSocketServer::mscstrCRLF("\r\n");
    const quint16 clsSocketServer::mscuint16port = 8123;
    /**
     * @brief clsSocketServer::clsSocketServer - class constructor
     */
    clsSocketServer::clsSocketServer(QObject* pParent) : QTcpServer(pParent) {
        QString strListenFailure = QString("Cannot listen to port: ") + QString::number(clsSocketServer::mscuint16port);
        Q_ASSERT_X(listen(QHostAddress::Any, clsSocketServer::mscuint16port)!=false
                  ,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    }
    /**
     * @brief clsSocketServer::~clsSocketServer - class destructor
     */
    clsSocketServer::~clsSocketServer() {
        close();
    }
    /**
     * @brief clsSocketServer::discardClient
     */
    void clsSocketServer::discardClient() {
        QTcpSocket* pSckClient = (QTcpSocket*)sender();
        pSckClient->deleteLater();
    }
    /**
     * @brief clsSocketService::incomingConnection
     * @param sfd : Client Socket Descriptor
     */
    void clsSocketServer::incomingConnection(qintptr sfd) {
        QTcpSocket* pSckClient = new QTcpSocket(this);    
        connect(pSckClient, SIGNAL(readyRead()), this, SLOT(readClient()));
        connect(pSckClient, SIGNAL(disconnected()), this, SLOT(discardClient()));
        pSckClient->setSocketDescriptor(sfd);
    }
    /**
     * @brief clsSocketServer::readClient
     */
    void clsSocketServer::readClient() {
        enum {
            REQ_METHOD = 0
           ,REQ_DATA
        };
        QTcpSocket* pSckClient = (QTcpSocket*)sender();
    
        if (!pSckClient->canReadLine() ) {
        //Cannot read input stream, abort!
            return;
        }
        QStringList slstTokens = QString(pSckClient->readLine()).split(QRegExp("[ \r\n][ \r\n]*"));
       ...
    }
    

    My application launches a child process which uses QTcpSocket:

    //Set-up the data stream
    mdsIn.setDevice(this);
    mdsIn.setVersion(QDataStream::Qt_5_15);
    //Connect up the signals
    connect(this, &QIODevice::readyRead, this, &clsModHelper::dataIn);
    connect(this, &QAbstractSocket::errorOccurred, this, &clsModHelper::errorOccurred);
    //Connect to the Application
    connectToHost(clsModHelper::mscpszHost, muint16Port);
    
    if ( !waitForConnected(clsModHelper::mscintConnectionTimeout) ) {
        exit(EXIT_FAILURE);
    }    
    

    clsModHelper::mscpszHost is "localhost" and muint16Port is 8123. The slots for dataIn and errorOccurred:

    /**
     * @brief clsModHelper::dataIn
     */
    void clsModHelper::dataIn() {
        qdbg() << "dataIn";
    }
    /**
     * @brief clsModHelper::errorOccurred
     * @param socketError : Error information
     */
    void clsModHelper::errorOccurred(QAbstractSocket::SocketError socketError) {
        qdbg() << "errorOccurred: " << errorString();
    }
    

    I'm not seeing anything in either dataIn or errorOccurred. I also tried adding:

    pSckClient->write("Hello", 5);
    

    To the end of the incomingConnection function in the main application, I don't see it received by the child process. What have I done wrong or missed out?


  • Lifetime Qt Champion

    @SPlatten Did you make sure incomingConnection was called?



  • @SPlatten said in QTcpServer / QTcpSocket help:

    First, this is not a good practice:

    Q_ASSERT_X(listen(QHostAddress::Any, clsSocketServer::mscuint16port)!=false
    ,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());

    When build as release, this will be removed!
    Please change this into:

    bool success = listen(QHostAddress::Any, clsSocketServer::mscuint16port);
    Q_ASSERT_X(!success,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    

    or

    listen(QHostAddress::Any, clsSocketServer::mscuint16port);
    Q_ASSERT_X(!isListening(),"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    


  • @jsulm I can see incomingConnection is called as I have a breakpoint in the function on the last line.



  • @KroMignon Thank you, good call!



  • @SPlatten And second, where did you connect QTcpServer::newConnection?

    I am missing something like:

    clsSocketServer::clsSocketServer(QObject* pParent) : QTcpServer(pParent) {
        connect(this, &QTcpServer::newConnection, [this](){
            while(hasPendingConnections())
            {
                auto socket = m_server->nextPendingConnection();
                if(!socket)
                    break;
                // do something with the client!
            }
    
        });
        bool success = listen(QHostAddress::Any, clsSocketServer::mscuint16port);
        Q_ASSERT_X(!success,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    }
    


  • @KroMignon, I used:
    https://doc.qt.io/qt-5/qtnetwork-threadedfortuneserver-example.html

    As a guide for the server, I don't see anything like that on that page?


  • Lifetime Qt Champion

    @KroMignon said in QTcpServer / QTcpSocket help:

    And second, where did you connect QTcpServer::newConnection?

    He's overwriting QTcpServer::incomingConnection() because he follows the threading example (for unknown reasons)



  • @KroMignon, Are you sure your test in Q_ASSERT_X is correct? I get an exception immediately when using !isListening() or !success.



  • @Christian-Ehrlicher , is it wrong?



  • @SPlatten said in QTcpServer / QTcpSocket help:

    Are you sure your test in Q_ASSERT_X is correct?

    my bad, sorry: the test is inverted!
    should be

    Q_ASSERT_X(success,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    or 
    Q_ASSERT_X(isListening(),"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());
    


  • @KroMignon , Thanks for confirming.



  • @SPlatten said in QTcpServer / QTcpSocket help:

    is it wrong?

    It is not specially wrong, but using QTcpServer::waitForConnected() is a blocking method, so no event will be handled during this call for the used thread!

    Using QTcpServer::newConnection() signal is the non blocking alternative, which is more QEventLoop/QEvent friendly ;)

    ==> Read documentation for more details



  • @KroMignon, I call connectToHost in the process that is launched this connects to the application that launched it and this uses QTcpSocket not QTcpServer. The new connection would be created by the launcher when it processes the connection.

    I call waitForConnection purposely as the child process has no other purpose without a connection to the launcher.

    I think the problem is in the launcher in this function:

    void clsSocketServer::incomingConnection(qintptr sfd) {
        QTcpSocket* pSckClient = new QTcpSocket(this);    
        connect(pSckClient, SIGNAL(readyRead()), this, SLOT(readClient()));
        connect(pSckClient, SIGNAL(disconnected()), this, SLOT(discardClient()));
        pSckClient->setSocketDescriptor(sfd);
    
        pSckClient->write("Hello", 5);
    }
    

    I can see in the debugger an incomingConnection is called, but the test write, doesn't happen.



  • @KroMignon said in QTcpServer / QTcpSocket help:

    Please change this into:
    bool success = listen(QHostAddress::Any, clsSocketServer::mscuint16port);
    Q_ASSERT_X(!success,"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());

    or
    listen(QHostAddress::Any, clsSocketServer::mscuint16port);
    Q_ASSERT_X(!isListening(),"clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data());

    You can use Q_ASSUME is for this kind of cases. Q_ASSUME(listen(QHostAddress::Any, clsSocketServer::mscuint16port));

    @SPlatten said in QTcpServer / QTcpSocket help:

    I used:
    https://doc.qt.io/qt-5/qtnetwork-threadedfortuneserver-example.html

    Unfortunately that's not a good example to start on TCP. Some of us here prepared a better example to serve as a starting point, have a look at https://wiki.qt.io/WIP-How_to_create_a_simple_chat_application



  • @VRonin , thank you, I will take a look now.



  • This post is deleted!

Log in to reply