developer's handbook - overly obtuse, or best practice?

So, I'm learning about sockets and reading everything in sight. I started digging into the developer's handbook and after an entire section about the perils of byte ordering in socket connections, I was presented with this little gem:

Code:
sa.sin_addr.s_addr = htonl((((((132 << 8) | 163) << 8) | 96) << 8) | 3);

Of course, after I read the manpage for htonl, I knew that it took an address and stored it in network order - yippee. But it took a minute for me to realize what:

Code:
(((((132 << 8) | 163) << 8) | 96) << 8) | 3

was for. Seriously - 132.163.96.3 (his ip was different, but same effect)? Surely, there's an easier way to do network address to integer conversions... So, I went looking, and found:

Code:
sa.sin_addr.s_addr = inet_addr("132.163.96.3");

Hmm... seems reasonable (inet_addr returns an address in network order). But, this network coding is soooo new to me. What's the scoop? Is the example as obtuse as it seems or is it somehow a socket programming best practice?
 

Though the Freebsd man page for inet_addr(3) does not mention it being deprecated. It's definitely deprecated on Windows, if you care about such.

Edit: Another consideration is that the bit-shifting version is more efficient. It doesn't do any parsing. It doesn't have to convert from ASCII to the number represented. Bit shifting is typically implemented as an intrinsic function. These are highly optimized.
 
Ah, performance. Yes, it would be a compile time constant. I wasn't thinking in that vein. I can tell I'm going to have to shift my paradigm a bit. As for it being deprecated, I did find mention of it being discouraged in favor of more agnostic routines given the growing number of IPv6 netizens. The recommend from Beej’s Guide to Network Programming is to use inet_pton instead.
 
Ah, performance. Yes, it would be a compile time constant. I wasn't thinking in that vein.
It's also worth mentioning that htonl(3) and friends are mostly or entirely no-ops on big-endian machines. Conversion from a string, while fast on modern machines, is still slower than some simple bitwise arithmetic due to branching instructions required for parsing the string.

I can tell I'm going to have to shift my paradigm a bit. As for it being deprecated, I did find mention of it being discouraged in favor of more agnostic routines given the growing number of IPv6 netizens. The recommend from Beej’s Guide to Network Programming is to use inet_pton instead.
While IPv6 is a good motivating factor, that's not entirely the reason for avoiding inet_addr(3); the BUGS section at the end of the man page indicates an edge case that, while detectable, encourages use of a different conversion function instead. I definitely recommend inet_pton(3) instead. There's also the non-POSIX inet_aton(3), but it differs from inet_pton(3) in a few ways:
  • it doesn't work with IPv6;
  • it accepts non-canonical IPv4 addresses like "192.168.264", which means if you accidentally forget the last dot in "192.168.2.64" or "192.168.26.4", you are dealing with a completely different address (192.168.1.8);
  • it only returns 1 on conversion success and 0 on conversion failure with no errors specified; and
  • it is unavailable on Windows and may be unavailable on other systems as well.
 
On the subject of POSIX. I was looking around :), and came across ieee-1003.1-2008 and 1003.1.2017. They take some getting used to, but once I figured out the layout of the standards, the descriptions in XSH 2.10 Sockets is pretty concise and well written as are the definitions and headers in XBD 3 and 13. I also came across this wiki page https://wiki.freebsd.org/FreeBSD_and_Standards that talked about a few caveats to FreeBSD POSIX compliance. My questions are, should I strive to write POSIX compliant code? and is FreeBSD 12.x compliant to a degree that it is possible to stick to the POSIX stuff?
 
Ah, performance. Yes, it would be a compile time constant. I wasn't thinking in that vein. I can tell I'm going to have to shift my paradigm a bit...
I told you C is for near-the-metal screaming performance use cases. Use a higher level language if you want readability, and if you don't want to worry about platform specific stuff like lowest common denominator POSIX compliance or Windows compatibility.
 
Thanks Jose, I'm good with C. Its fun to play close to metal. If I wanted high level, I'd probably go w/Ruby or even Python.
 
Not sure of the context but there is also:

sa.sin_addr.s_addr = INADDR_ANY;

This will just allow you to listen on all addresses.
 
My questions are, should I strive to write POSIX compliant code? and is FreeBSD 12.x compliant to a degree that it is possible to stick to the POSIX stuff?
If you are programming in C or C++, directly to the operating system API ...
And you are hoping to port your code to other Unix-like OSes (for example Linux, or several others) ...
Then using POSIX as a guideline now will save you lots of trouble later.

Yes, both FreeBSD and Linux are highly POSIX compliant, in most areas. If you program to a modern C/C++ standard as far as the language is concerned, and POSIX for the OS interfaces, most likely moving the code between OSes will be quite easy (probably the biggest problem will be fixing the make files, between GNU and Berkeley make).

On the other hand, if you are intending for your code to not live long (educational exercises only), or always to be on FreeBSD, then POSIX matters less. I still like to use POSIX standards instead of man pages, because they are exceptionally clear, so I often end up programming to POSIX even if it isn't strictly necessary.

And on the third hand (can you say Zaphod Beeblebrox?): if you are programming in a high-level language like Python, which wraps the raw OS interfaces in standardized libraries, then the whole POSIX question becomes irrelevant.
 
Conversion from a string, while fast on modern machines, is still slower than some simple bitwise arithmetic due to branching instructions required for parsing the string.

Performance is king but ...

Unless you're parsing thousands of IP addresses per second, the performance of a function like inet_aton doesn't matter. The normal use case is to parse a few addresses from a config file - once - at start up.

There are plenty of other areas to focus on for performance.
 
Back
Top