C sin_addr.s_addr=INADDR_;需要htonl吗?
我遇到了两条线索: 一个使用C sin_addr.s_addr=INADDR_;需要htonl吗?,c,sockets,C,Sockets,我遇到了两条线索: 一个使用htonl,另一个不使用 哪个是对的?INADDR\u ANY是IPV4中的“任意地址”。该地址是点符号形式的0.0.0.0,因此在任何端点上都是十六进制形式的0x000000。将其通过htonl无效 现在,如果您想了解其他宏常量,请查看INADDR\u LOOPBACK它是否在您的平台上定义。很可能是这样一个宏: #define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ if (some_conditi
htonl
,另一个不使用
哪个是对的?
INADDR\u ANY
是IPV4中的“任意地址”。该地址是点符号形式的0.0.0.0
,因此在任何端点上都是十六进制形式的0x000000
。将其通过htonl
无效
现在,如果您想了解其他宏常量,请查看INADDR\u LOOPBACK
它是否在您的平台上定义。很可能是这样一个宏:
#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
if (some_condition)
sa.s_addr = htonl(INADDR_LOOPBACK);
else
sa.s_addr = INADDR_ANY;
(来自linux/in.h
,与winsock.h
中的等效定义)
因此,对于INADDR\u环回
,需要htonl
为了保持一致性,因此最好在所有情况下都使用
htonl
。由于其他常量(如INADDR\u LOOPBACK
)是按主机字节顺序排列的,因此我认为此族中的所有常量都应该应用htonl
,包括INADDR\u ANY
(注意:我在@Mat编辑时写下了这个答案;他的答案现在还说最好保持一致,并始终使用htonl
)
基本原理
如果您这样编写代码,将对未来的代码维护人员造成危害:
#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
if (some_condition)
sa.s_addr = htonl(INADDR_LOOPBACK);
else
sa.s_addr = INADDR_ANY;
如果我在查看这段代码,我会立即问为什么其中一个常量应用了htonl
,而另一个没有。我会将它报告为一个bug,不管我是否碰巧有“内部知识”,即INADDR\u ANY
始终为0,因此转换它是不可行的
您编写的代码不仅仅是关于拥有正确的运行时行为,它还应该尽可能明显,并且容易让人相信它是正确的。因此,您不应在INADDR\u ANY
周围去掉htonl
。我可以看到不使用htonl
的三个原因:
htonl
可能会冒犯有经验的套接字程序员,因为他们知道它不起任何作用(因为他们熟记常量的值)我通常不喜欢在已经有了“体面”的答案时回答。在这种情况下,我将破例,因为我添加到这些答案中的信息被误解了
INADDR\u ANY
定义为全零位IPv4地址、0.0.0
或0x00000000
。对此值调用htonl()
,将得到相同的值零。因此,从技术上讲,对该常量值调用htonl()
INADDR\u ALL
被定义为一个全一位IPv4地址,255.255.255
或0xffffff
。使用INADDR\u ALL
调用htonl()
将返回INADDR\u ALL
。同样,调用htonl()
在技术上也不是必需的
头文件中定义的另一个常量是INADDR\u LOOPBACK
,定义为127.0.0.1
,或0x7F000001
。此地址以网络字节顺序给出,如果没有htonl()
,则无法将其传递到套接字接口。必须对该常量使用htonl()
有些人认为,一致性和代码可读性要求程序员对任何名为INADDR.*
的常量使用htonl()
——因为有些常量是必需的。这些海报是错的
本线程中给出的示例如下:
if (some_condition)
sa.s_addr = htonl(INADDR_LOOPBACK);
else
sa.s_addr = INADDR_ANY;
引用“John Zwinck”的话:
“如果我在查看这段代码,我会立即质疑为什么其中一个常量应用了htonl,而另一个没有。我将其报告为一个bug,我是否碰巧有“内部知识”,即INADR_ANY始终为0,因此转换它是不可行的。我认为(并希望)许多其他维护人员也会这样做。”
如果我收到这样一个bug报告,我会立即扔掉它。这个过程将节省我很多时间,处理那些不具备INADDR\u ANY
始终为0的“基本最低知识”的人的bug报告。(这意味着知道INADDR\u ANY
等的值。不知何故违反了封装或其他非启动程序——在netcat
输出和内核中使用相同的数字。程序员需要知道实际的数值。不知道的人并不缺乏内部知识,他们缺乏该领域的基本知识。)
实际上,如果您有一个程序员维护套接字代码,而该程序员不知道INADDR_ANY和INADDR_ALL的位模式,那么您已经有麻烦了。将0包装在返回0的宏中是一种心态,它是无意义一致性的奴隶,不尊重领域知识
维护套接字代码不仅仅是理解C。如果您不理解INADDR\u环回
和INADDR\u ANY
在与netstat
输出兼容的级别上的区别,那么您在该代码中是危险的,不应该更改它
Zwinck提出的关于不必要使用htonl()
的草人论点:
INADDR\u ANY
的价值。这就像写一篇只有经验丰富的C程序员才能记住NULL
的值一样。“背诵”给人的印象是数字有点难记,可能是几个数字,比如127.0.0.1
。但是不,我们夸张地讨论了记忆“全零位”模式的困难
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(0);
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
const struct in_addr inaddr_loopback = { htonl(INADDR_LOOPBACK) };
static const struct in_addr inaddr_any = { 0 };
#if BYTE_ORDER == BIG_ENDIAN
static const struct in_addr inaddr_loopback = { 0x7f000001 };
#elif BYTE_ORDER == LITTLE_ENDIAN
static const struct in_addr inaddr_loopback = { 0x0100007f };
#else
#error Neither big endian nor little endian
#endif
struct in_addr address4 = { htonl(use_loopback ? INADDR_LOOPBACK : INADDR_ANY };
struct in_addr address4 = { use_loopback ? htonl(INADDR_LOOPBACK) : INADDR_ANY };
struct in_addr address4;
inet_pton(AF_INET, "127.0.0.1", &address4);
struct addrinfo *ai, hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_TCP };
int error;
error = getaddrinfo(NULL, 80, &hints, &ai);
if (error)
...
for (item = result; item; item = item->ai_next) {
sock = socket(item->ai_family, item->ai_socktype, item->ai_protocol);
if (sock == -1)
continue;
if (connect(sock, item->ai_addr, item->ai_addrlen) != -1) {
fprintf(stderr, "Connected successfully.");
break;
}
close(sock);
}
struct *result, hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_TCP };
getaddrinfo(NULL, 80, &hints, &ai);
sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
connect(sock, result->ai_addr, result->ai_addrlen);