2016-04-09 3 views
0

Я вектор определяется как:Как сериализовать вектора в массив символов

std::vector<message *> 

где сообщение:

struct message{ 
    static unsigned int last_id; 
    unsigned int id; 
    std::string msg; 
    std::string timestamp; 
} 

Моя цель состоит в том, чтобы отправить эту информацию с помощью Winsock (с сервера для клиента), но это позволяет отправлять символы только в WinSock2.h. Принимая это во внимание, я хочу сериализовать всю информацию (id, msg и timestamp) в массиве символов, чтобы отправить все это вместе, а на клиенте есть функция десериализации, чтобы иметь один и тот же вектор I был на сервере.

Как я мог его реализовать?

Любая помощь приветствуется.

+0

Пожалуйста, просмотрите [MCVE]. Можете ли вы показать, что вы пробовали? –

+0

Я искал множество способов его реализации, но я не нашел подходящего решения. Любой ключевой факт для начала сериализации будет оценен по достоинству. – Zarauztarra

+1

Я не знаком с Winsock, но, вероятно, подобен тому, что я использовал. Представьте, что все 4 из ваших предметов «уплотнены» в один вектор символов (я использую std :: string). Как пункт назначения найдет отдельные поля? Возможно, протокол вашего собственного изобретения? A (несколько) связанная область называется «постоянным хранилищем», я считаю, что некоторые пакеты существуют, но не помнят имя. Удачи. –

ответ

1

Ниже приведен простой подход к проблеме сериализации.

Однако обратите внимание, что не переносится. Он принимает одинаковые условия среды с обеих сторон (клиент/сервер), то есть endianness и sizeof int и size_t. Это предположение, вероятно, неудовлетворительно при написании программ сервера/клиента, и ваш код также должен обрабатывать этот аспект.

Например, если вы можете сказать, что 32 бит является достаточным размером для значения id и длиной ваших строк, вы можете использовать htonl при сериализации и ntohl при десериализации.

Serializer:

class MessageSerializer 
{ 
public: 
    MessageSerializer(const message& messageStruct) 
    : m_msgRef(messageStruct) 
    , m_msgLength(m_msgRef.msg.length()) 
    , m_timeLength(m_msgRef.timestamp.length()) 
    {} 

    size_t RequiredBufferSize() const 
    { 
     return sizeof(int) + sizeof(size_t)*2 + m_msgLength + m_timeLength; 
    } 

    void Serialize(void* buffer) const 
    { 
     PushNum  (buffer, m_msgRef.id); 
     PushString (buffer, m_msgRef.msg.c_str(), m_msgLength); 
     PushString (buffer, m_msgRef.timestamp.c_str(), m_timeLength); 
    } 
private: 
    const message& m_msgRef; 
    const size_t m_msgLength; 
    const size_t m_timeLength; 

    template<typename INTEGER> 
    void PushNum(void*& buffer, INTEGER num) const 
    { 
     INTEGER* ptr = static_cast<INTEGER*>(buffer); 
     //copying content 
     *ptr = num; 
     //updating the buffer pointer to point the next position to copy 
     buffer = ++ptr; 
    } 
    void PushString(void*& buffer, const char* cstr, size_t length) const 
    { 
     PushNum(buffer, length); 
     //copying string content 
     memcpy(buffer, cstr, length); 
     //updating the buffer pointer to point the next position to copy 
     char* ptr = static_cast<char*>(buffer); 
     ptr += length; 
     buffer = ptr; 
    } 
}; 

десериализатор:

class MessageDeserializer 
{ 
public: 
    MessageDeserializer(const char* messageBuffer) 
    : m_msgBuffer(messageBuffer) 
    {} 

    void Deserialize(message& messageOut) 
    { 
     messageOut.id   = PopNum<int>(m_msgBuffer); 
     messageOut.msg   = PopString(m_msgBuffer); 
     messageOut.timestamp = PopString(m_msgBuffer); 
    } 

private: 

    const void* m_msgBuffer; 

    template<typename INTEGER> 
    INTEGER PopNum(const void*& buffer) const 
    { 
     const INTEGER* ptr = static_cast<const INTEGER*>(buffer); 
     //copying content 
     INTEGER retVal = *ptr; 
     //updating the buffer pointer to point the next position to copy 
     buffer = ++ptr; 

     return retVal; 
    } 

    std::string PopString(const void*& buffer) const 
    { 
     size_t length = PopNum<size_t>(buffer); 
     const char* ptr = static_cast<const char*>(buffer); 
     //copying content 
     std::string retVal(ptr, length); 
     //updating the buffer pointer to point the next position to copy 
     ptr += length; 
     buffer = ptr; 

     return retVal; 
    } 
}; 

Тогда ваш используя код может быть что-то вроде:

