我有一个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知识非常有限。我不介意做这项工作,所以我很感激提供任何相关的参考资料。谢谢!
答案 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_t
和uint32_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。