0

I'm new on protobuf and QTcpServer/Socket and I want to read my .proto data send by my client, but when i'm reading the data, the QString done is empty

For now, I just want to send a message that say "hello" when my client is connected. The QByteArray returning by QTcpSocket::readAll is NOT empty, but the QString created with the bytes is empty.

Here is my .proto ultra basic one :

syntax = "proto3";

package protobuf;
message Message
{
    string content = 2;
}

write functions :

// When i'm connecting to the server i create a PlayerManager and i call this function with message = "hello"
void Server::sendMessageToPlayer(const PlayerManager& playerManager, const QString& message)
{
    auto messageProto = new protobuf::Message;
    messageProto->set_content(message.toStdString());

    playerManager.socketManager()->sendData(*messageProto);
}

// I serialize the protobuf
template <typename protobufType>
void sendData(const protobufType& protobuf)
{
    std::string dataToSend;
    if (!protobuf.SerializeToString(&dataToSend))
    {
        // This never pass -> the protobuf is well serialize 
        qDebug() << "The protobuf send cannot be serialized, please, make sure that you used protobufs correctly";
    }

    // Before i write it
    write(dataToSend);
}

void SocketManager::write(const std::string& data)
{
    // I tried this but it's not working either
    // QTextCodec* codec     = QTextCodec::codecForName("CP1251");
    // QString     codecData = codec->toUnicode(data.c_str());

    QByteArray  block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_10);

    out << data.c_str();
    _tcpSocket->write(block);
    qDebug() << block;
}

read functions :

void SocketManager::read()
{
    QByteArray bytes = _tcpSocket->readAll();
    qDebug() << bytes;

    // Doesn't work either
    // QTextCodec* codec = QTextCodec::codecForName("CP1251");
    // QString     data  = codec->toUnicode(bytes);
    QString     data(bytes);
    qDebug() << data;

    emitMessageType(data.toStdString());
}

void SocketManager::emitMessageType(const std::string& data)
{
    // Protobufs can parse an empty string (and so, emit signal), to avoid that, the function will tell you if data
    // are empty, then return
    if (data.empty())
    {
        qDebug() << "Datas are empty";
        return;
    }

    protobuf::Message message;
    if (message.ParseFromString(data))
    {
        emit messageProtoReceived(message);
        return;
    }

    qDebug() << "The data send cannot be translate, please, make sure that you used protobufs correctly";
}

So, I would like that my client receive "hello" when he is connected but my debug are :

Server side :

"\x00\x00\x00\b\x12\x05hello\x00"

Client side

"\x00\x00\x00\b\x12\x05hello\x00"
""
Datas are empty

When I use QTextCodec (commented lines of the code) the debug are :

Server side :

"\x00\x00\x00\x0E\x00\x12\x00\x05\x00h\x00""e\x00l\x00l\x00o"

Client side

"\x00\x00\x00\x0E\x00\x12\x00\x05\x00h\x00""e\x00l\x00l\x00o"
"\u0000\u0000\u0000\u000E\u0000\u0012\u0000\u0005\u0000h\u0000e\u0000l\u0000l\u0000o"
The data send cannot be translate, please, make sure that you used protobufs correctly

So the QByteArea is parse, but protobuf don't succeed to parse the given string.

Thanks for reading, I hope youc ould help me

Ephesiel
  • 119
  • 9
  • `out << data.c_str()` inside write function might discard at first `\0`. Try converting to `QByteArray` as described here: https://stackoverflow.com/questions/10767760/correct-way-to-losslessly-convert-to-and-from-stdstring-and-qbytearray to ensure whole string is sent via socket. – Zaiborg Jul 31 '19 at 07:59
  • 1
    `QByteArray block(data.c_str(), data.length());` `QDataStream out(&block, QIODevice::WriteOnly);` `out.setVersion(QDataStream::Qt_5_10);` `_tcpSocket->write(block);` This is working ty ! Could you answer, I will mark it as accepted – Ephesiel Jul 31 '19 at 08:10

2 Answers2

1

Not sure if it's the case but the documentation says that the default constructor of a QString that takes a QByteArray:

The given byte array is converted to Unicode using fromUtf8(). Stops copying at the first 0 character, otherwise copies the entire byte array.

So probably, you are having some troubles whit the conversion.

As alternative, you can try ParseFromArray method, instead of converting a QByteArray into a std::string.

const auto byteArray = _tcpSocket->readAll();
protobuf::Message message;
if (!message.ParseFromArray(byteArray.data(), byteArray.size())) {
    qDebug() << "Failed to parse person.pb.";
}
mohabouje
  • 3,867
  • 2
  • 14
  • 28
  • This is working, but only when i do what @Zaiborg told me to do. And it's work also with ParseFromString :). Thanks anyway ! But I have another problem and you seems to know protobuf. To be clearer on my code, I omit to say that I try to parse different kind of `.proto` in my `emitMessageType` function. The first to be parse is protobuf::Player and it's asking an int AND a string. But protobuf::Player success the parse with a string serialize by a protobuf::Message and it's anoying – Ephesiel Jul 31 '19 at 08:22
  • sorry @Ephesiel, but I don't get your problem, maybe you could provide an update in your question with a small example of the protobuf model or the code itself. – mohabouje Jul 31 '19 at 08:29
  • Sorry it's difficult to explain in a little comment, here is a link to an other question that i ask -> [link](https://stackoverflow.com/questions/57287462/why-my-protobuf-class-can-parse-a-string-serialized-by-an-other-protobuf-class) – Ephesiel Jul 31 '19 at 09:26
0

out << data.c_str() inside SocketManager::write might discard at first \0. Try converting to QByteArray as described at Correct way to losslessly convert to and from std::string and QByteArray to ensure the whole string is sent.

Zaiborg
  • 2,492
  • 19
  • 28