bind() 的结构 sockaddr_in 成员字节顺序

struct sockaddr_in member byte order for bind()

我正在学习套接字编程,我对 htons() 和函数族在学习中的不一致使用感到困惑 material。我目前正在阅读 this site,其中包含以下代码段:

001 1:       struct sockaddr_in adr_inet;
002 2:       int adr_len;
003 3:
004 4:       memset(&adr_inet,0,sizeof adr_inet);
005 5:
006 6:       adr_inet.sin_family = AF_INET;
007 7:       adr_inet.sin_port = ntohs(0);
008 8:       adr_inet.sin_addr.s_addr = ntohl(INADDR_ANY);
009 9:       adr_len = sizeof adr_inet;

在同一提到的站点上进一步向下的后续示例具有以下代码段:

030 30:      struct sockaddr_in adr_inet;/* AF_INET */
...
042 42:      /* Create an AF_INET address */
043 43:      memset(&adr_inet,0,sizeof adr_inet);
044 44:
045 45:      adr_inet.sin_family = AF_INET;
046 46:      adr_inet.sin_port = htons(9000);
047 47:      memcpy(&adr_inet.sin_addr.s_addr,IPno,4);
048 48:      len_inet = sizeof adr_inet;
049 49:
050 50:      /* Now bind the address to the socket */
051 51:      z = bind(sck_inet,
052 52:          (struct sockaddr *)&adr_inet,
053 53:          len_inet);

问题:

为什么 ntohs() 在第一个实例中用于 adr_inet.sin_port,而在第二个实例中使用 htons()

问题:

为什么 ntohs()htons() 都没有用在 adr_inet.sin_family 上?

提到的站点没有解释为什么 ntohs()htons() 在各自的示例中使用;它只对 "note the use of" 表示函数。

我了解字节顺序,网络字节顺序是大端顺序。我的问题更多是关于您什么时候想要 struct sockaddr_in 的网络成员与主机字节顺序?在第二个代码示例中,.sin_port 在传递给 bind() 之前设置为网络字节顺序。我可以看到以网络或主机字节顺序将数据传递给此函数的情况:bind() 是一个 "network-related" 函数,因此它可能需要以网络字节顺序的数据;另一方面 bind() 是在主机上执行的,那么为什么不应该按主机字节顺序接受数据呢?

Why is ntohs() used on adr_inet.sin_port in the first instance, but htons() in the second?

第一个是错误的,但在实践中无论如何都是有效的。

如今几乎所有机器都使用 8 位字节和一致的大端格式或一致的小端格式。对于前者,hton[sl]ntoh[sl] 都是空操作;在后者 both 上颠倒了字节顺序,因此即使它们的预期语义不同,实际上也做同样的事情。因此,使用错误的一个仍然适用于您可能 运行 一个程序的所有系统。

在设计插座 API 时,情况并非总是如此;例如,当时流行的 PDP-11 有点臭名昭著地使用 'middle-endian' (!) aka 'NUXI' order for 32-bit.

Why is neither ntohs() nor htons() used on adr_inet.sin_family?

同样在古代,互联网协议栈只是几种(最多十几种)竞争网络技术中的一种。 family 字段为这些不同的协议区分了不同类型的 sockaddr_* 结构,这些协议并不都遵循互联网 'rule' 的 big-endian,至少不一致。由于 family 没有通用网络表示,他们只是按主机顺序保留它——这通常对主机软件来说更方便。

现在实际上除了 INET、INET6 和 UNIX 之外,没有人使用任何系列 -- 后者可以通过在文件系统中使用命名管道来替换,这通常至少同样好。

Why is neither ntohs() nor htons() used on adr_inet.sin_family?

adr_inet.sin_family被初始化为AF_INET的值。这在 bits/socket.h 中定义(在您的示例中由 netinet/in.h 调用)为:

#define PF_INET     2   /* IP protocol family.  */

然后,

#define AF_INET     PF_INET

所以AF_INET只是程序将关联套接字识别为TCP/IP连接的一种方式。它实际上不会保存 IPv4 地址本身的值,因此无需对其执行字节序转换。

另外,请注意,在 C 的较新迭代中,netinet/in.h 有一条评论如下:

/* Functions to convert between host and network byte order.

   Please note that these functions normally take `unsigned long int' or
`unsigned short int' values as arguments and also return them.  But
this was a short-sighted decision since on different systems the types
may have different representations but the values are always the same.  */ 
extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
        __THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
        __THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
        __THROW __attribute__ ((__const__));

而您引用的网站引用了 unsigned longunsigned short 的旧用法 转换函数的数据类型。因此,您可能会遇到来自该站点的 运行 代码问题,如果 您正在使用较新版本的 C.