gpt4 book ai didi

C++ 序列化 - 使用从 char * 到结构的 reinterpret_cast

转载 作者:太空狗 更新时间:2023-10-29 19:58:02 25 4
gpt4 key购买 nike

我正在使用 sendto(.. )recvfrom()

update_packet需要是通用的消息格式,这意味着它的字段有预先确定的固定大小,struct的大小是字段的总和。

struct node {
uint32_t IP;
uint16_t port;
int16_t nil;
uint16_t server_id;
uint16_t cost;
};

struct update_packet {
uint16_t num_update_fields;
uint16_t port;
uint32_t IP;

struct node * nodes;

update_packet() :
num_update_fields(num_nodes), IP(myIP), port(myport)
{//fill in nodes array};
};

(update_packet包含struct node的指针数组)

我使用 reinterpret_cast 通过 UDP 发送一个 update packet 的实例,然后编译并发送到正确的目的地。

int update_packet_size = sizeof(up);
sendto(s, reinterpret_cast<const char*>(&up), update_packet_size, 0,
(struct sockaddr *)&dest_addr, sizeof(dest_addr));

但是,当我收到它并尝试通过

对其进行解码时
struct update_packet update_msg =
reinterpret_cast<struct update_packet>(recved_msg);

我得到一个错误

In function ‘int main(int, char**)’:
error: invalid cast from type ‘char*’ to type ‘update_packet’
struct update_packet update_msg =
reinterpret_cast<struct update_packet>(recved_msg);

为什么会出现此错误,我该如何解决?

此外,这是通过套接字在 struct 实例中交换数据的正确方法吗?如果没有,我该怎么办?我需要像 http://beej.us/guide/bgnet/examples/pack2.c 中那样的 pack()ing 函数吗? ?

最佳答案

一般性

类型转换问题已在其他问题中得到妥善解答。

但是,您永远不应该依赖指针转换来通过网络发送/接收结构,原因有很多,包括:

  • 打包:编译器可以对齐结构变量并插入填充字节。这取决于编译器,因此您的代码将不可移植。如果两台正在通信的机器运行您使用不同编译器编译的程序,它可能无法运行。
  • Endianness:同理,发送多字节数(如int)时的字节顺序在两台机器上可能不同。

这将导致代码可能会工作一段时间,但几年后会导致很多问题,如果有人更改编译器、平台等...因为这是一个教育项目你应该尝试以正确的方式去做......

出于这个原因,将数据从结构体转换为字符数组以通过网络发送或写入文件时,应谨慎进行,逐个变量,并在可能的情况下考虑字节序。这个过程称为“序列化”。

详细序列化

序列化意味着您将数据结构转换为可以通过网络发送的字节数组。

序列化格式不一定是二进制:文本或 xml 是可能的选项。如果数据量很小,文本可能是最好的解决方案,您可以仅使用字符串流(std::istringstream 和 std::ostringstream)来依赖 STL

有几个很好的库可以序列化为二进制文件,例如 Qt 中的 Boost::serialization 或 QDataStream。您也可以自己做,寻找“C++ 序列化”的 SO

使用 STL 简单序列化为文本

在您的情况下,您可能只是使用类似以下内容序列化为文本字符串:

std::ostringstream oss;

oss << up.port;
oss << up.IP;
oss << up.num_update_fields;
for(unsigned int i=0;i<up.num_update_fields;i++)
{
oss << up.nodes[i].IP;
oss << up.nodes[i].port;
oss << up.nodes[i].nil;
oss << up.nodes[i].server_id;
oss << up.nodes[i].cost;
}

std::string str = oss.str();

char * data_to_send = str.data();
unsigned int num_bytes_to_send = str.size();

反序列化收到的数据:

std::string str(data_received, num_bytes_received);
std::istringstream(str);


update_packet up;
iss >> up.port;
iss >> up.IP;
iss >> up.num_update_fields;
//maximum number of nodes should be checked here before doing memory allocation!
up.nodes = (nodes*)malloc(sizeof(node)*up.num_update_fields);
for(unsigned int i=0;i<up.num_update_fields;i++)
{
iss >> up.nodes[i].IP;
iss >> up.nodes[i].port;
iss >> up.nodes[i].nil;
iss >> up.nodes[i].server_id;
iss >> up.nodes[i].cost;
}

这将是 100% 便携且安全的。您可以通过检查 iss 错误标志来验证数据有效性。

为了安全起见,您也可以:

  • 使用 std::vector 代替节点指针。这将防止内存泄漏和其他问题
  • 检查 iss >> up.num_update_fields; 之后的节点数,如果它太大,则在分配一个巨大的缓冲区之前中止解码,这将使您的程序甚至系统崩溃。网络攻击基于这样的“漏洞”:如果不进行这种检查,您可能会通过让服务器分配比其 RAM 大 100 倍的缓冲区来导致服务器崩溃。
  • 如果你的网络 API 有一个 std::iostream 接口(interface),你可以直接使用它的 << 和 >> 运算符,而不使用中间字符串和字符串流
  • 您可能认为使用空格分隔的文本是对带宽的浪费。仅当您的节点数量很大并且使带宽使用变得不可忽视且至关重要时才考虑这一点。在这种情况下,您需要序列化为二进制文件。但如果文本解决方案完美运行,请不要这样做(提防过早优化!)

简单的二进制序列化(不了解字节顺序/字节顺序):

替换:

oss.write << up.port;

通过:

oss.write((const char *)&up.port, sizeof(up.port));

字节顺序

但是在你的项目中,Big-Endian是必需的。如果您在 PC (x86) 上运行,则需要反转每个字段中的字节。

1)第一种选择:手工

const char * ptr = &up.port;
unsigned int s = sizeof(up.port);
for(unsigned int i=0; i<s; i++)
oss.put(ptr[s-1-i]);

最终代码:检测字节顺序(这并不难 - 在 SO 上查找)并调整您的序列化代码。

2) 第二种选择:使用像 boost 或 Qt 这样的库

这些库允许您选择输出数据的字节顺序。然后他们会自动检测平台字节顺序并自动完成这项工作。

关于C++ 序列化 - 使用从 char * 到结构的 reinterpret_cast,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27055729/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com