Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    Unsolved QUdpSocket and QDataStream

    General and Desktop
    2
    6
    1085
    Loading More Posts
    • 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_
      l3u_ last edited by

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

      VRonin 1 Reply Last reply Reply Quote 0
      • VRonin
        VRonin @l3u_ last edited by

        @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_ 2 Replies Last reply Reply Quote 3
        • l3u_
          l3u_ @VRonin last edited by

          @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 Reply Quote 0
          • l3u_
            l3u_ @VRonin last edited by

            @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() …

            VRonin 1 Reply Last reply Reply Quote 0
            • VRonin
              VRonin @l3u_ last edited by 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

              "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_ 1 Reply Last reply Reply Quote 3
              • l3u_
                l3u_ @VRonin last edited by l3u_

                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 Reply Quote 0
                • First post
                  Last post