假设我们有一个二进制协议,字段网络有序(大端)。
struct msg1
{
int32 a;
int16 b;
uint32 c
}
如果不是将网络缓冲区复制到我的msg1然后使用“networkToHost”函数来读取msg1
我重新排列/反转msg1到
struct msg1
{
uint32 c
int16 b;
int32 a;
}
只需从网络缓冲区执行反向复制即可创建msg1。在这种情况下,不需要networkToHost功能。这种习惯用法在大端机器上不起作用,但对我来说这不是问题。除此之外,我还有其他任何缺点吗?
感谢
P.S。对于上面我们强制执行严格的对齐(#pragma pack(1)
等)
答案 0 :(得分:20)
除此之外,我还有其他任何缺点吗?
我担心你误解了endian转换问题的本质。 “大端”并不意味着你的字段反向布局,所以
struct msg1_bigendian
{
int32 a;
int16 b;
uint32 c
}
大端架构上的等同于
struct msg1_littleendian
{
uint32 c;
int16 b;
int32 a;
}
在一个小端架构上。相反,它意味着每个字段中的字节顺序是相反的。我们假设:
a = 0x1000000a;
b = 0xb;
c = 0xc;
在大端架构上,这将被描述为:
10 00 00 0a
00 0b
00 00 00 0c
首先是高阶(最重要)字节。
在小端机器上,这将被布置为:
0a 00 00 10
0b 00
0c 00 00 00
最低位字节排在第一位,最后一位是最后一位。
序列化它们并将消息的序列化形式叠加在一起,您将发现不兼容性:
10 00 00 0a 00 0b 00 00 00 0c (big endian)
0a 00 00 10 0b 00 0c 00 00 00 (little endian)
int32 a int16 b int32 c
请注意,这不仅仅是字段反向运行的情况。你的提议会导致一个小端的机器将大端表示误认为:
a = 0xc000000; b = 0xb00; c = 0xa000010;
当然不是传播的内容!
对于每个传输的字段,您确实必须将每个字段转换为网络字节顺序并再次返回。
更新:
好的,我明白你现在要做什么。你想要反向定义结构,然后从字节串的 end 到开头的memcpy(反向复制),并以这种方式反转字节顺序。在这种情况下,我会说,是的,这是一个黑客,是的,它使你的代码不可移植,是的,它是不值得的。事实上,在字节顺序之间进行转换并不是一项非常昂贵的操作,并且它比处理每个结构的布局要容易得多。
答案 1 :(得分:6)
你确定这是必需的吗?很可能,您的网络流量将成为您的瓶颈,而不是CPU速度。
答案 2 :(得分:5)
同意@ribond -
这对开发人员来说非常容易混淆,因为他们必须努力将这些与语义相同的结构分开。
鉴于网络延迟比CPU处理它的速度快10,000,000倍,我只是保持不变。
答案 3 :(得分:2)
根据编译器如何打包结构中的字节,中间的16位数字可能不会在正确的位置结束。它可能存储在32位字段中,当您反转字节时,它将“消失”。
说真的,这样的技巧在你写它们时看起来很可爱,但从长远来看它们根本就不值得。
修改强>
您添加了“打包1”信息,因此错误消失但关于“可爱技巧”的事情仍然存在 - 不值得。写一个函数来反转32位和16位数字。
inline void reverse(int16 &n)
{
...
}
inline void reverse(int32 &n)
{
...
}
答案 4 :(得分:1)
除非您能证明存在显着的性能损失,否则您应该使用相同的代码将数据传输到网络或从网络传输数据,而不管机器的字节顺序如何。作为优化,对于网络顺序与硬件字节顺序相同的平台,您可以使用技巧,但要记住对齐要求等。
在这个例子中,许多机器(尤其是大型机器)将需要在int16成员的末尾和下一个int32成员之间使用2字节的填充。因此,虽然您可以读入10字节的缓冲区,但您不能将该缓冲区视为结构的图像 - 在大多数平台上将为12字节。
答案 5 :(得分:1)
正如你所说,这对大端机器来说是不可移植的。如果您希望您的代码在x86世界之外使用,那么这绝对是一个绝对破坏者。让我们其他人一个忙,只需使用ntoh / hton例程,或者你可能会发现自己在thedailywtf上有特色。
答案 6 :(得分:0)
请为您提供帮助的程序员,并在某个缓冲区中对字节序列进行显式转换。带结构的诡计将引导你直接进入字节序和对齐地狱(在那里)。