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

Help writing non blocking TCP code



  • Hello,

    I am working on an app that uses QTcpSockets, and I have some issues...

    The app is using Qsocket write, in a bytesWritten slot to write from a data queue and read of bytesAvailable in the readyRead slot...

    But I have 2 issues:

    1. Data writing seems to block the UI. I think that it is because I do not know how much can be written at a time in the write call. So the write tries to write all it has in buffer, most likely causing a TCP write block.
      When using winsock sockets API, one can use non blocking sockets and WSAEWOULDBLOCK to control this. But what is the QT equivalent?
    2. Nothing seems to happen when the UI is not "up"... ie, when the application is minimized to tray. (note, the application is a Qml app)...

    Any idea as to what I should do to remedies these issues?

    Thanks,
    Cyrille


  • Qt Champions 2017



  • Hello,

    This code uses socketStream << jsonData; to send the data...

    I am assuming that this uses the write function to do the writing...
    This means that if the message is large (10MB, 100MB for example), this call, which is in the UI thread will block the UI...

    This is problematic as my application needs the sending to happen while the rest of the UI stays active...

    In order to do that, my application saves the data to be sent and keep a "next to write" pointer in memory. Something like this (pseudo code written on the fly):

    QByteArray dataToSend; // The data that needs to be sent
    int sendingPos; // pos of the next chunk to send
    
    // Called by the user to initiate a data sending...
    void onUserTellsSystemToSend(QByteArray data)
    {
      dataToSend= data; // make a copy of the data
      sendingPos= socket.write(dataToSend.data(), min(dataToSend.count(), 10*1024)); // send the first 10K.
    }
    
    // This is connected to the socked byteWritten signal to do the rest of the sending ansychrnously
    void onByteWrittenSlot(int NbBytes)
    {
      if (sendingPos>=dataToSend.count()) // Done sending?
        dataToSend.clear;                 // clear the buffer
      else
        sendingPos+= socket.write(dataToSend.data(), min(dataToSend.count()-sendinPos, 10*1024)); // send the next 10kb...
    }
    

    But I think that even then the write in the onByteWrittenSlot function still ends up blocking as the socket inner buffer is full at this time, hence my write blocks the slot and the message queue...

    When using berkley socket in the past, I would mark my socket as non blocking and the write call would return the number of bytes that it could write without blocking, but I have not found how to do that with QT.

    Could someone confirm this?

    Cyrille


  • Qt Champions 2017

    Correct. However this tutorial also provides a threaded example where the UI thread is kept disentangled from the socket operations.



  • @kshegunov
    Hello, Sorry, I missed the 2nd part, since they was not much visible code, I did not spot it...

    Anyhow, what you are saying is that, in my case, I have to go threaded to solve my issues? There is no way to change the code in the onByteWrittenSlot to make it work, something like:

    // This is connected to the socked byteWritten signal to do the rest of the sending ansychrnously
    void onByteWrittenSlot(int NbBytes)
    {
      if (sendingPos>=dataToSend.count()) // Done sending?
        dataToSend.clear;                 // clear the buffer
      else {
        int nb= socket.getNumberOfBytesICanWriteWithoutBlocking();
        sendingPos+= socket.write(dataToSend.data(), min(dataToSend.count()-sendinPos, nb)); // send the next  block
      }
    }
    

    Cyrille


  • Qt Champions 2017

    @Cyrille-de-Brebisson said in Help writing non blocking TCP code:

    Anyhow, what you are saying is that, in my case, I have to go threaded to solve my issues?

    You don't "have to", strictly, but if you're working with rather big datasets it's going to be beneficial, so you don't get the network latency to show into the GUI thread.

    There is no way to change the code in the onByteWrittenSlot to make it work, something like

    However you change it it's still going to require time to flush the buffers from the socket layer to the actual network, and from Qt to the socket layer. If the data is large-ish, it can be noticeable, so that's when you'd opt for threads - when you can't tolerate the latency. In most cases you can work with the GUI thread fine, though, I'm not saying you always have to do the threading.

    (Note that Qt already buffers the data for you, so most of what you do with that second buffer isn't necessary)



  • Hello,

    @kshegunov said in Help writing non blocking TCP code:

    Note that Qt already buffers the data for you, so most of what you do with that second buffer isn't necessary)

    Does this mean that if I do a socket.write(QByteArray), regardless of the size of the byteArray, it will write the whole thing at once (ie: write will return the count of the QByteArray) as QT will buffer the array (this is assuming of course that they are no errors)?

    When is onByteWrittenSlot called? Once the whole data that was sent to write is finally fully passed through to the native network layer? or would writing 10MB to a socket cause multiple signals?

    If I reuse the above 10MB example.
    If I have a 10MB ByteArray can I do a single socket.write(array) and will I get a single onByteWrittenSlot signal in return?

    Cyrille


  • Lifetime Qt Champion

    @Cyrille-de-Brebisson said in Help writing non blocking TCP code:

    When is onByteWrittenSlot called?

    Is perfectly documented in the signal documentation:

    This signal is emitted every time a payload of data has been written to the device's current write channel. The bytes argument is set to the number of bytes that were written in this payload.



  • Hello,

    @Christian-Ehrlicher said in Help writing non blocking TCP code:

    This signal is emitted every time a payload of data has been written to the device's current write channel. The bytes argument is set to the number of bytes that were written in this payload.

    yep, I read that, what I do not know is if a "payload of data" is the equivalent of one write call or something else...

    Cyrille


  • Lifetime Qt Champion

    @Cyrille-de-Brebisson It's OS-dependent and should not matter at all. The important thing is you get a progress.


  • Qt Champions 2017

    @Cyrille-de-Brebisson said in Help writing non blocking TCP code:

    If I have a 10MB ByteArray can I do a single socket.write(array) and will I get a single onByteWrittenSlot signal in return?

    No you may get multiple, as Christian wrote, it still shouldn't matter.



  • Hello,

    What maters to me is that I would like, if possible, to not block the GUI...

    At the moment, my socket.write calls seems to do so :-(

    I would rather, if possible, avoid threads.

    So, here is another formulation of the question.
    Is there a way for me to know how many data can be "in the QT side of the pipe" so that I am not causing stalls in my write calls?

    Then, I can monitor the differential bettwen the "byte written" signal data and what I sent through write to "throttle" my writes and stay bellow this limit. Limit which might vary per OS/network channel of course.

    Cyrille


  • Lifetime Qt Champion

    @Cyrille-de-Brebisson said in Help writing non blocking TCP code:

    At the moment, my socket.write calls seems to do so :-(

    But not because of the write() call since this is done async as already explained. Please provide a minimal example to reproduce the issue you're seeing.


Log in to reply