Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QLocalSocket doesn't emit readyRead
Forum Updated to NodeBB v4.3 + New Features

QLocalSocket doesn't emit readyRead

Scheduled Pinned Locked Moved Unsolved General and Desktop
9 Posts 3 Posters 3.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Ananym
    wrote on last edited by
    #1

    I'm a beginner to IPC and sockets in general and I'm seeing behaviour that is puzzling to me.
    I have a server and client application. The client maintains one QLocalSocket connected to the server's QLocalServer. After an initial successful handshake message, the client sends messages corresponding to user events to the server, where the server processes them immediately inside its connection's readyRead slot and returns a response.

    The problem is that the client doesn't seem to acknowledge this response some of the time - its own readyRead signal does not always fire when the server responds. This seems to be generally the case every other time. Upon sending the next message, the client may then fire readyRead TWICE, for both the previous and the new message which are both then received in full. I have tried using flush() or waitforbyteswritten() on the server side to make sure the return messages are actually written - this appears to make no difference.

    However, introducing a tiny sleep - as brief as 1 milisecond - on the client immediately after writing the initial message apparently causes the client's readyRead to be emitted much more reliably when the response comes in. This is surely not a good solution, but I cannot understand the mechanics here that would point me to how this should be done. Besides the sleep, the client returns to its event loop immediately after sending the message, doing nothing but awaiting the response - there shouldn't be anything preventing it from emitting the signal. Since I'm pretty sure that the server is in fact writing and flushing each reply on time reliably, I'm not sure what would cause the messages to 'buffer up' in the fashion that I'm seeing. I've played with making various connections into type Qt::QueuedConnection, but this hasn't seemed to help anywhere I've tried it.

    I'm actually using a small wrapper that handles the message protocol, including the size byte etc. This seems to work fine as the messages come through in full. Having debugged it, it's not a case of any signals being suppressed by the wrapper.

    Client:

    //Has a connection from connection_'s readyRead signal to a handling slot.
    
    void ConnectionManager::sendMessage(QVariantMap messageContent)
    {
        //Called by user
        QDataStream writer(&block,QIODevice::WriteOnly);
        writer << messageContent;
        connection_->write(block);
        //Via the wrapper, prefixes the size to the block then calls the connection's write()
        //QThread::msleep(1) - makes all the difference?
    }
    

    Server:

    void ExtensionBridge::onNewConnection(QSharedPointer<IPC::Connection> connection)
    {
        connect(connection.data(),&IPC::Connection::readyRead,this,[](){
            QByteArray data=connection->read();
            //process data, etc etc
            QByteArray replyBlock;
            QDataStream writer(&replyBlock, QIODevice::WriteOnly);
            writer << replyData;
            connection->write(replyBlock)
        }
    //Initial handshake logic also here
    }
    

    Any ideas on what could be going on here? Why would my client's readyRead not fire? Why would a tiny sleep fix it? If anything I would expect a tiny sleep that prevents the program from returning to the event loop before the reply is written to impede the firing of readyRead, not improve it.

    J.HilkJ 1 Reply Last reply
    0
    • VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by VRonin
      #2

      QLocalSocket is implemented differently depending on the OS so if you want to know the reason why you have to specify that detail.
      As to the behaviour, it's totally fine. readyRead gives you no guarantee of how much data is available in the socket, it just tells you something is there. You can look at the ChatClient::onReadyRead method of this example to see how to correctly read from sockets using QDataStream and a small explanation on why and how it works.


      P.S.

      QDataStream writer(&block,QIODevice::WriteOnly);
          writer << messageContent;
          connection_->write(block);
      

      There's no need to put block in-between. You can use QDataStream directly on the socket:

      QDataStream writer(connection_);
      writer << messageContent;
      

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      A 1 Reply Last reply
      4
      • A Ananym

        I'm a beginner to IPC and sockets in general and I'm seeing behaviour that is puzzling to me.
        I have a server and client application. The client maintains one QLocalSocket connected to the server's QLocalServer. After an initial successful handshake message, the client sends messages corresponding to user events to the server, where the server processes them immediately inside its connection's readyRead slot and returns a response.

        The problem is that the client doesn't seem to acknowledge this response some of the time - its own readyRead signal does not always fire when the server responds. This seems to be generally the case every other time. Upon sending the next message, the client may then fire readyRead TWICE, for both the previous and the new message which are both then received in full. I have tried using flush() or waitforbyteswritten() on the server side to make sure the return messages are actually written - this appears to make no difference.

        However, introducing a tiny sleep - as brief as 1 milisecond - on the client immediately after writing the initial message apparently causes the client's readyRead to be emitted much more reliably when the response comes in. This is surely not a good solution, but I cannot understand the mechanics here that would point me to how this should be done. Besides the sleep, the client returns to its event loop immediately after sending the message, doing nothing but awaiting the response - there shouldn't be anything preventing it from emitting the signal. Since I'm pretty sure that the server is in fact writing and flushing each reply on time reliably, I'm not sure what would cause the messages to 'buffer up' in the fashion that I'm seeing. I've played with making various connections into type Qt::QueuedConnection, but this hasn't seemed to help anywhere I've tried it.

        I'm actually using a small wrapper that handles the message protocol, including the size byte etc. This seems to work fine as the messages come through in full. Having debugged it, it's not a case of any signals being suppressed by the wrapper.

        Client:

        //Has a connection from connection_'s readyRead signal to a handling slot.
        
        void ConnectionManager::sendMessage(QVariantMap messageContent)
        {
            //Called by user
            QDataStream writer(&block,QIODevice::WriteOnly);
            writer << messageContent;
            connection_->write(block);
            //Via the wrapper, prefixes the size to the block then calls the connection's write()
            //QThread::msleep(1) - makes all the difference?
        }
        

        Server:

        void ExtensionBridge::onNewConnection(QSharedPointer<IPC::Connection> connection)
        {
            connect(connection.data(),&IPC::Connection::readyRead,this,[](){
                QByteArray data=connection->read();
                //process data, etc etc
                QByteArray replyBlock;
                QDataStream writer(&replyBlock, QIODevice::WriteOnly);
                writer << replyData;
                connection->write(replyBlock)
            }
        //Initial handshake logic also here
        }
        

        Any ideas on what could be going on here? Why would my client's readyRead not fire? Why would a tiny sleep fix it? If anything I would expect a tiny sleep that prevents the program from returning to the event loop before the reply is written to impede the firing of readyRead, not improve it.

        J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by
        #3

        hi @Ananym and welcome,

        I think, but keep in mind I'm unfamiliar with QLocalSocket, the issue is indeed connected to the nature of readyRead and processing queues of your cpu.

        In the docu it's stated:

        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).

        if you emit directly a response via a write request, your client may still be inside the readyRead call -> no reemitting of the signal.

        That's an issue one usually does not run into, as Socket connections do have a latency, but with QLocalSocket that's different.


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        1 Reply Last reply
        2
        • VRoninV VRonin

          QLocalSocket is implemented differently depending on the OS so if you want to know the reason why you have to specify that detail.
          As to the behaviour, it's totally fine. readyRead gives you no guarantee of how much data is available in the socket, it just tells you something is there. You can look at the ChatClient::onReadyRead method of this example to see how to correctly read from sockets using QDataStream and a small explanation on why and how it works.


          P.S.

          QDataStream writer(&block,QIODevice::WriteOnly);
              writer << messageContent;
              connection_->write(block);
          

          There's no need to put block in-between. You can use QDataStream directly on the socket:

          QDataStream writer(connection_);
          writer << messageContent;
          
          A Offline
          A Offline
          Ananym
          wrote on last edited by
          #4

          QLocalSocket is implemented differently depending on the OS so if you want to know the reason why you have to specify that detail.

          Windows, sorry. Although I certainly will have to test on other platforms once I can prove it working.

          As to the behaviour, it's totally fine. readyRead gives you no guarantee of how much data is available in the socket, it just tells you something is there. You can look at the ChatClient::onReadyRead method of this example to see how to correctly read from sockets using QDataStream and a small explanation on why and how it works.

          Right, but I'm fairly sure there IS data available in the socket, since it was just written without any visible issue, and my application is sitting in the eventloop without ever having readyRead fire. I've been through the example but I'm guessing that it's something to do with the way that I send a response inside the server's readyRead, instants after the client sends the original message, that's tripping things up - I'm just not sure how.

          There's no need to put block in-between. You can use QDataStream directly on the socket

          This comes down to my wrapper that handles adding the size info to the message.

          if you emit directly a response via a write request, your client may still be inside the readyRead call -> no reemitting of the signal.

          I'm sure it's something LIKE this - but my client never enters readyRead, so I don't see how it can be that particular hangup. The message is initially sent due to a user event, and a reply is received if I sleep a millisecond between sending and returning to the event loop, and apparently goes ignored if I don't. I can't really come up with an idea of what could be happening to account for that difference.

          Thanks for the responses guys!

          1 Reply Last reply
          0
          • VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by
            #5

            What kind of delay are we talking about? seconds?

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Ananym
              wrote on last edited by
              #6

              Not a time delay - without the sleep, the client's readyRead doesn't fire at all, until another message/response takes place whereupon the client emits readyRead twice, receiving the original response and then the new one.

              It generally seems to follow this no signal / two consecutive signals / no signal pattern, but can vary depending on how hard the user hammers it. Which, you'd think it would suggest that the server isn't actually writing the data from the buffer to the device every time - but I've tried flush() and waitforbyteswritten() and examined the bytes written in debug, and I'm pretty sure it is. If that WERE the case, I can't see why the tiny sleep would fix it.

              It almost seems like readyRead only fires if the data is available at the instant the eventloop is resumed - if the data is written while the client is waiting in the event loop, nothing happens.

              1 Reply Last reply
              0
              • VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #7

                Very strange, I'll put together a quick basic example so we can have a proper test

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                1 Reply Last reply
                0
                • VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by
                  #8

                  Can you try compiling and run this basic example? It works fine for me (Win7)

                  Server

                  #include <QCoreApplication>
                  #include <QLocalSocket>
                  #include <QLocalServer>
                  #include <QTimer>
                  #include <QDataStream>
                  #include <QDebug>
                  #include <QTime>
                  int main(int argc, char *argv[])
                  {
                      QCoreApplication a(argc, argv);
                      QLocalServer localServer;
                      QObject::connect(&localServer,&QLocalServer::newConnection,[&localServer](){
                          QLocalSocket* sock = localServer.nextPendingConnection();
                          Q_ASSERT(sock);
                          QObject::connect(sock,&QIODevice::readyRead,[sock](){
                              QString tempString;
                              QDataStream stream(sock);
                              for(;;){
                                  stream.startTransaction();
                                  stream >> tempString;
                                  if(!stream.commitTransaction())
                                      return;
                                  qDebug() << QStringLiteral("Server Received at ")
                                              + QTime::currentTime().toString(Qt::ISODateWithMs)
                                              + QStringLiteral(": ")
                                              + tempString;
                              }
                          });
                          QTimer* senderTimer = new QTimer(sock);
                          QObject::connect(senderTimer,&QTimer::timeout,sock,[sock](){
                              QDataStream stream(sock);
                              stream << QStringLiteral("Server Sent at ")
                                      + QTime::currentTime().toString(Qt::ISODateWithMs);
                          });
                          senderTimer->start(1000);
                      });
                      Q_ASSUME(localServer.listen(QStringLiteral("TestPipe")));
                      qDebug() << QStringLiteral("Server Started");
                      return a.exec();
                  }
                  

                  Client

                  #include <QCoreApplication>
                  #include <QLocalSocket>
                  #include <QTimer>
                  #include <QDataStream>
                  #include <QDebug>
                  #include <QTime>
                  int main(int argc, char *argv[])
                  {
                      QCoreApplication a(argc, argv);
                      QLocalSocket clientSocket;
                      QObject::connect(&clientSocket,&QIODevice::readyRead,[&clientSocket](){
                          QString tempString;
                          QDataStream stream(&clientSocket);
                          for(;;){
                              stream.startTransaction();
                              stream >> tempString;
                              if(!stream.commitTransaction())
                                  return;
                              qDebug() << QStringLiteral("Client Received at ")
                                          + QTime::currentTime().toString(Qt::ISODateWithMs)
                                          + QStringLiteral(": ")
                                          + tempString;
                          }
                      });
                      QObject::connect(&clientSocket,&QLocalSocket::connected,[&clientSocket](){
                          QTimer* senderTimer = new QTimer(&clientSocket);
                          QObject::connect(senderTimer,&QTimer::timeout,&clientSocket,[&clientSocket](){
                              QDataStream stream(&clientSocket);
                              stream << QStringLiteral("Client Sent at ")
                                      + QTime::currentTime().toString(Qt::ISODateWithMs);
                          });
                          senderTimer->start(1000);
                      });
                      clientSocket.connectToServer(QStringLiteral("TestPipe"));
                      qDebug() << QStringLiteral("Client Started");
                      return a.exec();
                  }
                  
                  

                  To run the test open the server first and then the client you can tweak the argument of senderTimer->start(1000); to increase/decrease the interval between calls

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  1 Reply Last reply
                  7
                  • A Offline
                    A Offline
                    Ananym
                    wrote on last edited by
                    #9

                    Welp. That works fine.
                    And when I amended it so the server sends its response inside readyRead, that works fine.
                    And when I changed the sending/receiving method to match the more explicit serialization my wrapper uses, that - frustratingly - worked fine.

                    Hm! I hope to test it with the same wrapper library I had been using but integrating it is going to take me some time to stumble around. The server application I'm adding to already has quite a busy event loop, so it's hard to say what else might play into it. Thanks a lot for your help, having such an elegant minimal solution does make things easier and this goes a long way to exclude possible factors.

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved