struct sockaddr_in bind()的成员字节顺序

时间:2017-05-31 03:07:52

标签: c sockets endianness

我正在学习套接字编程,并且对我在学习材料中使用htons()和函数族的不一致使用感到困惑。我目前正在阅读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()用于各自的示例中;它只是说“注意使用”所述功能。

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

2 个答案:

答案 0 :(得分:4)

  

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

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

现在几乎所有机器都使用8位字节和一致的big-endian或一致的little-endian格式。在前者hton[sl]ntoh[sl]都是无操作;在后者两者反转字节顺序,因此实际上做同样的事情,即使他们的预期语义不同。因此,使用错误的程序仍然适用于您可能运行程序的所有系统。

当设计套接字API时,情况并非总是如此;例如当时流行的PDP-11在某种程度上臭名昭着地使用了'middle-endian'(!)又名为'NUXI'的32位命令。

  

为什么adr_inet.sin_family上既没有使用ntohs()也没有使用htons()?

在古代,互联网协议栈只是几种(多达十几种)竞争网络技术中的一种。 family字段区分了这些不同协议的不同类型的sockaddr_*结构,这些协议并不都遵循大端的互联网“规则”,至少不一致。由于family没有通用网络表示,他们只是将其保留为主机顺序 - 这通常对主机软件更方便。

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

答案 1 :(得分:1)

  

为什么ntohs()上没有使用htons()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。