C++ 具有向量的可变大小数据包结构
最近,我一直在潜心研究网络编程,我在构造具有可变“数据”属性的数据包时遇到了一些困难。前面的几个问题帮助很大,但我仍然缺乏一些实现细节。我试图避免使用可变大小的数组,而只是使用向量。但我无法正确地传输它,我相信它在序列化过程中的某个地方 现在来看一些代码 包头C++ 具有向量的可变大小数据包结构,c++,network-programming,networking,C++,Network Programming,Networking,最近,我一直在潜心研究网络编程,我在构造具有可变“数据”属性的数据包时遇到了一些困难。前面的几个问题帮助很大,但我仍然缺乏一些实现细节。我试图避免使用可变大小的数组,而只是使用向量。但我无法正确地传输它,我相信它在序列化过程中的某个地方 现在来看一些代码 包头 class Packet { public: void* Serialize(); bool Deserialize(void *message); unsig
class Packet {
public:
void* Serialize();
bool Deserialize(void *message);
unsigned int sender_id;
unsigned int sequence_number;
std::vector<char> data;
};
类数据包{
公众:
void*序列化();
bool反序列化(void*消息);
未签名的整数发送者\u id;
无符号整数序列号;
std::矢量数据;
};
数据包植入
typedef struct {
unsigned int sender_id;
unsigned int sequence_number;
std::vector<char> data;
} Packet;
void* Packet::Serialize(int size) {
Packet* p = (Packet *) malloc(8 + 30);
p->sender_id = htonl(this->sender_id);
p->sequence_number = htonl(this->sequence_number);
p->data.assign(size,'&'); //just for testing purposes
}
bool Packet::Deserialize(void *message) {
Packet *s = (Packet*)message;
this->sender_id = ntohl(s->sender_id);
this->sequence_number = ntohl(s->sequence_number);
this->data = s->data;
}
typedef结构{
未签名的整数发送者\u id;
无符号整数序列号;
std::矢量数据;
}包;
void*数据包::序列化(整数大小){
包*p=(包*)malloc(8+30);
p->sender\u id=htonl(此->sender\u id);
p->序列号=htonl(该->序列号);
p->data.assign(大小,&');//仅用于测试目的
}
bool数据包::反序列化(void*消息){
数据包*s=(数据包*)消息;
此->发送方id=ntohl(s->发送方id);
此->序列号=NTOLL(s->序列号);
此->数据=s->数据;
}
在执行过程中,我只需创建一个数据包,分配其成员,并相应地发送/接收。上述方法仅负责序列化。不幸的是,数据从未被传输
这里有几点需要指出。我猜malloc是错误的,但我不确定如何计算它(即,它将是什么其他值)。除此之外,我不确定以这种方式使用向量的正确方式,希望有人能告诉我如何使用(请提供代码示例!):)
编辑:我已经将这个问题授予了关于向量数据属性实现的最全面的答案。感谢所有的回复这个技巧在结构的结尾用C样式数组工作,但不是用C++向量。不能保证C++向量类(并且它最可能不会)将包含的数据放入包结构中存在的“报头对象”中。相反,该对象将包含一个指向其他地方的指针,实际数据存储在那里。我认为问题的核心是您尝试以这种方式“序列化”向量,并且您可能假设向量的状态信息被传输。正如您所发现的,当您试图在网络上移动对象时,这种方式实际上不起作用,而指针等东西在另一台机器上没有任何意义 我认为最简单的处理方法是将数据包更改为以下结构:
struct Packet {
unsigned int sender_id;
unsigned int sequence_number;
unsigned int vector_size;
char data[1];
};
data[1]
位是一个用于可变长度数组的旧C技巧-它必须是结构中的最后一个元素,因为您实际上是在写入超过结构大小的内容。为此,您必须获得数据结构的正确分配,否则您将受到伤害
您的序列化函数如下所示:
void* Packet::Serialize(std::vector<char> &data) {
Packet* p = (Packet *) malloc(sizeof(Packet) + data.size());
p->sender_id = htonl(this->sender_id);
p->sequence_number = htonl(this->sequence_number);
p->vector_size = htonl(data.size());
::memcpy(p->data, data[0], size);
}
void*数据包::序列化(std::向量和数据){
Packet*p=(Packet*)malloc(sizeof(Packet)+data.size());
p->sender\u id=htonl(此->sender\u id);
p->序列号=htonl(该->序列号);
p->vector_size=htonl(data.size());
::memcpy(p->data,data[0],size);
}
如您所见,我们将传输向量的数据大小和内容,并将其复制到一个简单的C数组中,该数组易于传输。您必须记住,在网络发送例程中,您必须正确计算结构的大小,因为您必须发送sizeof(Packet)+sizeof(data)
,否则您将被切断向量并返回到良好的缓冲区溢出区域
免责声明-我没有测试上面的代码,它只是从内存中编写的,因此您可能需要修复奇怪的编译错误。我认为您需要直接使用套接字函数返回的字节数组 出于这些目的,最好在协议中包含消息的两个不同部分。第一部分是一个固定大小的“标题”。在您的示例中,这将包括后面的“是”的大小、“有效负载”或、
data
因此,借用一些代码片段并对其进行扩展,您可能会有如下内容:
void* Packet::Serialize(std::vector<char> &data) {
Packet* p = (Packet *) malloc(sizeof(Packet) + data.size());
p->sender_id = htonl(this->sender_id);
p->sequence_number = htonl(this->sequence_number);
p->vector_size = htonl(data.size());
::memcpy(p->data, data[0], size);
}
类型定义结构{
未签名的整数发送者\u id;
无符号整数序列号;
unsigned int data_length;//这是新的
}打包头;
因此,当你得到一个缓冲区时,你会把它当作一个PacketHeader*
,然后检查data\u length
,以知道接下来的字节向量中会出现多少字节
我还要补充几点
- 将这些字段设置为无符号整数是不明智的。C和C++的标准没有指定<代码> int >代码>,并且你希望在所有编译器上都能预测的东西。我建议使用
- 请注意,当您从套接字获取字节时。。。它决不能保证与另一端写入的
或send()
大小相同。您可能会收到不完整的消息(“术语中的数据包”),或者在一次write()
或read()
调用中可能会收到多条消息。如果它们缺少一个请求,您就有责任对它们进行缓冲,如果您在同一个过程中收到多个请求,就有责任对它们进行循环recv()
Packet* p = (Packet *) malloc(8 + 30);
查看您的代码,我假设您希望从调用seralize函数的数据包
对象中写出一个字节序列。在这种情况下,您没有nee
void* Packet::Serialize(int size)
{
char* raw_data = new char[sizeof sender_id + sizeof sequence_number + data.size()];
char* p = raw_data;
unsigned int tmp;
tmp = htonl(sender_id);
std::memcpy(p, &tmp, sizeof tmp);
p += sizeof tmp;
tmp = htonl(sequence_number);
std::memcpy(p, &tmp, sizeof tmp);
p += sizeof tmp;
std::copy(data.begin(), data.end(), p);
return raw_data;
}
struct PacketHeader
{
unsigned int senderId;
unsigned int sequenceNum;
};
class Packet
{
protected:
PacketHeader header;
std::vector<char> data;
public:
char* serialize(int& packetSize);
void deserialize(const char* data,int dataSize);
}
char* Packet::serialize(int& packetSize)
{
packetSize = this->data.size()+sizeof(PacketHeader);
char* packetData = new char[packetSize];
PacketHeader* packetHeader = (PacketHeader*)packetData;
packetHeader->senderId = htonl(this->header.senderId);
packetHeader->sequenceNum = htonl(this->header.sequenceNum);
char* packetBody = (packetData + sizeof(packetHeader));
for(size_t i=0 ; i<this->data.size() ; i++)
{
packetBody[i] = this->data.at(i);
}
return packetData;
}
void deserialize(const char* data,int dataSize)
{
PacketHeader* packetHeader = (PacketHeader*)data;
this->header.senderId = ntohl(packetHeader->senderId);
this->header.sequenceNum = ntohl(packetHeader->sequenceNum);
this->data.clear();
for(int i=sizeof(PacketHeader) ; i<dataSize ; i++)
{
this->data.push_back(data[i]);
}
}