C - inet_pton()无法产生网络字节顺序

4
我对inet_pton()函数感到困惑。根据 man 页面的描述,该函数将字符串src转换为af地址族中的网络地址结构,然后将网络地址结构复制到dst中。参数af必须是AF_INET或AF_INET6。 dst以网络字节序写入。因此该函数生成一个网络字节序的值。但我得到了以下代码:
 struct sockaddr_in a;
 char sip[20];
 inet_pton(AF_INET, "192.168.0.182", (void *)&a.sin_addr);
 inet_ntop(AF_INET, (void *)&a.sin_addr, sip, 20);

 printf("htonl:%08x\n", htonl(a.sin_addr.s_addr));
 printf("inet_pton:%08x\n", a.sin_addr.s_addr);
 printf("inet_ntop:%s\n", sip);

输出:

htonl:c0a800b6
inet_pton:b600a8c0
inet_ntop:192.168.0.182
inet_pton输出为b6.00.a8.c0,转换为182.0.168.192。其输出与htonl的输出不同。由于htonl将主机字节顺序转换为网络字节顺序,因此如果inet_pton生成网络字节顺序,则它们的输出应该相同。这是否意味着inet_pton实际上生成主机字节顺序?如果inet_pton已经生成网络字节顺序,我为什么需要htonl来获取正确的值?

@alk,这不是我想做的事情。我的意思是,如果“inet_pton”在“s_addr”中存储了“网络字节顺序”,那么“htonl(s_addr)== s_addr”是否成立? - 27Blend
1
@27Blend:不行,因为s_addr只是一个数字。它没有附加“网络字节顺序”的标签。 - Zan Lynx
@ZanLynx 的代码中,s_addr 存储了 inet_pton 函数的结果。根据 man 手册,该函数将值以 网络字节序 的形式写入。这是否意味着 s_addr 也是以 网络字节序 的形式写入的呢? - 27Blend
@27Blend 看起来它对我来说是网络字节顺序。在您的打印语句中,当您打印 inet_pton 的结果时,它会倒过来。可能是因为您在 x86 处理器上编译和运行,这不是网络字节顺序,因此会将其反转。 - Zan Lynx
@27Blend 你似乎对ntohl和htonl宏的作用也感到困惑。在Intel CPU上,它们的作用完全相同:颠倒字节顺序。在大端模式下的PowerPC或SPARC上,这些宏根本不起作用。如果你有一台PDP-11,它们必须执行不同的操作,因为PDP-11是中间端模式,即32位字以16位为单位进行颠倒。 - Zan Lynx
printf("inet_pton:%08x\n", a.sin_addr.s_addr);语句将会打印你传入的32位整数,它是以主机字节序为基准的,相对于网络字节序而言看起来是反过来的。虽然inet_pton函数确实会按照网络字节序存储字节,但是你的printf()语句却欺骗了你。 - nos
1个回答

7

是的, inet_pton 将地址字节以网络顺序放入目标缓冲区中。我们通过一个例子来看看会发生什么。使用地址“192.168.0.182”,inet_pton 生成这四个字节:

c0   a8  00  b6    (hex)
192  168 0   182   (dec)

那就是网络字节序。当你调用htonl时(实际上这不正确,你应该调用ntohl将数据从网络字节序转换为主机字节序,但正如@ZanLynx所指出的,在x86上这两个函数是相同的),你重新排列字节如下:
b6 00 a8 c0

然而,您将该表单传递给 printf 并使用 %x 作为格式。 这告诉 printf 将这四个字节解释为单个32位整数,但是 x86 是一个小端机器,因此当它将这四个字节作为整数加载时,b6 是最低位字节,c0 是最高位字节,这就产生了您看到的结果:

htonl:c0a800b6

因此,一般情况下,如果您拥有以网络形式表示的IPv4地址,并且希望快速以对程序员有意义的顺序显示它(用于调试或其他目的),则可以使用以下方式:

printf("%x\n", ntohl(a.sin_addr.s_addr));

您还可以显示单个字节,因为它们以网络顺序存储(实际上完全相同,但可能更容易理解),只需使用unsigned char *(或等效的<stdint.h>中的uint8_t *)来打印各个字节:

uint8_t *ipp = (void *)&a.sin_addr.s_addr;
printf("%02x %02x %02x %02x\n", ipp[0], ipp[1], ipp[2], ipp[3]);

在这里,您需要使用无符号类型来避免符号扩展。在上述语句中,每个字符都将在调用printf时提升为int类型。如果您使用包含0xc0的有符号char,它通常会被符号扩展为32位int:0xffffffc0。作为替代,您可以使用格式规范“%02hhx”,这明确告诉printf您实际上正在传递一个char;然后它只会查看每个提升int的最低字节。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接