有两个系统通过 TCP 进行通信。第一个使用小端,第二个使用大端。系统之间的ICD包含很多结构体(字段)。为每个字段进行字节交换看起来不是最好的解决方案。
是否有任何通用的解决方案/实践来处理具有不同字节顺序的系统之间的通信?
Gro*_*roo 10
每个系统可能具有不同的体系结构,但字节顺序应由通信协议定义。如果协议规定“数据必须以大端字节序发送”,那么这就是系统发送数据以及其他系统接收数据的方式。
我猜你问的原因是因为你想将结构指针转换为 achar*并通过网络发送它,这是行不通的。
That is generally a bad idea. It's far better to create an actual serializer, so that your internal data is decoupled from the actual protocol, which also means you can easily add support for different protocols in the future, or different versions of the protocols. You also don't have to worry about struct padding, aliasing, or any implementation-defined issues that casting brings along.
(update)
So generally, you would have something like:
void Serialize(const struct SomeStruct *s, struct BufferBuilder *bb)
{
BufferBuilder_append_u16_le(bb, s->SomeField);
BufferBuilder_append_s32_le(bb, s->SomeOther);
...
BufferBuilder_append_u08(bb, s->SomeOther);
}
Run Code Online (Sandbox Code Playgroud)
Where you would already have all these methods written in advance, like
// append unsigned 16-bit value, little endian
void BufferBuilder_append_u16_le(struct BufferBuilder *bb, uint16_t value)
{
if (bb->remaining < sizeof(value))
{
return; // or some error handling, whatever
}
memcpy(bb->buffer, &value, sizeof(value));
bb->remaining -= sizeof(value);
}
Run Code Online (Sandbox Code Playgroud)
We use this approach because it's simpler to unit test these "appending" methods in isolation, and writing (de)serializers is then a matter of just calling them in succession.
But of course, if you can pick any protocol and implement both systems, then you could simply use protobuf and avoid doing a bunch of plumbing.
一般来说,通过网络传输的值应该采用网络字节顺序,即大端字节序。因此,值应该从主机字节顺序转换为网络字节顺序以进行传输,并在接收时转换回来。
函数htonsandntohs对 16 位整数值执行此操作,对 32 位整数值执行此操作htonl。ntohl在小端系统上,这些函数本质上反转字节,而在大端系统上,它们是无操作的。
例如,如果您有以下结构:
struct mystruct {
char f1[10];
uint32_t f2;
uint16_t f3;
};
Run Code Online (Sandbox Code Playgroud)
然后您将像这样序列化数据:
struct mystruct {
char f1[10];
uint32_t f2;
uint16_t f3;
};
Run Code Online (Sandbox Code Playgroud)
并像这样反序列化它:
// s points to the struct to serialize
// p should be large enough to hold the serialized struct
void serialize(struct mystruct *s, unsigned char *p)
{
memcpy(p, s->f1, sizeof(s->f1));
p += sizeof(s->f1);
uint32_t f2_tmp = htonl(s->f2);
memcpy(p, &f2_tmp, sizeof(f2_tmp));
p += sizeof(s->f2);
uint16_t f3_tmp = htons(s->f3);
memcpy(p, &f3_tmp, sizeof(f3_tmp));
}
Run Code Online (Sandbox Code Playgroud)
虽然您可以使用编译器特定的标志来打包结构,使其具有已知的大小,从而允许您使用memcpy整个结构并仅转换整数字段,但这样做意味着某些字段可能无法正确对齐,这在某些情况下可能会出现问题架构。无论结构体的整体大小如何,上述方法都将起作用。
| 归档时间: |
|
| 查看次数: |
515 次 |
| 最近记录: |