A primitive way for simple cases is to describe the data structure:
struct Data { int a; float b; char c[32]; } data;
Write it to the socket on the transmitting side ( send(socket, &data, sizeof(data), 0)
and read on the host in exactly the same structure ( recv(socket, &data, sizeof(data), 0)
). It is very important that the structure on both sides (transmitting and receiving) was identical in memory location (the same type sizes, the same byte order in the system, the same alignment of the structure fields, the same representation of floating-point numbers). Otherwise we get the wrong data that was sent. In practice, if the receiving party is also written in another language, we will get extra fuss and p ostorov for error.
The next option is to fill the data buffer manually:
int foo = 42; long bar = 0; std::string str; str.append((char*)&foo, sizeof(int)); str.append((char*)&bar, sizeof(long));
Here, there is no problem with the alignment of the structure fields as in the first version, we glue the data ourselves, without gaps. But the rest of the problems are still with us (as before, the byte order, the size of the types, the representation of floating-point numbers must be identical on the transmitter and the receiver).
Manual, byte packing flow.
uint32_t foo = 42; std::vector<uint8_t> buffer; buffer.push_back(static_cast<uint8_t>(foo >> 0)); buffer.push_back(static_cast<uint8_t>(foo >> 8)); buffer.push_back(static_cast<uint8_t>(foo >> 16)); buffer.push_back(static_cast<uint8_t>(foo >> 24));
Here, we simply take each piece of data and manually transfer it to the output stream in a system independent order. Disassemble also have manually. The most universal way, because all aspects of the generated stream are controlled by ourselves. For convenience, you can write a serializer / deserializer class for the required types (including custom ones).
Over time (and maybe immediately), the difficulties associated with changes in the transmitted data are added (for example, additional data needed to be transferred or some old ones have already become irrelevant). Especially if the receiver must receive data in the old format and in the new one. You have to add some version identifiers. Additionally, you need to handle cases when you need to transfer optional data (which may be missing) or data of dynamic size (arrays).
In order not to solve all these problems on your own, you can take a ready-made solution, for example protobuf from google. It supports different languages, has a system of versions, support for complex data. Or a slightly simpler solution (but also faster), also from google flatbuffers .
If the amount of information transmitted is not critical, it may be convenient to generate data in the form of json (for example, using https://github.com/nlohmann/json ). If there is a javascript programmer on the receiving side, he will be very grateful to you (and not only the JavaScript programmer). Also, as a programmer from a typed language, I recommend using schemas for checking json.
As an alternative to json, you can take a messagepack , which is "like json", but more compact. If you need even more compact, you can shake the transmitted string using zlib for example.
For all variants, it is also necessary to take into account that transmitting pointers is meaningless, since on the receiving side, they will indicate unknown where. Also understand the subtleties of data transmission over the network. For example, data sent via UDP may not reach the recipient, data sent via TCP may be fragmented or glued to its neighbors upon receipt, etc. Perhaps you should think about ready-made online libraries, for example RakNet , which includes almost everything for building a multiplayer game.