[Solved] Using QTcpSocket::write(QByteArray) several times - only sends the first time.



  • Following scenario: I want to be able to send and recieve messages via QTcpSocket from a server to a client and vice versa. To do this, I implemented several methods to be able to encode different objects into a QDataStream. The client connects to the server, the server stores the respective QTcpSocket and is then ready to send a recieve messages.

    The methods utilize this schema:

    @void ServerConnection::send( Player *reciever, QString identifier, QString text ) {

    QTcpSocket *client = getClient( reciever ); // simply looks up which QTcpSocket goes with the corresponding player. No fancy implementation details here.
    
    if ( client == 0 ) {
        return;
    }
    
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_0);
    out << (quint16)0;
    
    // send an identifier
    out << identifier;
    
    // send the data
    out << text;
    
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));
    
    client->write(block);
    

    }@

    The client responds in this way to messages sent to it:

    @void ClientConnection::readNewData() {

    qint16 blockSize = 0;
    
    QDataStream in(_tcpSocket);
    in.setVersion(QDataStream::Qt_5_0);
    
    if (blockSize == 0) {
        if (_tcpSocket->bytesAvailable() < (int)sizeof(quint16))
            return;
    
        in >> blockSize;
    }
    
    if (_tcpSocket->bytesAvailable() < blockSize)
        return;
    
    QString identification;
    in >> identification;
    
    if ( identification == "Request" ) {
    
        QString type;
        in >> type;
    
        if ( type == "Identification" ) {
            send((QString)"Identification", (QString)"Emerald Player");
        } else if ( type == "CompanyName") {
            send((QString)"CompanyName", _player->companyName());
        } else if ( type == "CEOName" ) {
            send((QString)"CEOName", _player->ceoName());
        } else {
            qDebug() << "Recieved unknown Request";
            qDebug() << type;
        }
    } else if ( identification == "Player") {
        _player->decode(&in);
    } else if ( identification == "Finance") {
    
    } else if (/* yatta yatta - several differente else-ifs here */) {
    
    } else {
        qDebug() << "Recieved unknown Identification token.";
        qDebug() << identification;
    }
    

    }
    @

    Now, I want to invoke the send method several times, e.g., like this:

    @void ServerConnection::doStuff() {

    // do irrelevant stuff
    
    send(player, (QString)"Request", (QString)"CompanyName");
    send(player, (QString)"Request", (QString)"CEOName");
    

    }@

    As you can see, I just invoke the sending process twice. However, Qt decides to send only once. Both send commands work perfectly on their own, invoking them right after each other leads to only the first one beeing sent and recieved.

    I tried to add client->flush() right after client->write(block) to no avail, I tried to invoke client->waitForBytesWritten() right before client->write(block) and I tried both, to no avail. I propably didn't quite understand how waitForBytesWritten() works, so I guess I'm not utilizing it right. However, I fail to see how exactely this is to be done.

    I cannot just add these two messages into one, simply because I want to be able to encode lots of different objects and I need to be flexible on when sending what.

    Any help explaining how to make sure I can utilize several write() commands after each other would be extremely appreciated.



  • Try put
    @client->waitForBytesWritten(3000);@
    after
    @client->write(block);@
    And look what happened.



  • In my opinion better use request-response type communications. But correct me if i'm wrong :).



  • Tried to use waitForBytesWritten() after sending, no change.

    Can you elaborate your second statement? Do you mean I should explicitely wait until I got a response until I send again?



  • The data is probably sent correctly, but the way your readNewData function is written means it can only treats one complete message.
    As TCP and Qt doesn't guarantee that "1 sent message = 1 readyRead signal", you need to handle the cases where you don't receive a complete message, and where you receive several messages for the same signal.

    @void ClientConnection::readNewData() {
    /* this has to be a class member, initialized to 0 when the connection opens /
    /
    quint16 blockSize = 0; */

    QDataStream in(_tcpSocket);
    in.setVersion(QDataStream::Qt_5_0);
    
    while(1) { 
    
        if (blockSize == 0) {
            if (_tcpSocket->bytesAvailable() < (int)sizeof(blockSize))
                return;
    
            in >> blockSize;
        }
    
        if (_tcpSocket->bytesAvailable() < blockSize)
            return;
    
       // read your message here
       // ...
    
       // reset for the next message       
       blockSize = 0;
    }
    

    }@



  • [quote author="Aerius" date="1360849229"]Do you mean I should explicitely wait until I got a response until I send again?[/quote]

    Well, it is referred.
    Look at incoming data probably you already read both messages at once.

    edit
    As alexisdm already wrote :).



  • [quote author="alexisdm" date="1360849664"]
    As TCP and Qt doesn't guarantee that "1 sent message = 1 readyRead signal"
    [/quote]

    Thank you, this was the core information I was missing. Your implementation also works perfectly, so thanks for that as well. :)





  • If you're passing a limited set of short messages, perhaps you can also consider using Qxt's RPC stuff. It allows you just use signals and slots across a network connection.



  • Seems Qxt is a really nice stuff. Andre have you try use any modules from it?



  • Yes, I have used it in the past.



  • Thanks for the tip Andre, I'll look into it. For the most part though, I'll be encoding and decoding objects of various sizes (see lines 36 to 41 in the second snip of code) and send them, so I guess the mechanism won't help me in that regard.

    However, the mechanism sounds interesting, so maybe I can use that wherever I don't need to send whole objects across the network.


Log in to reply
 

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