繁体   English   中英

在C ++中创建和使用跨平台结构

[英]Creating and using a cross platform struct in C++

我正在编写一个具有网络功能的跨平台游戏(使用SFML和RakNet ),我已经到了我已经在我的Ubuntu服务器上编译服务器并让我的Mac上运行客户端的地步。 所有的开发都在我的Mac上完成,所以我最初一直在测试服务器,它运行良好。

我正在通过网络发送struct ,然后简单地将它们从char *转换回(例如) inet::PlayerAdded 现在这一直很好(大部分),但我的问题是:这总是有效吗? 这似乎是一种非常脆弱的方法。 例如,即使在其他平台上,结构体的布局也一样吗? 你会推荐什么?

#pragma pack(push, 1)
struct Player
{
    int dir[2];
    int left;
    float depth;
    float elevation;
    float velocity[2];
    char character[50];
    char username[50];
};

// I have been added to the game and my ID is back
struct PlayerAdded: Packet
{
    id_type id;
    Player player;
};
#pragma pack(pop)

如果(除其他事项外)您尝试从little-endian机器到big-endian机器执行此操作,这将无效,因为正确的int表示将在两者之间反转。

如果您的结构的对齐或包装在不同机器之间发生变化,这也可能会失败。 如果你有一些64位机器而一些是32位机器怎么办?

您需要使用适当的便携式序列化库,如Boost.SerializationGoogle Protocol Buffers,以确保您拥有可以独立于硬件成功解码的有线协议(也称为可传输数据格式)。

关于Protocol Buffers的好处是,您可以使用与protobuf流兼容的ZLIB兼容流透明地压缩数据。 我实际上做到了这一点,效果很好。 我想其他装饰器流可以以类似的方式使用,以根据需要增强或优化您的基本线程协议。

像许多其他答案一样,如果可以避免,我建议不要发送原始二进制数据。 像Boost serial或Google Protobuf这样的东西可以在没有太多开销的情况下做得很好。

但是你当然可以创建跨平台的二进制结构,它始终是完成的,并且是一种非常有效的数据交换方式。 在该数据上分层“结构”是有道理的。 但是你必须非常小心布局,幸运的是大多数编译器为你提供了很多选择。 “打包”就是这样一种选择,并且需要处理很多。

您还需要注意数据大小。 简单包括stdint.h并使用固定大小的类型,如uint32_t 请注意浮点值,因为并非所有体系结构都将共享相同的值,因为它们可能会执行32位浮点数。 同样对于endianess,大多数架构都会使用相同的,如果不是,你可以简单地在不同的客户端上翻转它。

答案“......甚至在其他平台上也是如此......”通常都没有。 即使解决了诸如不同CPU和/或不同字节顺序之类的问题,也是如此。

不同的操作系统(即使在相同的硬件平台上)可能使用不同的数据表示; 这通常被称为“平台ABI”,它在例如32位/ 64位Windows,32位/ 64位Linux,MacOSX之间有所不同。

'#pragma pack'只有一半,因为超出对齐限制可能会有数据类型大小差异。 例如,64位Windows上的“long”为32位,而Linux和MacOSX上为64位。

也就是说,问题显然不是新的,并且已经在过去得到解决 - 远程过程调用标准(RPC)包含如何以独立于平台的方式定义数据结构以及如何编码/解码“缓冲区”的机制代表这些结构。 它被称为“XDR”(外部数据表示)。 请参阅RFC1832。 随着编程的进行,这个轮子已经多次重新发明; 无论你转换为XML,使用XDR做低级别的工作,使用google :: protobuf,boost或Qt :: Variant,还有很多可供选择。

在纯粹的实现方面:对于简单,假设“unsigned int”在32位边界处是32位对齐的; 如果您可以将所有数据编码为32位值的数组,那么您必须处理的唯一外部化问题是字节序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM