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

problem with QLocalSocket sending continues data to QLocalServer



  • I'm trying to send some data from QLocalSocket to QLocalSever in a loop. Sever only gets the first data and not receiving subsequent data, but if I introduce 1 mec delay between each call from the client then the server starts to receive everything. Please check out the below Client & Server code.

    client.cpp

    #include "client.h"
    #include "QDataStream"
    #include <QTest>
    
    TestClient::TestClient() : m_socket{new QLocalSocket(this)}{
        m_socket->connectToServer("TestServer");
        if (m_socket->waitForConnected(1000)) {
          qDebug("socket Connected!");
        }
        connect(m_socket, &QLocalSocket::readyRead, this, &TestClient::onNewData);
    }
    
    void TestClient::onNewData() {
        qCritical() << "data received from server";
    }
    
    void TestClient::sendDataToServer() {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_5_10);
        QString testString = "test data";
        out << quint32(testString.size());
        out << testString;
        m_socket->write(block);
        m_socket->flush();
    }
    
    void TestClient::startClient() {
        for(int i = 0; i < 5; i++) {
            //QTest::qWait(1); //works if I uncomment this line
            sendDataToServer();
        }
    }
    

    server.cpp

    #include "server.h"
    
    TestServer::TestServer() : m_server{new QLocalServer(this)} {
        QLocalServer::removeServer("TestServer");
        if (!m_server->listen("TestServer")) {
            qCritical() << "couldn't connect to server";
        }
        connect(m_server, &QLocalServer::newConnection, this, &TestServer::onNewConnection);
    }
    
    void TestServer::onNewConnection() {
        m_socket = m_server->nextPendingConnection();
        connect(m_socket, &QLocalSocket::readyRead, this,
                &TestServer::onNewData);
        connect(m_socket, &QLocalSocket::disconnected, m_socket,
                &QLocalSocket::deleteLater);
    }
    
    void TestServer::onNewData() {
        QLocalSocket* client = qobject_cast<QLocalSocket*>(sender());
        client->readAll();
        qCritical() << "data read by server";
    }
    

    from the qt doc, it's stated that

    readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

    so is this my problem? adding timer is the only solution here?

    You can compile this test project -> testsocket


  • Lifetime Qt Champion

    All is fine - you get all data with one readyRead signal / readAll() call.



  • @Venkateswaran said in problem with QLocalSocket sending continues data to QLocalServer:

    Sever only gets the first data and not receiving subsequent data,

    How are you sure? You do not debug out the content/length of client->readAll();, so you don't know. My guess is: you assume a readAll() or onNewData signal correspond one-to-one with write/flish from client. But they don't. Your "1 msec delay" will make that true, probably, which is why you think you need that. You don't.



  • @JonB yes I thought it's a one-to-one relationship. if I write 5 from client-side then I will get 5 readyRead signals.


  • Lifetime Qt Champion

    @Venkateswaran Why? It's a stream with no further protocol. It can even happen that you get a readyRead signal for every single byte you sent.



  • @Venkateswaran
    And it absolutely is not! :)

    • readyRead fires when there is at least 1 byte available.
    • readAll reads all that happens to be there when it is called. Anything ranging from 0 bytes to every byte sent!
    • readyRead won't fire again till you've done a readAll.

    That's it. No one-to-one. Your job to buffer received data at receiver, or split it up, if that's what you want to do.



  • @JonB Thanks for clarifying this. Now I have another problem, on the server-side, I need to call another function with received test string from the client. I have tested the readAll() and as you said I'm receiving 130 bytes all at once (which is 5 times my test string). So how do I separate on server-side? I'm using QDataStream on client-side to send data and is there an elegant way to decouple the received bytes at server-side with QDataStream? maybe its a really basic question but it would be helpful for me if you show some example.



  • @Venkateswaran
    Start by: have you read (and understood!) all the detail in https://doc.qt.io/qt-5/qdatastream.html#details ? And do look at subsection https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions, because you may be looking for that.

    How is the client sending? ("I'm using QDataStream on client-side to send data" --- with just which calls on what data object types?) If you are using QDataStream at client send you will want QDataStream at server receive. And just btw: when you talk sometimes about "bytes" and sometimes about "string" do you indeed want QDataStream or did you maybe want QTextStream?



  • @JonB also should i need to call m_socket->waitForBytesWritten(); at client side ?



  • @Venkateswaran
    Nope :) Not unless you want to, it just stops client proceeding to the next line of code till bytes are written. But it makes no difference to the protocol/behaviour. And no effect at server-side.



  • @JonB Thanks for the information. I want to send Boolean, Number, String from the client so DataStream would be the right one.
    This is how I'm sending data from the client :

    void TestClient::sendDataToServer() {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_5_10);
        QString testString = "test data";
        out << quint32(testString.size());
        out << testString;
        m_socket->write(block);
        m_socket->flush();
    }
    

    This is what I tried at the server side to receive data (and it work for this basic example).

    void TestServer::onNewData() {
        QLocalSocket* client = qobject_cast<QLocalSocket*>(sender());
        qCritical() << "TestServer::onNewData" << client->bytesAvailable();
        QDataStream in;
        in.setDevice(client);
        in.setVersion(QDataStream::Qt_5_10);
        quint32 blockSize = 0;
        QString test;
    
        while(client->bytesAvailable() > (int)sizeof(quint32)) {
            in >> blockSize;
            if (client->bytesAvailable() < blockSize || in.atEnd()) return;
            in >> test;
            qDebug() << test << "printing received value";
        }
        qCritical() << "data read by server";
    }
    

    But looks like This Answer has a nice example to start with



  • @Venkateswaran
    Note that the link you reference (by @VRonin) uses the transactions I suggested earlier: https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions. No bytesAvailable() or buffering. Up to you.



  • @JonB said in problem with QLocalSocket sending continues data to QLocalServer:

    readyRead won't fire again till you've done a readAll.

    This is not correct. From https://doc.qt.io/qt-5/qabstractsocket.html

    The readyRead() signal is emitted every time a new chunk of data has arrived.

    So it doesn't care whether you read the data or not


    I suggest having a look at this article it uses QTcpSocket but the code is exactly the same for QLocalSocket. I'd focus on the ChatClient::onReadyRead method and its explanation below



  • @VRonin said in problem with QLocalSocket sending continues data to QLocalServer:

    This is not correct.

    Damn, and sorry! I thought that it had said that, but not. I may have confused with

    readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).