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. QTcpSocket : readRead() not being triggered when data is written on the socket

QTcpSocket : readRead() not being triggered when data is written on the socket

Scheduled Pinned Locked Moved Unsolved General and Desktop
qtcpsocketqtcpservertcpsockettcp servertcp
11 Posts 5 Posters 3.4k 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.
  • R Offline
    R Offline
    RBLL
    wrote on 25 Mar 2020, 17:18 last edited by
    #1

    Hi,

    So I have a weird behavior trying to stream data over TCP. Basically, this is what happens:

    • The client successfuly connects to the server and sends an initial request to start the data stream.
    • The server starts sending the data stream packets through multiple write calls on the socket.
    • The client is waiting on waitForReadyRead(). The first time data is available (first readyRead()) everything goes as expected.
    • However, afterwards, readyRead() is never triggered again even though the server keeps writing on the socket.

    Here is the code (with the processing of the data replaced by a simple console output):

    • Client connecting to the server and passing the socket descriptor:
    void AccelStreamThread::run()
    {
    	// create the socket
    	QTcpSocket *wTcpSocket = new QTcpSocket();
    	connect(wTcpSocket, SIGNAL(disconnected()), this, SLOT(connectionClosedByServer(QTcpSocket *socket)));
    	connect(wTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QTcpSocket *socket)));
    	
    	// connect to the server
    	connectToServer(wTcpSocket, mMclpcIpAddress, mMclpcPort);
    	
    	// send the initial request
    	sendRequest(wTcpSocket, mClientRequest);
    
    	QDataStream wIn(wTcpSocket);
    	wIn.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    
    	forever
    	{
     		QVector<QPair<float, float>> wAccelData;
    		
    		// whenever data is available in the socket
    		if (wTcpSocket->waitForReadyRead(3000))
    		{
    			std::cout << "Client inside waitForReadyRead." << std::endl;
    
    			//emit accelStreamThreadResultsRdy(wAccelData);
    		}
    	}
    }
    
    • Here are the connectToServer and sendRequest methods:
    bool AccelStreamThread::connectToServer(QTcpSocket *socket, QString ipAdress, QString port)
    {
    	if (socket != NULL)
    	{
    		socket->connectToHost(ipAdress, port.toInt());
    		if (socket->waitForConnected(3000))
    		{
    			qDebug() << "Client connected to server.";
    			return true;
    		}
    		else
    			qDebug() << "Client couldn't connect to server.";
    	}
    	return false;
    }
    
    void AccelStreamThread::sendRequest(QTcpSocket *socket, QString request)
    {
    	QByteArray wBlock;
    	QDataStream wOut(&wBlock, QIODevice::WriteOnly);
    	wOut.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    	wOut << quint16(0) << request;
    	
    	wOut.device()->seek(0);
    	wOut << quint16(wBlock.size() - sizeof(quint16));
    	socket->write(wBlock);
    }
    
    • Whenever the server receives an incoming connection, this is what happens:
    void TCPServer::incomingConnection(qintptr handle)
    {
    	ClientSocket* wSocket = new ClientSocket(this);
    	wSocket->setSocketDescriptor(handle);
    }
    

    And then the data is written from the server to the client through this SLOT which is called multiple times (loop).

    void ClientSocket::AccelStreamTaskResult(QVector<QPair<float, float>> accelData)
    {
    	QByteArray wBlock;
    	QDataStream wOut(&wBlock, QIODevice::WriteOnly);
    	wOut.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    	
    	wOut << quint16(0);
    	wOut << accelData.front();
    	wOut.device()->seek(0);
    	wOut << quint16(wBlock.size() - sizeof(quint16));
    	
    	std::cout << "Server calling write function" << std::endl
    	write(wBlock);
    }
    

    Now, what happens is that the server will start firing many write() calls through AccelStreamTaskResult reflected by many console outputs of "Server calling write function". However, the client will only pass the if(waitForReadyRead()) gate once, therefore only outputing "Client inside waitForReadyRead." once.

    Any ideas as to what is happening?

    Thank you very much!

    1 Reply Last reply
    0
    • M Offline
      M Offline
      MrShawn
      wrote on 25 Mar 2020, 17:31 last edited by
      #2

      Sure,

      Ask yourself where do you think you are actually writing the data to the client socket?

      QDataStream::QDataStream(QIODevice *d)

      You are just writing to some QByteArray, you want to call the constructor which asks for a ptr to QIODevice i.e. your client socket.
      You actually did the QDataStream right on the client side.

      Also I would use readyRead signal from the socket rather than use a thread's run method.

      1 Reply Last reply
      0
      • C Offline
        C Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on 25 Mar 2020, 17:46 last edited by
        #3

        Don't use a blocking connection when waiting for data but signals and slots.
        Then your QIODevice::seek() does not work: https://doc.qt.io/qt-5/qiodevice.html#details

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        2
        • S Offline
          S Offline
          SGaist
          Lifetime Qt Champion
          wrote on 25 Mar 2020, 19:17 last edited by
          #4

          Hi,

          One thing you did not share was the code reading the data received.

          Why are you using the blocking API and then mixing in the synchronous API ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          R 1 Reply Last reply 26 Mar 2020, 12:55
          2
          • S SGaist
            25 Mar 2020, 19:17

            Hi,

            One thing you did not share was the code reading the data received.

            Why are you using the blocking API and then mixing in the synchronous API ?

            R Offline
            R Offline
            RBLL
            wrote on 26 Mar 2020, 12:55 last edited by
            #5

            @SGaist

            Hi, so I'm fairly new to both QT and Tcp programming so perhaps the way I'm seeing things is completely wrong.

            Basically, the idea is that the client will receive data packets at a very fast rate from the server that it needs to update live on a graph. So I thought that I would have the receive function in a separate thread. This is what happens: whenever the client sends the request to start the data streaming, it spawns a new thread that receives the data and then emits a SIGNAL for the client to process it and display the results. However, inside the newly created thread, I had to waitForReadyRead otherwise I don't know if data is available on the socket...

            Feel free to suggest another way to do this as it might not be the best way to do it...

            JonBJ 1 Reply Last reply 26 Mar 2020, 13:50
            0
            • R RBLL
              26 Mar 2020, 12:55

              @SGaist

              Hi, so I'm fairly new to both QT and Tcp programming so perhaps the way I'm seeing things is completely wrong.

              Basically, the idea is that the client will receive data packets at a very fast rate from the server that it needs to update live on a graph. So I thought that I would have the receive function in a separate thread. This is what happens: whenever the client sends the request to start the data streaming, it spawns a new thread that receives the data and then emits a SIGNAL for the client to process it and display the results. However, inside the newly created thread, I had to waitForReadyRead otherwise I don't know if data is available on the socket...

              Feel free to suggest another way to do this as it might not be the best way to do it...

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on 26 Mar 2020, 13:50 last edited by
              #6

              @RBLL
              Because QTcpSocket is already implemented asynchronously for you, you don't need the overhead or complication of your own separate threads or signals. And no need to call the unattractive waitForReadyRead.

              Dump your separate thread. Just use signal https://doc.qt.io/qt-5/qiodevice.html#readyRead from your GUI thread to act on data received.

              R 1 Reply Last reply 26 Mar 2020, 13:53
              2
              • JonBJ JonB
                26 Mar 2020, 13:50

                @RBLL
                Because QTcpSocket is already implemented asynchronously for you, you don't need the overhead or complication of your own separate threads or signals. And no need to call the unattractive waitForReadyRead.

                Dump your separate thread. Just use signal https://doc.qt.io/qt-5/qiodevice.html#readyRead from your GUI thread to act on data received.

                R Offline
                R Offline
                RBLL
                wrote on 26 Mar 2020, 13:53 last edited by
                #7

                @JonB Ah thank you very much. I guess I was over complicating things for no reason. Thanks I'll try it out!

                JonBJ 1 Reply Last reply 26 Mar 2020, 14:14
                1
                • R RBLL
                  26 Mar 2020, 13:53

                  @JonB Ah thank you very much. I guess I was over complicating things for no reason. Thanks I'll try it out!

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on 26 Mar 2020, 14:14 last edited by
                  #8

                  @RBLL
                  Yep, give it a go. Hopefully it keeps up, come back if it is does not, that's a separate issue.

                  I also see you seem to be receiving "messages" back to your client. readyRead signal will activate (potentially) even with just one byte back, it makes no guarantee the number of bytes you'll get in one go will be equal to a complete sent message. Your client code is responsible for buffering received data to implement this. To save you writing it, look at https://doc.qt.io/qt-5/qiodevice.html#startTransaction, where Qt has the code to implement this for you.

                  I think the example https://doc.qt.io/qt-5/qtnetwork-fortuneclient-example.html uses transactions, it's worth looking at.

                  R 1 Reply Last reply 26 Mar 2020, 14:20
                  0
                  • JonBJ JonB
                    26 Mar 2020, 14:14

                    @RBLL
                    Yep, give it a go. Hopefully it keeps up, come back if it is does not, that's a separate issue.

                    I also see you seem to be receiving "messages" back to your client. readyRead signal will activate (potentially) even with just one byte back, it makes no guarantee the number of bytes you'll get in one go will be equal to a complete sent message. Your client code is responsible for buffering received data to implement this. To save you writing it, look at https://doc.qt.io/qt-5/qiodevice.html#startTransaction, where Qt has the code to implement this for you.

                    I think the example https://doc.qt.io/qt-5/qtnetwork-fortuneclient-example.html uses transactions, it's worth looking at.

                    R Offline
                    R Offline
                    RBLL
                    wrote on 26 Mar 2020, 14:20 last edited by RBLL
                    #9

                    @JonB
                    Ah thats a fair point. I always assumed that at least the number of bytes would make it. I'll look into transactions!

                    However, I still have the issue (even after removing the client thread and only usind readyRead) that the readyRead signal is only triggered once. I'll keep investigating. The issue might be from the server. I'll keep you posted.

                    Thank you very much for you help!

                    JonBJ 1 Reply Last reply 26 Mar 2020, 14:40
                    0
                    • R RBLL
                      26 Mar 2020, 14:20

                      @JonB
                      Ah thats a fair point. I always assumed that at least the number of bytes would make it. I'll look into transactions!

                      However, I still have the issue (even after removing the client thread and only usind readyRead) that the readyRead signal is only triggered once. I'll keep investigating. The issue might be from the server. I'll keep you posted.

                      Thank you very much for you help!

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on 26 Mar 2020, 14:40 last edited by JonB
                      #10

                      @RBLL said in QTcpSocket : readRead() not being triggered when data is written on the socket:

                      that the readyRead signal is only triggered once

                      Again (although it may well be your code, leave you to investigate), just as readyRead might fire with just the first byte of a "message", it might also fire just once with hundreds of bytes across multiple messages if multiple messages are sent (while developing/debugging, see bytesAvailable from readyRead). That's why transactions can help you. You will need to play and see how it behaves in your environment.

                      EDIT P.S. Also, ISTR readyRead will not fire again until you have done something like readAll to read the available bytes from the (internal) buffer.

                      1 Reply Last reply
                      2
                      • S Offline
                        S Offline
                        SGaist
                        Lifetime Qt Champion
                        wrote on 26 Mar 2020, 19:34 last edited by
                        #11

                        The edited part of @JonB is important, you have to read the data.

                        Interested in AI ? www.idiap.ch
                        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                        1 Reply Last reply
                        1

                        1/11

                        25 Mar 2020, 17:18

                        • Login

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