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

27B*_*end 0 c sockets networking network-programming endianness

我对这个inet_pton()功能感到困惑。根据手册页

该函数将字符串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);
Run Code Online (Sandbox Code Playgroud)

输出:

htonl:c0a800b6
inet_pton:b600a8c0
inet_ntop:192.168.0.182
Run Code Online (Sandbox Code Playgroud)

inet_ptonis的输出b6.00.a8.c0,它转换为182.0.168.192,它也不同于 的输出htonl

由于htonl将主机字节顺序转换为网络字节顺序,所以如果inet_pton产生网络字节顺序,我想它们的输出应该是一样的?这是否意味着inet_pton实际产生主机字节顺序?

如果inet_pton已经产生network byte order,为什么我需要htonl获得正确的值?

Gil*_*ton 5

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

c0   a8  00  b6    (hex)
192  168 0   182   (dec)
Run Code Online (Sandbox Code Playgroud)

就是网络字节顺序。当您然后调用htonl(这实际上并不正确 - 您应该调用ntohl网络顺序转换为主机顺序但正如@ZanLynx 指出的那样,这两个函数在 x86 上是相同的),您将字节重新排序为:

b6 00 a8 c0
Run Code Online (Sandbox Code Playgroud)

但是然后您将该表单传递给printfwith%x作为格式。这告诉printf将四个字节解释为一个 32 位整数,但 x86 是一个小端机器,所以当它加载四个字节作为一个整数时,b6是最低阶字节,c0是最高阶字节,它产生你锯:

htonl:c0a800b6
Run Code Online (Sandbox Code Playgroud)

因此,一般来说,如果您有一个网络形式的 IPv4 地址,并且您想以对您(作为程序员)“有意义”的顺序快速显示它(用于调试或其他),您可以使用:

printf("%x\n", ntohl(a.sin_addr.s_addr));
Run Code Online (Sandbox Code Playgroud)

您还可以显示单个字节,因为它们以网络顺序驻留(这实际上是完全相同的事情,但可能更容易理解),只需使用unsigned char *(或等效的uint8_t *from <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]);
Run Code Online (Sandbox Code Playgroud)

(这里需要使用一个无符号的类型,以避免符号扩展。在上述声明中,每个字符将被提升到一个int在调用printf。如果你有一个签名的char包含,比如说,将0xC0,它通常是符号扩展成a 32 位int: 0xffffffc0。作为替代,您可以使用格式规范"%02hhx",明确告诉printf您确实将 a 传递给它char;然后它只会查看每个提升的最低字节int。)