2010-11-18 53 views
5

我正在编写一个具有网络功能的跨平台游戏(使用SFML和RakNet),并且我已经到达了在我的Ubuntu服务器上编译服务器并获得客户端在我的Mac上。所有的开发都是在我的Mac上完成的,所以我最初一直在测试服务器,并且它工作正常。在C++中创建和使用跨平台结构

我通过网络发送struct s,然后简单地将它们从char *转换回(例如)inet::PlayerAdded。现在这一直工作正常(大部分),但我的问题是:这会一直工作吗?这似乎是一个非常脆弱的方法。例如,结构的布局是否总是与其他平台,Windows相同?你会推荐什么?如果(除其他事项外),试图从little-endian的机器做它大端机作为正确int表示将两者颠倒

#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) 
+2

对于序列downvoter,有什么特别的原因? (史蒂夫的downvote看起来特别糟糕) – KevinDTimm 2010-11-18 16:38:01

+0

在这样的地方,积分毫无意义。我不会为此担心。 – 2010-11-18 17:18:07

+1

我不在乎分,我想知道为什么每个答案(除了一个,所有的最不有用)得到一个downvote。尤其是当其中一个(低估的)答案显然是最好的时候。 – KevinDTimm 2010-11-18 20:37:06

回答

5

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

但是您当然可以创建跨平台的二进制结构,它始终都在进行,并且是交换数据的非常有效的方式。对这些数据分层“结构”才有意义。但是,您必须非常小心布局,幸运的是,大多数编译器为您提供了多种选择。 “pack”就是这样的一个选项,并且需要处理很多。

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

+0

尽管大多数答案都非常棒,但我认为这对我最有帮助。我想我会试一试,如果我用这种方法碰到障碍,我会知道该用什么。 – ErikPerik 2010-11-18 17:02:47

9

这是行不通的。

如果结构的对齐或打包在机器之间变化,这也可能失败。如果你有一些64位机器和一些32位机器呢?

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

关于协议缓冲区的一件好事就是您可以使用兼容protobuf流的ZLIB兼容流来透明地压缩数据。我实际上已经做到了,它运作良好。我想其他装饰器流可以用类似的方式来根据需要增强或优化你的基础线协议。

2

答案是“......甚至在其他平台上也布置...”通常是否定的。即使解决了不同的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位值的数组,那么唯一需要处理的外部化问题就是字节顺序。