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_pton
is的输出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)
Run Code Online (Sandbox Code Playgroud)
那就是网络字节顺序。当您然后调用htonl
(这实际上并不正确 - 您应该调用ntohl
以从网络顺序转换为主机顺序但正如@ZanLynx 指出的那样,这两个函数在 x86 上是相同的),您将字节重新排序为:
b6 00 a8 c0
Run Code Online (Sandbox Code Playgroud)
但是然后您将该表单传递给printf
with%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
。)