简体   繁体   中英

How would one cast a buffer to a struct without corrupting the data?

i am working on a network project of mine in order to learn more about networking and right now i have designed a simple protocol/structure that i fill and send to the server, the problem is that all vectors and probably arrays aswell are invalid on the server side.

im gonna try to explain it with code, its alot easier that way.

My protocol:

typedef struct NETWORK_PROTOCOL {
    int packet_size;
    int number_of_data_files;
    std::vector<std::string> data_files;
}

so its a pretty simple protocol, and what i did is that i fill it with data and its completely valid on the client side, however as soon as i send it to the server and try to convert it back it the vector is invalid but the integers are still valid.

this is how i create and send the data from the client:

NETWORK_PROTOCOL Protocol;

//Fills protocol with data

int sendt = send(ClientSocket, (const char*)&Protocol, Protocol.packet_size, 0);

and when it hits the server i still get the full size of the data, but as i said earlier it does not convert back properly :/

Code on the server side that tries to cast it back:

NETWORK_PROTOCOL* Protocol;

iResult = recv(ClientSocket, buffer, BUFFLEN, 0);

//then i have some validation code to check if the whole packet arrived since its TCP

Protocol = reinterpret_cast<NETWORK_PROTOCOL*>(buffer);
//And now the vector is invalid :/

im not really sure how to fix this problem, i thought it would be easy to convert it back since it is the exact same data on both sides. Any help to fix this issue is greatly appreciated.

std::vector can't be transferred this way: internally it uses pointers, so you send only a pointer, without any actual information, and that pointer is not valid on the receiving side.

In order to send the contents of vector, you need to somehow serialize it (convert it to the representation in which it can be easily transferred). For example, you can use is Boost.Serialization

#include <sstream>

// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>

struct NETWORK_PROTOCOL
{
private:
    friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & packet_size;
        ar & number_of_data_files; // you don't actually need it
        ar & data_files;
    }
public:
    int packet_size;
    int number_of_data_files;
    std::vector<std::string> data_files;
};

Now you can serialize it like this:

    std::ostringstream ofs;
    boost::archive::text_oarchive oa(ofs);
    oa << protocol; // protocol is your instance of NETWORK_PROTOCOL, which you want to send

   // and then you'll be able to get a buffer from ofs using ofs.str()

Deserialize it like this:

    NETWORK_PROTOCOL protocol;
    std::istringstream ifs(buf);
    boost::archive::text_iarchive ia(ifs);
    ia >> protocol;

For practical usages you may want to use binary archives instead. If you decide to go with boost.serialization, I recommend starting looking here .

You may also like Google Protocol Buffers: https://developers.google.com/protocol-buffers/docs/cpptutorial

This comment is longer than allowed. So I put it as an answer; although I think it answers partially.

To send all the data in one shipment wasted space and bandwidth, because you'd have to take a maximum for the number of names and their sizes. So I suggest you divide your transmission in phases.

In the first phase you send the number of filenames that you are transmitting. In this way you prepare the server for receiving n file names. Then in the second phase you make a loop divided in two transmissions. The first transmission you send the file name size, then you prepare a buffer for receiving the filename.

For these modes you only use basic types ( size_t and char * ).

On the server side you can build your vector<string> , if you want to give that illusion

I hope it helps you

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM