sin_addr.s_addr = INADDR_ANY; 是否到底需要 htonl 吗?
I came across two threads:
Socket with recv-timeout: What is wrong with this code?
Reading / Writing to a socket using a FILE stream in c
one uses htonl
and the other doesn't.
Which is right?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
由于诸如 INADDR_LOOPBACK 之类的其他常量均采用主机字节顺序,因此我认为该系列中的所有常量都应应用 htonl ,包括 INADDR_ANY 。 。
(注意:我在 @Mat 编辑时写了这个答案;他的答案现在也说最好保持一致并始终使用
htonl
。)理由
这对未来是一个危险如果你这样写,你的代码的维护者:
如果我正在审查这段代码,我会立即质疑为什么其中一个常量应用了
htonl
而另一个却没有。我会将其报告为错误,无论我是否碰巧知道 INADDR_ANY 始终为 0,因此转换它是无操作的。您编写的代码不仅要具有正确的运行时行为,还应该尽可能明显并且易于相信它是正确的。因此,您不应删除
INADDR_ANY
周围的htonl
。我认为不使用 htonl 的三个原因是:Since other constants like
INADDR_LOOPBACK
are in host byte order, I submit that all the constants in this family should havehtonl
applied to them, includingINADDR_ANY
.(Note: I wrote this answer while @Mat was editing; his answer now also says it's better to be consistent and always use
htonl
.)Rationale
It is a hazard to future maintainers of your code if you write it like this:
If I were reviewing this code, I would immediately question why one of the constants has
htonl
applied and the other does not. And I would report it as a bug, whether or not I happened to have the "inside knowledge" thatINADDR_ANY
is always 0 so converting it is a no-op.The code you write is not only about having the correct runtime behavior, it should also be obvious where possible and easy to believe it is correct. For this reason you should not strip out the
htonl
aroundINADDR_ANY
. The three reasons for not usinghtonl
that I can see are:htonl
because they will know it does nothing (since they know the value of the constant by heart).INADDR_ANY
是 IPV4 中的“任意地址”。该地址采用点分符号表示为0.0.0.0
,因此在任何字节序上都采用十六进制表示为0x000000
。通过htonl
传递它没有任何效果。现在,如果您想了解其他宏常量,请查看
INADDR_LOOPBACK
(如果您的平台上已定义)。它很可能是这样的宏:(来自
linux/in.h
,winsock.h
中的等效定义)。因此对于 INADDR_LOOPBACK 来说,一个 htonl 是必要的。
为了保持一致性,在所有情况下最好使用
htonl
。INADDR_ANY
is the "any address" in IPV4. That address is0.0.0.0
in dotted notation, so0x000000
in hex on any endianness. Passing it throughhtonl
has no effect.Now if you want to wonder about other macro constants, look at
INADDR_LOOPBACK
if it's defined on your platform. Chances are it will be a macro like this:(from
linux/in.h
, equivalent definition inwinsock.h
).So for
INADDR_LOOPBACK
, anhtonl
is necessary.For consistency, it could thus be better to use
htonl
in all cases.两者都不正确,因为
INADDR_ANY
和htonl
都已被弃用,并导致复杂、丑陋的代码只能在 IPv4 上运行。切换到使用getaddrinfo
来满足所有套接字地址创建需求:将
"1234"
替换为您的端口号或服务名称。Neither is right, in the sense that both
INADDR_ANY
andhtonl
are deprecated, and lead to complex, ugly code that only works with IPv4. Switch to usinggetaddrinfo
for all of your socket address creation needs:Replace
"1234"
with your port number or service name.史蒂文斯在UNIX网络编程一书中一致使用
htonl(INADDR_ANY)
(我的副本是1990年的)。FreeBSD 当前发行版本在
netinet/in.h
中定义了 12 个INADDR_
常量; 12 个中的 9 个需要htonl()
才能正常运行。 (这 9 个是INADDR_LOOPBACK
和其他 8 个多播组地址,例如INADDR_ALLHOSTS_GROUP
和INADDR_ALLMDNS_GROUP
。)在实践中,是否使用 < code>INADDR_ANY 或
htonl(INADDR_ANY)
,除了htonl()
可能造成的性能影响。甚至可能不存在可能的性能影响 - 使用我的 64 位gcc 4.2.1
,打开任何级别的优化似乎都会激活编译时htonl() 常量转换。
理论上,某些实现者可以将 INADDR_ANY 重新定义为 htonl() 实际执行某些操作的值,但这样的更改会破坏数万个现有的代码在那里并且无法在“现实世界”中生存......存在太多代码,这些代码显式或隐式依赖于被定义为某种零值整数的 INADDR_ANY 。当史蒂文斯写道时,他可能并不想让任何人假设 INADDR_ANY 始终为零:
Stevens uses
htonl(INADDR_ANY)
consistently in the book UNIX Network Programming (my copy is from 1990).The current release version of FreeBSD defines 12
INADDR_
constants innetinet/in.h
; 9 of the 12 requirehtonl()
for proper functionality. (The 9 areINADDR_LOOPBACK
and 8 other multicast group addresses such asINADDR_ALLHOSTS_GROUP
andINADDR_ALLMDNS_GROUP
.)In practice, it makes no difference whether you use
INADDR_ANY
orhtonl(INADDR_ANY)
, other than the possible performance hit fromhtonl()
. And even that possible performance hit may not exist -- with my 64-bitgcc 4.2.1
, turning on any level of optimization at all seems to activate compile-timehtonl()
conversion of constants.In theory it would be possible for some implementer to redefine
INADDR_ANY
to a value wherehtonl()
actually does something, but such a change would break tens of thousands of existing pieces of code out there and wouldn't survive in the "real world"... Too much code exists which depends explicitly or implicitly onINADDR_ANY
being defined as some sort of zero-valued integer. Stevens likely didn't intend for anyone to assume thatINADDR_ANY
is always zero when he wrote:本来打算将其添加为注释,但它有点啰嗦...
我认为从答案和注释中可以清楚地看出,需要对这些常量使用
htonl()
(尽管在INADDR_ANY
和INADDR_NONE
上调用它相当于无操作)。我认为出现混淆的问题是它没有在文档中明确指出 - 如果我只是错过了它,请有人纠正我,但我没有在手册页中看到,也没有在包含标头中明确指出声明INADDR_*
的定义按主机顺序排列。同样,对于INADDR_ANY
、INADDR_NONE
和INADDR_BROADCAST
来说没什么大不了的,但对于来说却很重要 INADDR_LOOPBACK
。现在,我已经用 C 语言完成了相当多的低级套接字工作,但是环回地址很少(如果有的话)在我的代码中使用。虽然这个话题已经有一年多了,但这个问题今天突然出现并咬住了我,这是因为我错误地假设包含头中定义的地址是按网络顺序排列的。不知道为什么我有这个想法 - 可能是因为
in_addr
结构需要具有网络顺序的地址,inet_aton
和inet_addr
返回它们的值网络顺序,所以我的逻辑假设是这些常量可以按原样使用。快速拼凑出 5 行代码来检验这一理论,结果却告诉我事实并非如此。如果任何当权者碰巧看到这一点,我会建议明确指出这些值实际上是按主机顺序而不是网络顺序,并且htonl()
应该应用于它们。为了保持一致性,我还建议,正如其他人已经在这里所做的那样,将htonl()
用于所有INADDR_*
值,即使它什么也不做到值。Was going to add this as a comment, but it got a little long-winded ...
I think it's clear from the answers and the commentary here that
htonl()
needs to be used on these constants (albeit that calling it onINADDR_ANY
andINADDR_NONE
are tantamount to no-ops). The problem that I see as to where the confusion arises is that it is not explicitly called out in documentation - someone please correct me if I simply missed it, but I have not seen in the man pages, nor in the include header where it explicitly states that the defines forINADDR_*
are in host order. Again, not a big deal forINADDR_ANY
,INADDR_NONE
, andINADDR_BROADCAST
, but it is significant forINADDR_LOOPBACK
.Now, I've done quite a bit of low-level socket work in C, but the loopback address rarely, if ever, gets used in my code. Although this topic is over a year old, this very problem just jumped up to bite me in the behind today, and it was because I went on the mistaken assumption that the addresses defined in the include header are in network order. Not sure why I had that idea - probably because the
in_addr
structure needs to have the address in network order,inet_aton
andinet_addr
return their values in network order, and so my logical assumption was that these constants would be usable as-is. Throwing together a quick 5-liner to test that theory showed me otherwise. If any of the powers-that-be happen to see this, I would make the suggestion to explicitly call out that the values are, in fact, in host order, not network order, and thathtonl()
should be applied to them. For consistency's sake, I would also suggest, as others have done so already here, thathtonl()
be used for all of theINADDR_*
values, even if it does nothing to the value.让我们总结一下,因为之前的答案似乎都不是最新的,而且我可能不会是最后一个看到这个问题页面的人。对于在 INADDR_ANY 常量周围使用 htonl 或完全避免使用 htonl 存在意见。
如今(现在已经有一段时间了)系统库大多支持 IPv6,因此我们既使用 IPv4,也使用 IPv6。 IPv6 的情况要容易得多,因为数据结构和常量不受字节顺序的影响。人们会使用“in6addr_any”和“in6addr_loopback”(都是 struct in6_addr 类型),并且它们都是网络字节顺序中的常量对象。
了解为什么 IPv6 不会遇到同样的问题(如果 IPv4 地址被定义为四字节数组,它们也不会遇到问题):
对于 IPv4,最好将 'inaddr_any' 和 'inaddr_loopback' 也设置为 'struct in_addr ' 常量(以便它们也可以与 memcmp 进行比较或使用 memcpy 进行复制)。事实上,在您的程序中创建它们可能是一个好主意,因为它们不是由 glibc 和其他库提供的:
对于 glibc,这只适用于我在函数内部的情况(而且我无法将其设为静态) code>),因为
htonl
不是宏,而是普通函数。问题是 glibc (与其他答案中声称的相反)不提供 htonl 作为宏,而是作为函数。因此,您必须:
这将是对标头的一个非常好的补充,然后您可以像使用 IPv6 一样轻松地使用 IPv4 常量。
但为了实现它,我必须使用一些常量来初始化它。当我确切地知道各个字节时,我不需要任何常量。正如有些人声称
htonl()
对于计算结果为零的常量来说是多余的,其他任何人都可以声称常量本身也是多余的。他是对的。在代码中我更喜欢显式而不是隐式。因此,如果这些常量(如 INADDR_ANY、INADDR_ALL、INADDR_LOOPBACK)都一致地采用主机字节顺序,那么只有这样对待它们才是正确的。例如(当不使用上述常量时):
当然,您可以说您不需要为 INADDR_ANY 调用
htonl
,因此您可以:但是当忽略常量的字节顺序时因为无论如何它都是零,那么我根本看不到使用该常量的逻辑。这同样适用于 INADDR_ALL,因为也可以轻松输入 0xffffffff;
解决这个问题的另一种方法是完全避免直接设置这些值:
这会增加一点无用的处理,但它没有字节顺序问题,而且对于 IPv4 和 IPv6 来说几乎是相同的(只需更改地址字符串)。
但问题是你为什么要这么做。如果您想
connect()
到 IPv4 本地主机(但有时到 IPv6 本地主机,或只是任何主机名), getaddrinfo() (在答案之一中提到)会更好,因为:到匹配的
struct addrinfo
记录的列表。每个
struct addrinfo
都包含一个指向struct sockaddr
的多态指针,您可以直接与connect()
一起使用它。因此,您不需要关心 struct sockaddr_in 的构造、到 struct sockaddr 的类型转换(通过指针)等。struct addrinfo *ai, 提示 = { .ai_family = AF_INET };
getaddrinfo(0, "1234", &hints, &ai);
记录依次包含
connect()
调用所需的指针多态struct sockaddr
结构。因此,结论是:
1) 标准 API 无法提供直接可用的 struct in_addr 常量(而是按主机顺序提供相当无用的无符号整数常量)。
当您确定您的查询具有足够的选择性以至于只返回一个结果时,您可以执行以下操作(为简洁起见,省略错误处理):
如果您担心
getaddrinfo()
可能比使用慢得多对于常量,系统库是解决这个问题的最佳位置。当service
为 null 且hints.ai_family
设置时,一个好的实现会返回请求的环回地址。Let's summarize it a little bit, as none of the previous answers seems to be up to date and I may not be the last person who will see this question page. There have been opinions both for and against usage of htonl around INADDR_ANY constant or avoiding it entirely.
Nowadays (and it's been nowadays for quite some time now) system libraries are mostly IPv6 ready, so we use IPv4 as well as IPv6. The situation with IPv6 is much easier as the data structures and constants don't suffer from byte order. One would use 'in6addr_any' as well as 'in6addr_loopback' (both struct in6_addr type) and both of them are constant objects in the network byte order.
See why IPv6 doesn't suffer from the same problem (if IPv4 addresses were defined as four byte arrays they wouldn't suffer either):
For IPv4, it would be nice to also have 'inaddr_any' and 'inaddr_loopback' as 'struct in_addr' constants (so that they can also be compared with memcmp or copied with memcpy). Indeed it might be a good idea to create them in your program as they aren't provided by glibc and other libraries:
With glibc, this only works for me inside a function (and I can't make it
static
), ashtonl
is not a macro but an ordinary function.The problem is that glibc (in contrast with what was claimed in other answers) doesn't provide htonl as a macro but rather as a function. Therefore you would have to:
That would be a really nice addition to the headers and then you could work with IPv4 constants as easily as you can with IPv6.
But then to implement that, I had to use some constants to initialize that. When I know the respective bytes exactly, I don't need any constants. Just as some people claim that
htonl()
is redundant for a constant that evaluates to zero, anyone else could claim that the constant itself is redundant as well. And he would be right.In the code I prefer to be explicit than implicit. Therefore if those constants (like INADDR_ANY, INADDR_ALL, INADDR_LOOPBACK) are all consistently in host byte order, then it's only correct if you treat them like that. See for example (when not using the above constant):
Of course you could say that you don't need to call
htonl
for INADDR_ANY and therefore you could:But then when ignoring the byte order of the constant because it's zero anyway, then I don't see much logic in using the constant at all. And the same applies to INADDR_ALL, as it's easy to type 0xffffffff as well;
Another way to get around it is to avoid setting those values directly altogether:
This adds a little bit of useless processing but it has no byte order problems and it is virtually the same for IPv4 and IPv6 (you just change the address string).
But the question is why are you doing that at all. If you want to
connect()
to IPv4 localhost (but sometimes to IPv6 localhost, or just any hostname), getaddrinfo() (mentioned in one of the answers) is much better for that, as:It is a function used for translating any hostname/service/family/socktype/protocol a
to a list of matching
struct addrinfo
records.Each
struct addrinfo
includes a polymorphic pointer tostruct sockaddr
that you can directly use withconnect()
. Therefore you don't need to care about the construction ofstruct sockaddr_in
, typecasting (via a pointer) tostruct sockaddr
, etc.struct addrinfo *ai, hints = { .ai_family = AF_INET };
getaddrinfo(0, "1234", &hints, &ai);
record that in turn include pointers polymorphic
struct sockaddr
structures which you need for theconnect()
call.So, the conclusion is:
1) The standard API fails to provide directly usable
struct in_addr
constants (instead it provides rather useless unsigned integer constants in host order).When you are sure your query is selective enough that it only returns one result, you could do (omitting error handling for brevity) the following:
If you're afraid
getaddrinfo()
might be significantly slower than using the constants, the system library is the best place to fix that. A good implementation would just return the requested loopback address whenservice
is null andhints.ai_family
is set.当已经有“体面”的答案时,我通常不喜欢回答。在这种情况下,我将破例,因为我添加到这些答案中的信息被误解了。
INADDR_ANY
定义为全零位 IPv4 地址、0.0.0.0
或0x00000000
。对此值调用htonl()
将得到相同的值,即零。因此,在这个常量值上调用htonl()
在技术上是没有必要的。INADDR_ALL
定义为全一位 IPv4 地址,255.255.255.255
或0xFFFFFFFF
。使用INADDR_ALL
调用htonl()
将返回INADDR_ALL
。同样,调用htonl()
在技术上并不是必需的。头文件中定义的另一个常量是 INADDR_LOOPBACK ,定义为 127.0.0.1 或 0x7F000001 。该地址以网络字节顺序给出,如果没有
htonl()
则无法传递到套接字接口。您必须将htonl()
与此常量一起使用。有些人建议,一致性和代码可读性要求程序员对任何名为
INADDR_*
的常量使用htonl()
——因为其中一些常量需要它。这些海报是错误的。该线程中给出的示例是:
引用“John Zwinck”:
“如果我正在审查这段代码,我会立即质疑为什么其中一个常量应用了 htonl 而另一个没有应用。我将其报告为错误,无论我是否碰巧知道 INADDR_ANY 始终是“内部知识” 0,所以转换它是一个无操作,我认为(并希望)许多其他维护者也会这样做。”
如果我收到这样的错误报告,我会立即扔掉它。这个过程会节省我很多时间,从那些不具备
INADDR_ANY
始终为 0 的“最低基本知识”的人那里获取错误报告。(建议了解INADDR_ANY 的值
等人以某种方式违反了封装或其他不可行的内容 - 在netcat
输出中和内核内部使用了相同的数字。 WHO不知道并不缺乏内部知识,他们缺乏基本该领域的知识。)真的,如果你有一个程序员维护套接字代码,而那个程序员不这样做不知道 INADDR_ANY 和 INADDR_ALL 的位模式,你已经有麻烦了。将 0 包装在返回 0 的宏中是一种心态,它是无意义的一致性的奴隶,并且不尊重领域知识。
维护套接字代码不仅仅是了解 C。如果您在与
netstat
输出兼容的级别上不了解INADDR_LOOPBACK
和INADDR_ANY
之间的区别,那么您的代码很危险,不应该更改它。Zwinck 提出的关于不必要使用 htonl() 的稻草人论点:
这是一个稻草人的论点,因为我们有这样的描述:有经验的套接字程序员都牢记
INADDR_ANY
的值。这就像写道只有有经验的 C 程序员才能记住NULL
的值。 “背”字给人的感觉是这个数字有点难记,可能就是几个数字,比如127.0.0.1
。但不,我们正在夸张地讨论记住“全零位”和“全一位”模式的难度。考虑到这些数值出现在例如 netstat 和其他系统实用程序的输出中,并且还考虑到其中一些值出现在 IP 标头中,因此不存在称职的套接字程序员不知道这些价值观,无论是用心还是用脑。事实上,在不了解这些基础知识的情况下尝试套接字编程可能会对网络可用性造成危险。
这个论点本意是荒谬的、不屑一顾的,所以不需要太多反驳。
很难知道这个论点从何而来。这可能是试图向反对派提供看似愚蠢的论点。无论如何,当您提供常量并使用典型的 C 编译器时,不使用
htonl()
宏对性能没有任何影响 - 无论哪种情况,常量表达式都会简化为常量。不将 htonl() 与 INADDR_ANY 一起使用的一个原因是,大多数有经验的套接字程序员都知道不需要它。更重要的是:那些不知道的程序员需要学习。使用 htonl() 没有额外的“成本”,问题是建立编码标准的成本,该标准会助长对此类至关重要的值的无知。
根据定义,封装会助长无知。这种无知正是使用封装接口的通常好处——知识是昂贵且有限的,因此封装通常是好的。问题是:通过封装可以最好地增强哪些编程工作?是否存在封装可以保护的编程任务?
从技术上讲,使用 htonl() 并不是不正确的,因为它对此值没有影响。然而,关于您应该使用它的论点可能会产生误导。
有些人认为更好的情况是开发人员不需要知道 INADDR_ANY 全部为零等等。这片无知的土地更糟,而不是更好。考虑到这些“神奇值”在 TCP/IP 的各种接口中使用。例如,在配置 Apache 时,如果您只想侦听 IPv4(而不是 IPv6),则必须指定:
我遇到过程序员错误地提供了本地 IP 地址而不是
INADDR_ANY
(0.0 .0.0)以上。这些程序员不知道INADDR_ANY
是什么,他们可能会在使用时将其包装在htonl()
中。这是抽象思维和封装的土地。“封装”和“抽象”的思想已被广泛接受并广泛应用,但它们并不总是适用。在 IPv4 寻址领域,将这些常量值视为“抽象”是不合适的——它们会直接转换为线路上的位。
我的观点是:
INADDR_ANY
与htonl()
没有“正确”的用法——两者是等效的。我不建议采用以任何特定方式使用该值的要求,因为INADDR_X
常量系列只有四个成员,并且其中只有一个INADDR_LOOPBACK
有一个值根据字节顺序而不同。最好只知道这一事实,而不是建立一个对值的位模式“视而不见”的使用值标准。在许多其他 API 中,程序员在不知道 API 使用的常量的数值或位模式的情况下继续操作是很有价值的。对于套接字 API,这些位模式和值用作输入并普遍显示。最好以数字方式了解这些值,而不是花时间考虑对它们使用
htonl()
。尤其是在用 C 语言编程时,套接字 API 的大多数“使用”都涉及获取其他人的源代码并对其进行调整。这是在接触使用
INADDR_ANY
的行之前了解它如此重要的另一个原因。I don't usually like to answer when there is already a "decent" answer. In this case, I am going to make an exception because information I added to these answers is being misconstrued.
INADDR_ANY
is defined as an all-zero-bits IPv4 address,0.0.0.0
or0x00000000
. Callinghtonl()
on this value will result in the same value, zero. Therefore, callinghtonl()
on this constant value is not technically necessary.INADDR_ALL
is defined as an all-one-bits IPv4 address,255.255.255.255
or0xFFFFFFFF
. Callinghtonl()
withINADDR_ALL
will returnINADDR_ALL
. Again, callinghtonl()
is not technically necessary.Another constant defined in the header files is
INADDR_LOOPBACK
, defined as127.0.0.1
, or0x7F000001
. This address is given in network-byte order, and cannot be passed to the sockets interface withouthtonl()
. You must usehtonl()
with this constant.Some would suggest that consistency and code readability demand that programmers use
htonl()
for any constant namedINADDR_*
-- because it is required for some of them. These posters are wrong.An example given in this thread is:
Quoting from "John Zwinck":
"If I were reviewing this code, I would immediately question why one of the constants has htonl applied and the other does not. And I report it as a bug, whether or not I happened to have the "inside knowledge" that INADDR_ANY is always 0 so converting it is a no-op. And I think (and hope) many other maintainers would do the same."
If I were receiving such a bug report, I would immediately throw it away. This process would save me a lot of time, fielding bug reports from people who don't have the "basic minimum knowledge" that
INADDR_ANY
is always 0. (Suggesting that knowing the values ofINADDR_ANY
et al. somehow violates encapsulation or whatever is another non-starter -- the same numbers are used in thenetcat
output and inside the kernel. Programmers need to know the actual numerical values. People who don't know aren't lacking inside knowledge, they are lacking basic knowledge of the area.)Really, if you have a programmer maintaining sockets code, and that programmer doesn't know the bit patterns of INADDR_ANY and INADDR_ALL, you are already in trouble. Wrapping 0 in a macro which returns 0 is the kind of mentality that is a slave to meaningless consistency and doesn't respect domain knowledge.
Maintaining sockets code is about more than understanding C. If you don't understand the difference between
INADDR_LOOPBACK
andINADDR_ANY
at a level compatible withnetstat
output, then you are dangerous in that code and shouldn't be changing it.Straw-man arguments proposed by Zwinck regarding the needless use of
htonl()
:This is a straw argument because we have a portrayal that experienced socket programmers know the value of
INADDR_ANY
by heart. This is like writing that only an experienced C programmer knows the value ofNULL
by heart. Writing "by heart" gives the impression that the number is slight difficult to memorize, perhaps a few digits, such as127.0.0.1
. But no, we are hyperbolically discussing the difficult of memorizing the patterns named "all zero bits" and "all one bits."Considering that these numerical values appear in the output of, e.g.,
netstat
and other system utilities, and also considering that some of these values appear in IP headers, there is no such thing as a competent sockets programmer who does not know these values, whether by heart or by brain. In fact, attempting sockets programming without knowing these basics can be dangerous to the network availability.This argument is intended to be absurd and dismissive, so it doesn't need much refuting.
It's hard to know where this argument came from. It could be an attempt to supply stupid-seeming arguments to the opposition. In any case, not using the
htonl()
macro makes no difference to performance when you provide a constant and use a typical C compiler -- the constant expressions are reduced to a constant in either case.A reason not to use
htonl()
with INADDR_ANY is that most experienced sockets programmer knows that it is not needed. What's more: those programmers who do not know need to learn. There is no extra "cost" with use ofhtonl()
, the trouble is the cost of establishing a coding standard which fosters ignorance of such critically important values.By definition, encapsulation fosters ignorance. That very ignorance is the usual benefit of using an encapsulated interface -- knowledge is expensive and finite, therefore encapsulation is usually good. The question becomes: which efforts of programming are best enhanced via encapsulation? Are there programming tasks which are disserved by encapsulation?
It is not technically incorrect to use
htonl()
, because it has no effect on this value. However, arguments that you should use it may be misleading.There are those who would argue that a better situation would be one in which the developer did not need to know that
INADDR_ANY
is all zeroes and so on. This land of ignorance is worse, not better. Consider that these "magic values" are used throughout various interfaces with TCP/IP. For example, when configuring Apache, if you would like to listen only to IPv4 (and not IPv6), you must specify:I have run into programmers who mistakenly supplied the local IP address instead of
INADDR_ANY
(0.0.0.0) above. These programmers don't know whatINADDR_ANY
is, and they probably wrap it inhtonl()
while they are at it. This is the land of abstaction-thinking and encapsulating.The ideas of "encapsulation" and "abstraction" have been widely accepted and too-widely applied, but they do not always apply. In the domain of IPv4 addressing, it's not appropriate to treat these constant values as "abstract" -- they are converted directly into bits on the wire.
My point is this: there is no "correct" usage of
INADDR_ANY
withhtonl()
-- both are equivalent. I would not recommend adopting a requirement that the value be used any particular way, because theINADDR_X
family of constants only have four members, and only one of them,INADDR_LOOPBACK
has a value which is different depending on byte ordering. It is better to just know this fact than to establish a standard for using the values which turns a "blind eye" to the bit patterns of the values.In many other APIs, it is valuable for programmers to proceed without knowing the numeric value or bit patterns of constants used by the APIs. In the case of the sockets API, these bit patterns and values are used as input and displayed pervasively. It is better to know these values numerically than to spend time thinking about using
htonl()
on them.When programming in C, especially, most "use" of the sockets API involves grabbing some other person's source code, and adapting it. This is another reason it is so important to know what
INADDR_ANY
is before touching a line which uses it.