I came across this snippet of code which appeared in the guts of setting up a socket:
#define PORT xxxx
struct sockaddr_in self;
self.sin_family = PF_INET;
self.sin_port = htons(PORT);
I understand that we need to convert byte order of the data that we are transmitting over a network to Network Byte Order
but I don't get why we need to convert port number to that as well when setting up a socket. I mean, when we do bind, isn't it a "local" thing? Say the port we intend to be bound is 1 and the machine actually uses little endian; now since we converted it into Network Byte Order, wouldn't we be binding a totally different port to the socket?
The network byte order is defined to always be big-endian, which may differ from the host byte order on a particular machine. Using network byte ordering for data exchanged between hosts allows hosts using different architectures to exchange address information without confusion because of byte ordering.
unsigned long htonl(unsigned long hostlong) − This function converts 32-bit (4-byte) quantities from host byte order to network byte order.
Your port will simply be a number stored in an integer, and as such it must be converted into the proper form. The htons() function does just that. It takes one parameter and returns it in the expected network-byte order.
According to big endian byte ordering or network byte order the bits are transmitted in this order: bits 0-7 first, then bits 8-15, then 16-23 and bits 24-31 last.
I think let's assume you are using TCP. The port number is going to be in the packet header. That is going to be transmitted. So it will be in Network Byte Order.
Are you asking why you the application programmer does it instead of the library doing it internally? If so, the only technical advantage I can think of is that it allows the application to do the conversion once and cache it and use it many times without requiring many conversions.
On TCP you only need to use it once per connection and typically won't make millions of connections. But on UDP you use it every time you send a packet, and it's reasonable to assume you'd make millions or billions of such calls.
Then, for myriad calls to say sendto()
for UDP, or what have you, the re-ordered-if-necessary address is supplied to the OS which can copy it as-is directly into outgoing network packets.
The alternative of doing it in the kernel would require every call to sendto()
to take what the app knows as the same address over and over, and reconvert it every time.
Since sendto()
benefits from this, that was perhaps sufficient reason for them to have the rest of the API work the same way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With