如何将向量序列化为字符数组
Posted
技术标签:
【中文标题】如何将向量序列化为字符数组【英文标题】:How to serialize a vector into an array of chars 【发布时间】:2016-04-09 10:55:08 【问题描述】:我有一个向量定义为:
std::vector<message *>
消息在哪里:
struct message
static unsigned int last_id;
unsigned int id;
std::string msg;
std::string timestamp;
我的目标是使用 Winsock 发送此信息(从服务器到客户端),但这仅允许发送 WinSock2.h 中出现的字符。考虑到这一点,我想序列化一个字符数组中的所有信息(id、msg 和时间戳)以便将它们全部发送,并且在客户端中,具有反序列化的函数以便具有相同的向量 I在服务器上。
我该如何实现它?
感谢任何帮助。
【问题讨论】:
请查看minimal reproducible example。你能展示你尝试过的东西吗? 我一直在寻找很多方法来实现它,但我找不到任何有效的解决方案。任何开始序列化的关键事实将不胜感激。 我对Winsock不熟悉,但可能和我用过的差不多。想象一下,您的所有 4 个项目都“压缩”成一个字符向量(我使用 std::string)。目的地如何找到各个字段?也许是您自己发明的协议?一个(有点)相关的区域被称为“持久存储”,我相信有些包存在,但不记得名字了。祝你好运。 从客户端发送到服务器我使用自己的协议(数据以“;”分隔,即我发送“cmd;username;msg;”。发送时出现问题从服务器到客户端,我需要发送更多信息(各种用户消息,关注者等),发送所有由“;”分隔的信息非常乏味。无论如何感谢您的回答! 为什么要将时间戳存储为字符串? 【参考方案1】:以下提供了一种解决序列化问题的简单方法。
但请注意,它不可移植。它假设双方(客户端/服务器)的环境条件相同,即字节顺序和 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);
【讨论】:
谢谢@R.G.该代码似乎可以工作,但是在创建序列化程序时编译之前出现错误。它在*myVector[i]
(已经被我的vector<message *>
替换)中说没有运算符匹配这些操作数“*”。操作数类型为:* std::vector*(myVector[i])
(Operator Precedence确保表达式的意思相同,但它可能会引出语法错误的原因)。如果它仍然存在,请在此处输入变量定义 (myVecor) 的完整语句,以及它在上述表达式中的使用时间。
问题已解决!我正在访问整个消息向量而不是单个消息。感谢您的时间@R.G.【参考方案2】:
您可以使用Boost serialization library 将结构保存/加载到字符数组中。 boost 库在 C++ 中被广泛使用,如果你不熟悉它,我建议你看看它。
您可以学习使用Boost 套接字,而不是使用winsock,并使您的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。该库还提供其他格式。另一个优点是它支持版本控制。
【讨论】:
谢谢@J-Mik。感谢您的回答,但我需要在 Winsock 中完成。无论如何,我会看看 Boost 序列化库,以防它在其他方面对我有帮助。以上是关于如何将向量序列化为字符数组的主要内容,如果未能解决你的问题,请参考以下文章
序列化为 JSON 字符串时如何修复 OutOfMemoryException?
将 JSON 反序列化为 C# 对象以在网格中将嵌套数组显示为字符串