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. QUdpSocket and QDataStream

QUdpSocket and QDataStream

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 2 Posters 1.8k 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.
  • l3u_L Offline
    l3u_L Offline
    l3u_
    wrote on last edited by
    #1

    Hi :-)

    I have a program with server-client communication via JSON data. I use TCP for this, and encapsulate the JSON data in QDataStreams, according to the (sadly inofficial) simple chat example. If I got it right, this is to ensure that the data is correct and complete, after a QDataStream transaction could be finished successfully.

    Recently, I added a second small server that answers UDP broadcasts. The purpose is that a client can discover a server, it broadcasts a JSON message and the server answers with it's socket address.

    Now, UDP is unreliable and connectionless. I wonder if encapsulating the JSON data in a QDataStream is also meaningful if an UDP socket is used, or if a datagram that is smaller than the maximum packet size of 65,507 bytes will be delivered as-is (or not at all) anyway.

    Thanks for all enlightenment ;-)

    VRoninV 1 Reply Last reply
    0
    • l3u_L l3u_

      Hi :-)

      I have a program with server-client communication via JSON data. I use TCP for this, and encapsulate the JSON data in QDataStreams, according to the (sadly inofficial) simple chat example. If I got it right, this is to ensure that the data is correct and complete, after a QDataStream transaction could be finished successfully.

      Recently, I added a second small server that answers UDP broadcasts. The purpose is that a client can discover a server, it broadcasts a JSON message and the server answers with it's socket address.

      Now, UDP is unreliable and connectionless. I wonder if encapsulating the JSON data in a QDataStream is also meaningful if an UDP socket is used, or if a datagram that is smaller than the maximum packet size of 65,507 bytes will be delivered as-is (or not at all) anyway.

      Thanks for all enlightenment ;-)

      VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by
      #2

      @l3u_ said in QUdpSocket and QDataStream:

      I wonder if encapsulating the JSON data in a QDataStream is also meaningful if an UDP socket

      It is. But it does nothing to guarantee the integrity of the data. The easiest way to do that is to send after the main message a checksum of the data itself. Using this example as a starting point (which I think is what you refer to in your question), you could change ChatClient::sendMessage to:

      void ChatClient::sendMessage(const QString &text)
      {
          if (text.isEmpty())
              return; // We don't send empty messages
          // create a QDataStream operating on the socket
          QDataStream clientStream(m_clientSocket);
          // set the version so that programs compiled with different versions of Qt can agree on how to serialise
          clientStream.setVersion(QDataStream::Qt_5_7);
          // Create the JSON we want to send
          QJsonObject message;
          message["type"] = QStringLiteral("message");
          message["text"] = text;
      // change from here on
      const QByteArray dataToSend = QJsonDocument(message).toJson(QJsonDocument::Compact);
      clientStream << dataToSend << QCryptographicHash::hash(dataToSend,QCryptographicHash::Md5);
      }
      

      and change ChatClient::onReadyRead to

      void ChatClient::onReadyRead()
      {
          // prepare a container to hold the UTF-8 encoded JSON we receive from the socket
          QByteArray jsonData;
      QByteArray jsonDataCheckSum; //changed here
          // create a QDataStream operating on the socket
          QDataStream socketStream(m_clientSocket);
          // set the version so that programs compiled with different versions of Qt can agree on how to serialise
          socketStream.setVersion(QDataStream::Qt_5_7);
          // start an infinite loop
          for (;;) {
              // we start a transaction so we can revert to the previous state in case we try to read more data than is available on the socket
              socketStream.startTransaction();
              // we try to read the JSON data 
              socketStream >> jsonData >> jsonDataCheckSum;
              if (socketStream.commitTransaction()) {
      //changed from here
      if(QCryptographicHash::hash(jsonData ,QCryptographicHash::Md5)!=jsonDataCheckSum){
      // the data was corrupted!
      continue;
      }
      // to here
                  // we successfully read some data
                  // we now need to make sure it's in fact a valid JSON
                  QJsonParseError parseError;
                  // we try to create a json document with the data we received
                  const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
                  if (parseError.error == QJsonParseError::NoError) {
                      // if the data was indeed valid JSON
                      if (jsonDoc.isObject()) // and is a JSON object
                          jsonReceived(jsonDoc.object()); // parse the JSON
                  }
                  // loop and try to read more JSONs if they are available
              } else {
                  // the read failed, the socket goes automatically back to the state it was in before the transaction started
                  // we just exit the loop and wait for more data to become available
                  break;
              }
          }
      }
      

      "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

      l3u_L 2 Replies Last reply
      3
      • VRoninV VRonin

        @l3u_ said in QUdpSocket and QDataStream:

        I wonder if encapsulating the JSON data in a QDataStream is also meaningful if an UDP socket

        It is. But it does nothing to guarantee the integrity of the data. The easiest way to do that is to send after the main message a checksum of the data itself. Using this example as a starting point (which I think is what you refer to in your question), you could change ChatClient::sendMessage to:

        void ChatClient::sendMessage(const QString &text)
        {
            if (text.isEmpty())
                return; // We don't send empty messages
            // create a QDataStream operating on the socket
            QDataStream clientStream(m_clientSocket);
            // set the version so that programs compiled with different versions of Qt can agree on how to serialise
            clientStream.setVersion(QDataStream::Qt_5_7);
            // Create the JSON we want to send
            QJsonObject message;
            message["type"] = QStringLiteral("message");
            message["text"] = text;
        // change from here on
        const QByteArray dataToSend = QJsonDocument(message).toJson(QJsonDocument::Compact);
        clientStream << dataToSend << QCryptographicHash::hash(dataToSend,QCryptographicHash::Md5);
        }
        

        and change ChatClient::onReadyRead to

        void ChatClient::onReadyRead()
        {
            // prepare a container to hold the UTF-8 encoded JSON we receive from the socket
            QByteArray jsonData;
        QByteArray jsonDataCheckSum; //changed here
            // create a QDataStream operating on the socket
            QDataStream socketStream(m_clientSocket);
            // set the version so that programs compiled with different versions of Qt can agree on how to serialise
            socketStream.setVersion(QDataStream::Qt_5_7);
            // start an infinite loop
            for (;;) {
                // we start a transaction so we can revert to the previous state in case we try to read more data than is available on the socket
                socketStream.startTransaction();
                // we try to read the JSON data 
                socketStream >> jsonData >> jsonDataCheckSum;
                if (socketStream.commitTransaction()) {
        //changed from here
        if(QCryptographicHash::hash(jsonData ,QCryptographicHash::Md5)!=jsonDataCheckSum){
        // the data was corrupted!
        continue;
        }
        // to here
                    // we successfully read some data
                    // we now need to make sure it's in fact a valid JSON
                    QJsonParseError parseError;
                    // we try to create a json document with the data we received
                    const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
                    if (parseError.error == QJsonParseError::NoError) {
                        // if the data was indeed valid JSON
                        if (jsonDoc.isObject()) // and is a JSON object
                            jsonReceived(jsonDoc.object()); // parse the JSON
                    }
                    // loop and try to read more JSONs if they are available
                } else {
                    // the read failed, the socket goes automatically back to the state it was in before the transaction started
                    // we just exit the loop and wait for more data to become available
                    break;
                }
            }
        }
        
        l3u_L Offline
        l3u_L Offline
        l3u_
        wrote on last edited by
        #3

        @VRonin Thanks for the detailed example! But doesn't the QDataStream itself add some checksum to the data? I thought it would do … because I tested my server/client connection with netem, really f**king up the connection with corrupted packets, packet loss, duplication, delay and do on, and the only thing that happened was that it took longer. But everything still worked. Or was that due to TCP being used?

        1 Reply Last reply
        0
        • VRoninV VRonin

          @l3u_ said in QUdpSocket and QDataStream:

          I wonder if encapsulating the JSON data in a QDataStream is also meaningful if an UDP socket

          It is. But it does nothing to guarantee the integrity of the data. The easiest way to do that is to send after the main message a checksum of the data itself. Using this example as a starting point (which I think is what you refer to in your question), you could change ChatClient::sendMessage to:

          void ChatClient::sendMessage(const QString &text)
          {
              if (text.isEmpty())
                  return; // We don't send empty messages
              // create a QDataStream operating on the socket
              QDataStream clientStream(m_clientSocket);
              // set the version so that programs compiled with different versions of Qt can agree on how to serialise
              clientStream.setVersion(QDataStream::Qt_5_7);
              // Create the JSON we want to send
              QJsonObject message;
              message["type"] = QStringLiteral("message");
              message["text"] = text;
          // change from here on
          const QByteArray dataToSend = QJsonDocument(message).toJson(QJsonDocument::Compact);
          clientStream << dataToSend << QCryptographicHash::hash(dataToSend,QCryptographicHash::Md5);
          }
          

          and change ChatClient::onReadyRead to

          void ChatClient::onReadyRead()
          {
              // prepare a container to hold the UTF-8 encoded JSON we receive from the socket
              QByteArray jsonData;
          QByteArray jsonDataCheckSum; //changed here
              // create a QDataStream operating on the socket
              QDataStream socketStream(m_clientSocket);
              // set the version so that programs compiled with different versions of Qt can agree on how to serialise
              socketStream.setVersion(QDataStream::Qt_5_7);
              // start an infinite loop
              for (;;) {
                  // we start a transaction so we can revert to the previous state in case we try to read more data than is available on the socket
                  socketStream.startTransaction();
                  // we try to read the JSON data 
                  socketStream >> jsonData >> jsonDataCheckSum;
                  if (socketStream.commitTransaction()) {
          //changed from here
          if(QCryptographicHash::hash(jsonData ,QCryptographicHash::Md5)!=jsonDataCheckSum){
          // the data was corrupted!
          continue;
          }
          // to here
                      // we successfully read some data
                      // we now need to make sure it's in fact a valid JSON
                      QJsonParseError parseError;
                      // we try to create a json document with the data we received
                      const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
                      if (parseError.error == QJsonParseError::NoError) {
                          // if the data was indeed valid JSON
                          if (jsonDoc.isObject()) // and is a JSON object
                              jsonReceived(jsonDoc.object()); // parse the JSON
                      }
                      // loop and try to read more JSONs if they are available
                  } else {
                      // the read failed, the socket goes automatically back to the state it was in before the transaction started
                      // we just exit the loop and wait for more data to become available
                      break;
                  }
              }
          }
          
          l3u_L Offline
          l3u_L Offline
          l3u_
          wrote on last edited by
          #4

          @VRonin I think the code you posted only works for a QTcpSocket. I can't do QDataStream socketStream(m_clientSocket) with a QUdpSocket, I have to read the data using readDatagram() …

          VRoninV 1 Reply Last reply
          0
          • l3u_L l3u_

            @VRonin I think the code you posted only works for a QTcpSocket. I can't do QDataStream socketStream(m_clientSocket) with a QUdpSocket, I have to read the data using readDatagram() …

            VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by VRonin
            #5

            @l3u_ said in QUdpSocket and QDataStream:

            I have to read the data using readDatagram()

            You don't if you use connectToHost but given you are reporting the problem I'll assume you don't want to go down that route.
            What you can do is use while(hasPendingDatagrams()) and readDatagram to store the data on a temporary buffer (a QByteArray) before using datastream on the buffer and use QByteArray::mid to discard bytes that were already processed

            "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

            l3u_L 1 Reply Last reply
            3
            • VRoninV VRonin

              @l3u_ said in QUdpSocket and QDataStream:

              I have to read the data using readDatagram()

              You don't if you use connectToHost but given you are reporting the problem I'll assume you don't want to go down that route.
              What you can do is use while(hasPendingDatagrams()) and readDatagram to store the data on a temporary buffer (a QByteArray) before using datastream on the buffer and use QByteArray::mid to discard bytes that were already processed

              l3u_L Offline
              l3u_L Offline
              l3u_
              wrote on last edited by l3u_
              #6

              Oh, I thought I couldn't even connectToHost with an UDP socket … thanks for pointing this out ;-)

              But I think I can't use it anyway, because the infrastructure of what I do is quite simple: The client does an UDP broadcast with "Is there a server out there?". So I can't connectToHost here, because I don't know it yet. If the server receives such a broadcast, it answers with "Here I am, this is my socket address" and sends the data back to the sender's address.

              So, speaking of the answer, I actually could do a connectToHost, but only for this one … and at the moment, both the server and the client are derived from the same abstract UDP communication class, so I would have to write more code just to handle half of the (frugal) communication with a "real" connection …

              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