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. How to use QTcpSocket properly in the real world?
QtWS25 Last Chance

How to use QTcpSocket properly in the real world?

Scheduled Pinned Locked Moved General and Desktop
12 Posts 3 Posters 10.5k 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.
  • L Offline
    L Offline
    liuyanghejerry
    wrote on last edited by
    #1

    There's already an example called Fortune Client Example, but it's not good enough that even need a QTimer.

    I wrote my own algorithm, however, it lost data sometimes. It's like:

    @void DataSocket::sendData(const QByteArray &content)
    {
    if(state() == QAbstractSocket::ConnectedState){
    // QByteArray buffer= qCompress(content);
    QByteArray buffer= content;
    QDataStream in(this);
    in << quint32(buffer.size()) << buffer;
    }
    }

    //signal newData() is connected with slot readyRead()
    void DataSocket::newData()
    {
    if(state() == QAbstractSocket::ConnectedState){
    if( bytesAvailable() < (qint64)sizeof(quint32) ) return;
    QDataStream in(this);
    if(!dataSize)in >> dataSize;
    if(bytesAvailable() < dataSize) {
    return;
    }
    QByteArray array;
    array.resize(dataSize);
    in >> array;
    // array = qUncompress(array);

        emit newData(array);
        dataSize = 0;
        if( bytesAvailable() ) newData();
    }
    

    }@

    Did I miss something? or is there a real world example of using QTcpSocket?

    1 Reply Last reply
    0
    • L Offline
      L Offline
      liuyanghejerry
      wrote on last edited by
      #2

      Any suggestions here?

      1 Reply Last reply
      0
      • K Offline
        K Offline
        koahnig
        wrote on last edited by
        #3

        You need to have a look to the "different member functions and signals supplied by QTcpSocket":http://qt-project.org/doc/qt-4.8/qtcpsocket-members.html
        Certainly there is the possibility of checking the socket with a timer trigger. However, that is not required. QTcpSocket, respectively one of the base classes has its own trigger. Have a look to the "readyRead() signal.":http://qt-project.org/doc/qt-4.8/qiodevice.html#readyRead

        Vote the answer(s) that helped you to solve your issue(s)

        1 Reply Last reply
        0
        • L Offline
          L Offline
          liuyanghejerry
          wrote on last edited by
          #4

          [quote author="koahnig" date="1344073423"]You need to have a look to the "different member functions and signals supplied by QTcpSocket":http://qt-project.org/doc/qt-4.8/qtcpsocket-members.html
          Certainly there is the possibility of checking the socket with a timer trigger. However, that is not required. QTcpSocket, respectively one of the base classes has its own trigger. Have a look to the "readyRead() signal.":http://qt-project.org/doc/qt-4.8/qiodevice.html#readyRead[/quote]

          Well, thanks. But I've already connected with that signal to receive new data.
          The problem I met, is that I was lack of a good way to combine the data pieces without losing data.
          The code snippet I show in the first thread is what I used to send and receive data. The second function is already connected with readyRead() signal, but I think there may something wrong in it leading to losing data.
          Or, is there a "standard way" to handle this?

          1 Reply Last reply
          0
          • K Offline
            K Offline
            koahnig
            wrote on last edited by
            #5

            There are two possibilities to go around this.

            • Do not read, if there are not enough bytes available. That is most of the time only applicable with fixed record length.
            • QTcpSocket inherits from QIODevice. QIODevice has methods seek and pos but they are not present on sequential devices such as QTcpSocket. So you have to write your own buffer and place all bytes received into that buffer.

            Vote the answer(s) that helped you to solve your issue(s)

            1 Reply Last reply
            0
            • L Offline
              L Offline
              liuyanghejerry
              wrote on last edited by
              #6

              Thanks, I'll check them.

              1 Reply Last reply
              0
              • F Offline
                F Offline
                franku
                wrote on last edited by
                #7

                Koahnig, could there be another reason? Please have a look at the code, I do not understand several things.

                @
                void DataSocket::newData() <-- this slot is called in an exec() eventloop.
                {
                if(state() == QAbstractSocket::ConnectedState){
                if( bytesAvailable() < (qint64)sizeof(quint32) ) return;
                QDataStream in(this);
                if(!dataSize)in >> dataSize;
                if(bytesAvailable() < dataSize) {
                return;
                }
                QByteArray array;
                array.resize(dataSize);
                in >> array;
                // array = qUncompress(array);

                    emit newData(array); <-- this is signal evaluated in an event handler why send signal with the same name as this funtion?
                    dataSize = 0;
                    if( bytesAvailable() ) newData(); <-- while this is true there's no event handler being called
                  }
                

                }@

                As long as the slots are not evaluated in the relevant exec() event handler there will no data be sent since the IODevice uses buffered I/O. I think, if you program this way you need to be shure, when each slot is evaluated and in what thread.

                liuyanghejer.. I want to ask what you mean with

                bq. but it’s not good enough that ...

                ? What is not good enough and what do you want to achieve?

                This, Jen, is the internet.

                1 Reply Last reply
                0
                • L Offline
                  L Offline
                  liuyanghejerry
                  wrote on last edited by
                  #8

                  Well, I have the definition below:
                  @
                  class DataSocket : public QTcpSocket
                  {
                  Q_OBJECT
                  public:
                  explicit DataSocket(QObject *parent = 0);
                  ~DataSocket();

                  signals:
                  void newData(QByteArray);

                  public slots:
                  void sendData(const QByteArray &content);
                  void newData();

                  protected:
                  quint32 dataSize;
                  };
                  @

                  The newData() is a slot while the newData(QByteArray) is a signal. This is still ok due to different function signature, though not really good.

                  Since the DataSocket class is actually QTcpSocket, code beyond is all running in main thread.

                  However, QTcpSocket also manage another thread that receiving network data. Even the main thread is already run in newData() slot, new data may come at the same time. If this really happens, I doubt that the signal will be emitted.

                  Code:
                  @
                  if( bytesAvailable() ) newData();
                  @

                  This is used for proceeding the new data.

                  What I want to achieve is to prevent using QTimer. Because QTimer is much more heavier than only a function call.

                  Let's see another socket code I copied from PokemonOnline(they use qt , https://github.com/coyotte508/pokemon-online):

                  @
                  void Network::onReceipt()
                  {
                  if (commandStarted == false) {
                  /* There it's a new message we are receiving.
                  To start receiving it we must know its length, i.e. the 2 first bytes /
                  if (this->bytesAvailable() < 4) {
                  return;
                  }
                  /
                  Ok now we can start /
                  commandStarted=true;
                  /
                  getting the length of the message /
                  char c1, c2, c3, c4;
                  this->getChar(&c1), this->getChar(&c2); this->getChar(&c3), this->getChar(&c4);
                  remainingLength= (uchar(c1) << 24) + (uchar(c2) << 16) + (uchar(c3) << 8) + uchar(c4);
                  /
                  Recursive call to write less code =) /
                  onReceipt();
                  } else {
                  /
                  Checking if the command is complete! /
                  if (this->bytesAvailable() >= remainingLength) {
                  emit isFull(read(remainingLength));
                  commandStarted = false;
                  /
                  Recursive call to spare code =), there may be still data pending */
                  onReceipt();
                  }
                  }
                  }
                  @

                  Leave the protocol(their own) code along. It also calls onReceipt() again to deal with pending data. This is a common way I think.

                  1 Reply Last reply
                  0
                  • K Offline
                    K Offline
                    koahnig
                    wrote on last edited by
                    #9

                    [quote author="franku" date="1344373695"]Koahnig, could there be another reason? Please have a look at the code, I do not understand several things.
                    [/quote]
                    That is for sure. There is only a code fragment given. My answers assume that the basics are handled ok. However, that seems to be the case judging the responses.

                    IMHO it is the common problem, that readyRead signals are triggered before the complete information arrived. E.g. a bulk of information is arriving, let's say 1kB, but the readyRead is triggered within the first few hundred bytes. bytesAvailable will indicate only e.g. 400 Bytes. Even so, that the buffer is filled during execution of your slot and you are checking again before leaving the slot, you may read another couple hundred Bytes. However, there is always a likelihood to that the end did not arrive yet.

                    [quote author="koahnig" date="1344160277"]There are two possibilities to go around this.

                    • Do not read, if there are not enough bytes available. That is most of the time only applicable with fixed record length.
                    • QTcpSocket inherits from QIODevice. QIODevice has methods seek and pos but they are not present on sequential devices such as QTcpSocket. So you have to write your own buffer and place all bytes received into that buffer. [/quote]

                    Those are the consequences then.
                    Well, fixed length may be interpret also that there is some information in the being of the block telling you the size of the block. You can enter a loop and wait until the information is complete.
                    The other is certainly doing your own buffering.

                    The first implementation has one big disadvantage. The routine gets stopped when the connection breaks during waiting for more data. In general it works fine besides this unpleasant effect.

                    The second option is preferable.

                    [quote author="liuyanghejerry" date="1344403700"]@
                    void Network::onReceipt()
                    {
                    if (commandStarted == false) {
                    /* There it's a new message we are receiving.
                    To start receiving it we must know its length, i.e. the 2 first bytes /
                    if (this->bytesAvailable() < 4) {
                    return;
                    }
                    /
                    Ok now we can start /
                    commandStarted=true;
                    /
                    getting the length of the message /
                    char c1, c2, c3, c4;
                    this->getChar(&c1), this->getChar(&c2); this->getChar(&c3), this->getChar(&c4);
                    remainingLength= (uchar(c1) << 24) + (uchar(c2) << 16) + (uchar(c3) << 8) + uchar(c4);
                    /
                    Recursive call to write less code =) /
                    onReceipt();
                    } else {
                    /
                    Checking if the command is complete! /
                    if (this->bytesAvailable() >= remainingLength) {
                    emit isFull(read(remainingLength));
                    commandStarted = false;
                    /
                    Recursive call to spare code =), there may be still data pending */
                    onReceipt();
                    }
                    }
                    }
                    @
                    [/quote]

                    The basic thought is ok in my opinion. However, IMHO I would not do the recursion. With the recursion it is an implementation equivalent to the first option.
                    One simple modification would be to replace the call (recursion to onReceipt) with a return. The next entry of onReceipt would be triggered by the next readyRead. When the remainingLength is already/still available you can check if enough bytes are now available. So, the buffering would not be done with additional memory, but handled by QTcpSocket.

                    Vote the answer(s) that helped you to solve your issue(s)

                    1 Reply Last reply
                    0
                    • L Offline
                      L Offline
                      liuyanghejerry
                      wrote on last edited by
                      #10

                      Will next readyRead be emitted even if the new data comes when onReceipt() still running?

                      1 Reply Last reply
                      0
                      • K Offline
                        K Offline
                        koahnig
                        wrote on last edited by
                        #11

                        [quote author="liuyanghejerry" date="1344430313"]Will next readyRead be emitted even if the new data comes when onReceipt() still running?[/quote]

                        My knowledge of the mechanisms is very limited. Would assume that the event will be put on the stack and processed when others are finished. But i never tried to monitor.

                        Vote the answer(s) that helped you to solve your issue(s)

                        1 Reply Last reply
                        0
                        • L Offline
                          L Offline
                          liuyanghejerry
                          wrote on last edited by
                          #12

                          [quote author="koahnig" date="1344431899"][quote author="liuyanghejerry" date="1344430313"]Will next readyRead be emitted even if the new data comes when onReceipt() still running?[/quote]

                          My knowledge of the mechanisms is very limited. Would assume that the event will be put on the stack and processed when others are finished. But i never tried to monitor. [/quote]

                          I tried and it shows I can't remove that line, or data will loss a lot if traffic is heavy.

                          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