Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有向量的可变大小数据包结构_C++_Network Programming_Networking - Fatal编程技术网

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()
    调用中可能会收到多条消息。如果它们缺少一个请求,您就有责任对它们进行缓冲,如果您在同一个过程中收到多个请求,就有责任对它们进行循环


此强制转换非常危险,因为您分配了一些原始内存,然后将其视为非POD类类型的初始化对象。这可能会在某个时候导致崩溃

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]);
    }
}