Little Endian系统中Big Endian系统发送的结构数据的反序列化

时间:2011-02-10 14:56:35

标签: c sockets endianness

我有一个C程序,它通过套接字从UDP数据包中的大型机接收数据。 C程序的主机正在从Unix(big endian)变为Linux(little endian),程序不再有效。我目前无法更改大型机客户端程序。

程序执行recvfrom并将数据接收到char数组中。以前我们能够简单地将这个缓冲区转换为匹配从MF传递的结构,并且一切都很完美。现在,由于不同的字节对齐,映射到结构失败。这是结构和一些代码。

struct CCClntPkt
{
    unsigned short packet_type;
    unsigned short reply_socket;
    unsigned long  msg_ID;
    unsigned short msg_length;
    unsigned char  client_string[250];
};

以前用于将接收数据缓冲区强制转换为此结构的代码如下所示:

char BCpacket_in[MAX_PACKET];
struct CCClntPkt *pClntPkt;

<snip>

rcv_cnt = recvfrom(BCServerSocket, BCpacket_in,
                sizeof(BCpacket_in),0x0,(struct sockaddr *)&from,
                &fromlen);

if (rcv_cnt > 0)
{
    pClntPkt = (struct CCClntPkt *) &BCpacket_in;
}

我能够使用ntohs获取packet_type和reply_socket的正确值,但是字符字段client_string被破坏了。我还尝试在pragma pack(1)之前和pragma pack(0)之后放置结构,但似乎仍存在对齐问题。

我还试过从BCpacket_in转换值,并且能够获得packet_type和reply_socket的正确值,但无法弄清楚如何拉出ulong msg_ID。代码是:

packet_type = BCpacket_in[0] << 8;
packet_type |= BCpacket_in[1];

reply_to_socket = BCpacket_in[2] << 8;
reply_to_socket |= BCpacket_in[3];

/*
msg_ID = BCpacket_in[4] << 24;
msg_ID |= BCpacket_in[5] << 16;
msg_ID |= BCpacket_in[6] << 8;
msg_ID |= BCpacket_in[7];
*/

我在这一点上很难过,所以任何帮助都表示赞赏。我不是这个程序的原作者,我的C知识非常有限。我不介意做这项工作,所以我很感激提供任何相关的参考资料。谢谢!

4 个答案:

答案 0 :(得分:3)

您必须手动将收到的数据包(BCpacket_in)解析为struct CCClntPkt数据包,这是执行此操作的唯一可移植方式。使用ntohl(网络到主机long)函数系列正确处理字节顺序翻译;请参阅联机帮助页byteorder(3)endian(3)

这些函数假设所有数据包都通过线路作为big-endian发送,因为这是互联网标准。

答案 1 :(得分:2)

各种类型的大小可能与您的大端主机到新的小端主机不同。

如果您在两台主机上编译此程序,它会显示struct的尺寸和布局:

#include <stddef.h>
#include <stdio.h>

struct CCClntPkt
{
    unsigned short packet_type;
    unsigned short reply_socket;
    unsigned long  msg_ID;
    unsigned short msg_length;
    unsigned char  client_string[250];
};

int main()
{
    printf("sizeof(unsigned short) = %u\n", (unsigned)sizeof(unsigned short));
    printf("sizeof(unsigned long) = %u\n", (unsigned)sizeof(unsigned long));

    printf("offsetof(struct CCClntPkt, reply_socket) = %u\n", (unsigned)offsetof(struct CCClntPkt, reply_socket));
    printf("offsetof(struct CCClntPkt, msg_ID) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_ID));
    printf("offsetof(struct CCClntPkt, msg_length) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_length));
    printf("offsetof(struct CCClntPkt, client_string) = %u\n", (unsigned)offsetof(struct CCClntPkt, client_string));

    return 0;
}

特别是,新主机上的long很可能比旧主机长。这可能是使用<stdint.h>中的C99精确宽度类型的好地方 - 如果在原始主机上short是16位类型而long是32位输入,分别替换uint16_tuint32_t

然后,您可以使用ntohs()ntohl()来执行字节顺序更正。

答案 2 :(得分:1)

msg_ID = BCpacket_in[4] << 24;
msg_ID |= BCpacket_in[5] << 16;
msg_ID |= BCpacket_in[6] << 8;
msg_ID |= BCpacket_in[7];

这对我来说似乎是正确的。

尝试使用unsigned char作为缓冲区来保护自己免受签名问题的困扰。

BTW,是大端的msg_id,并且你确定偏移:正如你所说的“打包”在客户端不起作用,所以可以得出结论是使用包装规则将结构发送到线路。大型机。

答案 3 :(得分:0)

这是我通常对从网络发送/接收的打包结构所做的事情:

#define PACKED __attribute__((__packed__))
struct PACKED message { ... };

这是特定于GCC的,请参阅here。然后你必须弄清long的大小。它在32位和64位平台上有所不同。您可能希望改为使用stdint.h类型。另请查看信息__builtin_bswap32() and __builtin_bswap64() GCC intrinsics