1

I would like to send a struct via a QUdpSocket. I know I should use QDataStream and QByteArray, but I can't because the receiver will not use Qt.

I tried many things, but I never find something that seems to do the work properly.

My struct will be :

typedef struct myStruct
    {
       int nb_trame;
       std::vector<bool>  vBool;
       std::vector<int>   vInt;
       std::vector<float> vFloat;
    } myStruct;

How do I procede to do that properly ?

0xPunt
  • 313
  • 1
  • 4
  • 21
  • 1
    you'll need to [serialize](https://en.wikipedia.org/wiki/Serialization) the struct contents before sending, and then deserialize on the receiving end. Have a look at this [other question](http://stackoverflow.com/questions/523872/how-do-you-serialize-an-object-in-c) eg. – Sander De Dycker May 26 '16 at 12:05
  • @SanderDeDycker Here is the problem, I have never done this before, and I don't find anything that explain it. I don't want to copy/paste something that I don't understand.... – 0xPunt May 26 '16 at 12:12
  • I included two links that can serve as a starting point for you to learn about serialization. If you need more reading material, then just search using the keyword "serialization", and you'll find plenty. – Sander De Dycker May 26 '16 at 12:15
  • 2
    Don't. Don't use `structs` as network protocols. Use network protocols as network protocols. Start by defining the protocol on paper in octets and then write yourself a library to send and receive it. This way you are introducing half a dozen external dependencies that will come back and bite you. – user207421 May 26 '16 at 12:57

1 Answers1

3

The solution to this is called serialization

serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer, or transmitted across a network connection link) and reconstructed later in the same or another computer environment.

The following is a fully working example how to serialize the mentioned struct using QDataStream:

// main.cpp
#include <limits>
#include <QDataStream>
#include <QVector>
#include <vector>

typedef struct myStruct
{
   int nb_trame;
   std::vector<bool>  vBool;
   std::vector<int>   vInt;
   std::vector<float> vFloat;

   void serialize(QDataStream &out) {
       out << nb_trame;
       out << QVector<bool>::fromStdVector(vBool);
       out << QVector<qint32>::fromStdVector(vInt);
       out << QVector<float>::fromStdVector(vFloat);
   }
} myStruct;

void fillData(myStruct &s) {
    s.nb_trame = 0x42;

    s.vBool.push_back(true);
    s.vBool.push_back(false);
    s.vBool.push_back(false);
    s.vBool.push_back(true);

    s.vInt.push_back(0xB0);
    s.vInt.push_back(0xB1);
    s.vInt.push_back(0xB2);
    s.vInt.push_back(0xB3);

    s.vFloat.push_back(std::numeric_limits<float>::min());
    s.vFloat.push_back(0.0);
    s.vFloat.push_back(std::numeric_limits<float>::max());
}

int main()
{
    myStruct s;

    fillData(s);

    QByteArray buf;
    QDataStream out(&buf, QIODevice::WriteOnly);
    s.serialize(out);
}

Then you can send buf with QUdpSocket::writeDatagram()


How QDataStream serializes

If we replace

QByteArray buf;
QDataStream out(&buf, QIODevice::WriteOnly);

with

QFile file("file.dat");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);

The serialized data gets written to the file "file.dat". This is the data the code above generates:

> hexdump -C file.dat
00000000  00 00 00 42 00 00 00 04  01 00 00 01 00 00 00 04  |...B............|
00000010  00 00 00 b0 00 00 00 b1  00 00 00 b2 00 00 00 b3  |................|
00000020  00 00 00 03 38 10 00 00  00 00 00 00 00 00 00 00  |....8...........|
00000030  00 00 00 00 47 ef ff ff  e0 00 00 00              |....G.......|
  • The data starts with four bytes that represent the member nb_trame (00 00 00 42)
  • The next eight bytes are the serialized form of the vector vBool (00 00 00 04 01 00 00 01)
    • 00 00 00 04 --> Number of entries in the vector
    • 01 00 00 01 --> True, False, False, True
  • The next 20 bytes are for vInt (00 00 00 04 00 00 00 b0 00 00 00 b1 00 00 00 b2 00 00 00 b3)
    • 00 00 00 04 --> Number of entries in the vector
    • 00 00 00 b0 00 00 00 b1 00 00 00 b2 00 00 00 b3 --> 0xB0, 0xB1, 0xB2, 0xB3 (4 bytes per entry)
  • The next 28 bytes are for vFloat (00 00 00 03 38 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 ef ff ff e0 00 00 00)
    • 00 00 00 03 --> Number of entries in the vector
    • 38 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 ef ff ff e0 00 00 00 --> 1.1754943508222875e-38, 0.0, 3.4028234663852886e+38 (8 bytes per entry)

Additional information (Original posting)

Serialization is not a trivial topic but there are many libraries out there, that can help you with that. In the end you have two options to choose from:

  1. Define your own serialization format
  2. Use an existing serialization format
    1. Binary
    2. Text based (e.g. JSON, XML)

Which one you choose depends highly on your needs and use cases. In general I would prefer established formats over self-brewed. Text-based formats are by nature less compact and need more space and therefore also bandwidth. This is something you should take into account when you decide for a format. On the other hand text-based/human-readable formats have the advantage of being easier to debug, as you can open them in a text editor. And there are many more factors you should consider.

Serialization works because you do not rely on machine dependent things. The only thing you have to take care of is that the serialized data is consistent and follows the defined format. So for the serialized data you know exactly how the byte order is defined, where specific data is stored and so on.

The idea is that the sender serializes the data, sends it over whatever channel is required, and the receiver deserializes the data again. In which format the data is stored on each of the both sides doesn't matter.

   +--------------------------------+             +--------------------------------+
   | Host A                         |             | Host B                         |
   |                                |             |                                |
   |                                |             |                                |
   |                                |             |                                |
   |  +-------------------------+   |             |  +-------------------------+   |
   |  |         Raw data        |   |             |  |         Raw data        |   |
   |  |(Specific to plattfrom A)|   |             |  |(Specific to plattfrom B)|   |
   |  +-------------------------+   |             |  +-------------------------+   |
   |               |                |             |               ^                |
   |               | serialize      |             |               | deserialize    |
   |               v                |             |               |                |
   |      +-----------------+       |  transmit   |      +-----------------+       |
   |      | Serialized Data +----------------------------> Serialized Data |       |
   |      +-----------------+       |             |      +-----------------+       |
   |                                |             |                                |
   +--------------------------------+             +--------------------------------+
exilit
  • 1,156
  • 11
  • 23