如何将矢量序列化为字符数组
How to serialize a vector into an array of chars
我有一个向量定义为:
std::vector<message *>
其中消息为:
struct message{
static unsigned int last_id;
unsigned int id;
std::string msg;
std::string timestamp;
}
我的目标是使用Winsock(从服务器到客户端)发送这些信息,但这只允许发送WinSock2.h中出现的字符。考虑到这一点,我想在一个字符数组中序列化所有信息(id、msg和时间戳),以便将其全部发送到一起。在客户端中,有一个反序列化的函数,以便具有与我在服务器中相同的向量。
我该如何实现它?
感谢您的帮助。
下面提供了一种解决序列化问题的简单方法。
但是,请注意它不是可移植的。它假定双方(客户端/服务器)的环境条件相同,即endianness和sizeof int以及size_t。在编写服务器/客户端程序时,这种假设可能不令人满意,您的代码也应该处理这方面的问题。
例如,如果您可以说32位对于id
值和字符串长度来说足够大,那么在序列化时可以使用htonl,在反序列化时则可以使用ntohl。
序列化程序:
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);
您可以使用Boost序列化库将结构保存/加载到char数组中。boost库在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。图书馆还提供其他格式。另一个优点是它支持版本控制。
相关文章:
- 指向指向字符数组的指针数组的指针
- 比较字符数组
- 如何使用Crypto++并为RSA返回可打印的字节/字符数组
- 使用无符号字符数组有效存储内存
- 错误:字符数组的初始值设定项太多
- 对字符数组中的元素执行逐位操作
- C++ 无法在字符数组中使用 for 循环打印字母模式
- 如何在 C++ 中从文件中读取字符数组(带有一些空格)
- C++ 传递二维字符数组
- 无法在 C++ 中输入字符数组
- 字符数组中的元素数
- 我的目标是编写一个程序来计算和存储字符串在字符数组中出现的位置
- 使用字符数组作为 Map 中的键
- C++:__aligned(__alignof__) 导致字符数组数据出现问题?
- 按顺序声明的字符数组重叠
- 在 C++ 中字符串的大小与字符数组的大小
- 寻找一种更好的方法来表示无符号字符数组
- 如何打印 2D 字符数组C++
- 从字符数组的元素中减去'a'是什么意思
- 我是否不正确地集中了这些字符数组?