//... 
MessageSerializer serializer(*myVector[i]); 
char* buffer = new char[serializer.RequiredBufferSize()]; 
serializer.Serialize(buffer); 

и:

//... 
message myMsg; 
MessageDeserializer(input).Deserialize(myMsg); 
+0

Спасибо @ R.G. Код, похоже, работает, но я получаю сообщение об ошибке перед компиляцией при создании сериализатора. Он говорит в '* myVector [i]' (уже заменен моим 'vector '), который оператор _no соответствует этим операндам «*». Типы операндов: * std :: vector <сообщение *, std :: allocator > _. Я попытался создать новые векторы, чтобы увидеть, была ли проблема с моей, но проблема сохраняется. Я бы очень признателен за любую помощь! – Zarauztarra

+0

Попробуйте написать выражение с круглой скобкой, например: '* (myVector [i])' ([Приоритет оператора] (http://en.cppreference.com/w/cpp/language/operator_precedence) гарантирует, что выражение означает то же самое, но это может привести к возникновению ошибки синтаксиса). Если он по-прежнему сохраняется, введите здесь полные инструкции определения переменной (myVecor) и когда он используется как в приведенном выше выражении. –

+0

Проблема исправлена! Я обращался ко всему вектору сообщений, а не к одному. Спасибо за ваше время @ R.G. – Zarauztarra

1

Вы можете использовать Boost serialization library, чтобы сохранить/загрузить свою структуру в массив символов. Библиотека ускорения широко используется в C++, и если вы не знакомы с ней, я бы рекомендовал взглянуть на нее.

Вместо использования winsock вы можете научиться использовать сокеты Boost и сделать свой код на C++ практически на любой платформе, а не только на Windows, но это еще одна тема.

Вот пример того, как сериализовать вектора, и восстановить его с другой стороны гнезда:

#include <vector> 
#include <boost/serialization/nvp.hpp> 
#include <boost/serialization/vector.hpp> 
#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 

struct message { 
    static unsigned int last_id; 
    unsigned int id; 
    std::string msg; 
    std::string timestamp; 

    template <class ArchiveT> 
    void serialize(ArchiveT& ar, const unsigned int /*version*/) // function used to serialize (save/load) data from the boost serialization library 
    { 
     ar & boost::serialization::make_nvp("LastId", last_id); 
     ar & boost::serialization::make_nvp("Id", id); 
     ar & boost::serialization::make_nvp("Msg", msg); 
     ar & boost::serialization::make_nvp("Timestamp", timestamp); 
    } 
}; 

unsigned int message::last_id; 

template <class T> 
void serialize_save(const T& obj, std::string& outString) 
{ 
    std::stringstream binaryOut; 
    boost::archive::binary_oarchive outArchive(binaryOut); 
    outArchive << obj; 

    outString = binaryOut.str(); 
} 

template <class T> 
void serialize_load(T& dataOut, const void* data, const size_t dataSize) 
{ 
    const char* dataPtr = reinterpret_cast<const char*>(data); 
    std::string dataString(dataPtr, dataPtr + dataSize); 
    std::stringstream dataStream(dataString); 
    boost::archive::binary_iarchive binArchive(dataStream); 
    binArchive >> dataOut; 
} 


void init_vector(std::vector<message*>& vect) { 
    const size_t vectorSize = 2; 

    vect.resize(vectorSize); 
    for (size_t i = 0; i < vectorSize; i++) { 
     vect[i] = new message(); 
     vect[i]->last_id = 0; 
     vect[i]->id = 1; 
     vect[i]->msg = "This is a message"; 
     vect[i]->timestamp = "12:02pm"; 
    } 
} 

int main() { 
    std::vector<message*> messages; 
    init_vector(messages); // initialize the vector. set it to any data 

    std::string outputBuffer; 
    serialize_save(messages, outputBuffer); // save the vector to a string (array of char) 

    socket_write(outputBuffer.c_str(), outputBuffer.size()); // write the serialized data to the socket 

    // on the reception side 
    std::string receiveBuffer; 
    socket_read(receiveBuffer); // receive socket data 

    std::vector<message*> receivedMessages; 
    serialize_load(receivedMessages, receiveBuffer.c_str(), receiveBuffer.size()); // from the array of character recover the vector 
    // here the vector receivedMessages contains the same values saved in init_vector() 
} 

Вы можете изменить формат экспорта, если вы хотите, изменив boost::archive::binary_iarchive объект. Например, замените его на boost::archive::xml_iarchive для сериализации объектов в XML. Существуют и другие форматы, предоставляемые библиотекой. Другим преимуществом является то, что он поддерживает управление версиями.

+0

Спасибо @ J-Mik. Я ценю ваш ответ, но мне нужно сделать это в Winsock. В любом случае, я посмотрю на библиотеку сериализации Boost, если это поможет мне в других аспектах. – Zarauztarra