[Solved]Qt Client-Server App send QString from Client to Server



  • I'm currently trying to write a client server application in Qt. I started out with the "Fortune Client":http://qt-project.org/doc/qt-4.8/network-fortuneclient.html and the "Threaded Fortune Server":http://qt-project.org/doc/qt-4.8/network-threadedfortuneserver.html. At this point I need to do the following:
    The client sends to the server one QString, containing the name of the command the server should execute ,and then other various QString containing user input;
    The server (the thread) reads the first QString, and then, based on user input, interogates a table in a certain way and sends the output back to the user (QStrings).
    So far I can send the table from the server to the client. I do not know how to send the user input from the client to the server.
    @
    void MyClient::getServerInfo()
    {
    Qstring item1, item2;
    //QString testString = "aTest";
    //QByteArray block;
    //QDataStream out(&block, QIODevice::WriteOnly);
    //out.setVersion(QDataStream::Qt_4_0);
    //out << (quint16)0;
    //out << testString;
    //out.device()->seek(0);
    //out << (quint16)(block.size()-sizeof(quint16));
    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_4_0);
    if(blockSize == 0)
    {
    if(tcpSocket->bytesAvailable() < (int)sizeof(quint16))
    return;
    in >> blockSize;
    }
    if(tcpSocket->bytesAvailable() < blockSize)
    return;
    int row = 0;
    tableWidget->clearContents();
    while(1)
    {
    in >> item1 >> item2;
    if (item1 == "") break;
    QTableWidgetItem *item0 = new QTableWidgetItem(item1);
    QTableWidgetItem *item1 = new QTableWidgetItem(item2);
    tableWidget->setItem(row, 0, item0);
    tableWidget->setItem(row, 1, item1);
    row++;
    }
    }
    @
    @
    void MyThread::run()
    {
    QTcpSocket tcpSocket;
    if(!tcpSocket.setSocketDescriptor(socketDescriptor))
    {
    emit error(tcpSocket.error());
    return;
    }
    //QString testString;
    //QDataStream in(&tcpSocket);
    //in.setVersion(QDataStream::Qt_4_0);
    //if(blockSize == 0)
    //{
    // if(tcpSocket.bytesAvailable() < (int)sizeof(quint16))
    // return;
    // in >> blockSize;
    //}
    //if(tcpSocket.bytesAvailable() < blockSize)
    // return;
    //in >> testString;
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    QSqlQuery myQuery;
    if(myQuery.exec("SELECT item1, item2 FROM mytable"))
    {
    while(myQuery.next())
    {
    item1 = myQuery.value(0).toString();
    item2 = myQuery.value(1).toString();
    out << item2 << item2;
    }
    }
    else
    out.device()->seek(0);
    out << (quint16)(block.size()-sizeof(quint16));
    tcpSocket.write(block);
    tcpSocket.disconnectFromHost();
    tcpSocket.waitForDisconnected();
    }
    @

    The following code works without the commented code. Knowing that I can succesfully send data from server to the client I tried to do the same thing in reverse, but I must be missing something. None of the two applications crash, but the table on the client side stays empty instead of displaying the data received from the server.


  • Moderators

    welcome to devnet

    when a client connects to the server, you are using a QTcpSocket. When the QTcpSocket of your client connects, QTcpServer in your server application provides you a pointer to a QTcpSocket.
    So you have basically in each application a QTcpSocket which allows the communication. Whatever you do to your QTcpSocket in your client to be able to receive information from a server, you have to also to the QTcpSocket (using its pointer) in the server. Checkout the different signals as used in the client example and basically do the same in your server app. That's it.

    The only difference between a server and a client app is that at the client side you are connecting actively your QTcpSocket to a server. In the server app you are waiting for connections and QTcpServer delivers you a QTcpSocket already connected. All other stuff following is the same on both sides.



  • Thank you for your reply.

    I was able to figure out what exactly is bothering me: I don't understand the part about prefixing the message with its length. There's also the part where the two QDataStreams differ:
    @
    QDataStream in(tcpSocket);
    QDataStream out(&block, QIODevice::WriteOnly);
    @

    Because the declaration of out contains nothing about tcpSocket I assumed that
    @
    tcpSocket.write(block);
    @

    also, somehow, sends the information stored in out as well, because I see no other instruction that links "out" with tcpSocket.

    I'm currently trying to to things symetrically, in a simplified manner. My code looks like this:
    @
    Client()
    {
    QString testStream = "clienttest";
    QDataStream stream(tcpSocket);
    stream.setVersion(QDataStream::Qt_4_0);
    stream << testStream;
    }
    @
    @
    Server()
    {
    QString testString;
    QDataStream stream(tcpSocket);
    stream.setVersion(QDataStream::Qt_4_0);
    stream >> testString;
    qDebug() << testString;
    }
    @

    qDebug prints "" instead of "clientTest".

    Therefore my problem lies qith QDataStreams and QByteArray and how to prefix messages with their length when needed.


  • Moderators

    Somehow you have to define the length of a string. In C you are using a NULL character for string termination. This is the simplest way to define a string. You simply need one byte in addition to the actual data content. QString and also std::string stores to the actual data somehow the actual length.

    No for sending information you need to have a similar mechanic. The same for other means like writing to a file and so on. Either you have some mechanic and its definition on how to recognize the actual length of your string. There are different ways to do so. E.g. you can use double quotes or any other terminating character. However, this brings the problem since you cannot have the terminating character within your string. Therefore, for transfer, but also for writing you may want to place the length of the string first. When reading, regardless of file or socket, you know the number of bytes to expect.

    I do not use QDataStream, but from the example in the detailed description can see that there is no need to thing about the length of the string.
    QDataStream is doing that "magic" for you.
    When sending the data over any line, how should the other side know the mechanics you are using. Even if you are using QDataStream and send it over a line, you need to know on the other side how many bytes to expect. Therefore, I do not think that your example code with QDataStream and tcpsocket can work at all. However, as stated I never use QDataStream.

    Personally, I prefer more transparency anyhow. Sending the number of bytes (e.g. as int16 or int32) allows you on the other side to allocate enough space and read the data in blocks or at once. Therefore, I would not use QDataStream at all. The internal magic must give you some overhead for the length of the string. So filling the string into a QByteArray and send this as in the first example is the best solution.

    The simple route as in your second post without getting the length and data allocation is not possible.



  • I solved it. The problem with the first version of the code was that getServerInfo() was connected to the readyRead() , but there was waitForReadyRead() in the run() function. Therefore the code now looks something like this :
    @
    Client()
    {
    //send data

    if(tcpServer->waitForReadyRead())
    {
            //read data
    }
    

    }

    Server()
    {
    if(tcpServer->waitForReadyRead())
    {
    //read data
    }
    //send data
    }
    @

    Thank you for your post. I will look further into QByte Array and see if I can work with that, as the current solution is rather hybrid-ish for my tastes.


  • Moderators

    Glad to hear that you have solved the problem.

    The important part is getting the general understanding.
    I limited my I/O access to QTcpSocket to reads and writes of bytes.

    Please mark your initial post with [solved] in the title line. This helps others to see that there might be solution. There is a greyed-green edit at the end of your first post. Click there and you can edit the first post including the title.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.