C - inet_pton() 不产生网络字节顺序
C - inet_pton() not producing network byte order
我对 inet_pton()
函数感到困惑。根据 man page
This function converts the character string src into a network address structure in the af address family, then copies the network address structure to dst. The af argument must be either AF_INET or AF_INET6. dst is written in network byte order.
因此该函数生成网络字节顺序值。但是我得到了这个代码:
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
已经产生 network byte order
,为什么我需要 htonl
才能得到正确的值?
是的,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 地址,并且您希望以 "makes sense" 对您(作为程序员)的顺序快速显示它(用于调试或其他),您将使用:
printf("%x\n", ntohl(a.sin_addr.s_addr));
您还可以显示单个字节,因为它们位于网络顺序中(这实际上是完全相同的,但可能更容易理解),只需使用 unsigned char *
(或等效的 uint8_t *
来自 <stdint.h>
) 以打印各个字节:
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
。如果你有一个signed char
contains, say, 0xc0, it is typically will be sign-extended into a 32-bit int
: 0xffffffc0. 作为替代方案,您可以使用格式规范 "%02hhx"
明确告诉 printf
你真的传递给它一个 char
;然后它只会查看每个提升的 int
的最低位字节。)
我对 inet_pton()
函数感到困惑。根据 man page
This function converts the character string src into a network address structure in the af address family, then copies the network address structure to dst. The af argument must be either AF_INET or AF_INET6. dst is written in network byte order.
因此该函数生成网络字节顺序值。但是我得到了这个代码:
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
已经产生 network byte order
,为什么我需要 htonl
才能得到正确的值?
是的,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 地址,并且您希望以 "makes sense" 对您(作为程序员)的顺序快速显示它(用于调试或其他),您将使用:
printf("%x\n", ntohl(a.sin_addr.s_addr));
您还可以显示单个字节,因为它们位于网络顺序中(这实际上是完全相同的,但可能更容易理解),只需使用 unsigned char *
(或等效的 uint8_t *
来自 <stdint.h>
) 以打印各个字节:
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
。如果你有一个signed char
contains, say, 0xc0, it is typically will be sign-extended into a 32-bit int
: 0xffffffc0. 作为替代方案,您可以使用格式规范 "%02hhx"
明确告诉 printf
你真的传递给它一个 char
;然后它只会查看每个提升的 int
的最低位字节。)