Same output for htonl() and ntohl() on an integer
Asked Answered
D

4

16

I ran the following program on little-endian [LE] machine [Linux, Intel processor]. I am unable to explain the 3 outputs in below code snippet. Since machine is LE, the value of a is stored as 0x78563412. When printing, it is displaying its actual value. Since its an LE machine, I expect ntohl() to be a no-op and display 0x78563412, which it is doing. However, I expect 0x12345678 for 2nd print statement containing htonl(). Can someone please help me understand why they are same?

int main() 
{
    int a = 0x12345678; 

    printf("Original - 0x%x\n", (a)); 
    printf("Network - 0x%x\n", htonl(a)); 
    printf("Host - 0x%x\n", ntohl(a)); 

    return 0;
}

Output:

Original - 0x12345678
Network - 0x78563412
Host - 0x78563412
Deli answered 10/7, 2012 at 23:11 Comment(0)
M
26

Since its an LE machine, I expect ntohl() to be a no-op

That's the mistake. Network byte order is big-endian, host byte order is little-endian. Therefore, both ntohl and htonl return a byte-swapped version of their input.

Remember, the point of htonl is that you can take an integer on the host, then write:

int i = htonl(a);

and the result is that the memory of i, when interpreted using network byte order, has the same value that a does. Hence, if you write the object representation of i to a socket and the reader at the other end expects a 4-byte integer in network byte order, it will read the value of a.

and display 0x78563412

Is this what you intended to write? If ntohl were a no-op (or rather, an identity function), then your third line necessarily would print the same thing as your first line, because you would have ntohl(a) == a. This is what happens on big-endian implementations, where your program prints:

Original - 0x12345678
Network - 0x12345678
Host - 0x12345678
Masterpiece answered 10/7, 2012 at 23:15 Comment(3)
As @Alok mentioned below, I was expecting the following behavior will always hold true: x == htonl(ntohl(x)). But this is not happening and your explanation was very helpful.Deli
@Bhaskar: Brian Roach's point is important too, then: you never calculated htonl(ntohl(a)). You calculated htonl(a) and ntohl(a).Masterpiece
demo for big-endian machines. Compared with the little-endian version on the same architecture it can be seen that no byte swap instructions are neededCorcoran
P
19

htonl and ntohl are exactly the same functions. They are supposed to satisfy htonl(ntohl(x)) == x. They are named differently only for documentation (you make it explicit that you are converting from host-to-network or the other way, even if it's the same). So, on a little-endian machine, they both perform byte-swapping, and on a big-endian machine, they are both no-ops.

Punctuation answered 10/7, 2012 at 23:15 Comment(5)
On a hypothetical "stupid-endian" C implementation where the bytes are neither big-endian (ordered 4321) nor little-endian (ordered 1234), but ordered for example 3214, you would still have htonl(ntohl(x)), but htonl and ntohl would not do the same thing, they'd be 8-bit rotations in opposite directions. I hope that no such architecture exists, but it could implement the sockets API thanks to the fact that htonl and ntohl are separate functions.Masterpiece
@SteveJessop you are right of course. This is probably the real reason we have two different functions. en.wikipedia.org/wiki/Endianness#Middle-endianPunctuation
@SteveJessop Yeah, I just figured that out and was trying to delete my comment, but you're fast! :-).Punctuation
@SteveJessop: I thought that was called VAX-endian?Ieper
@Ieper VAX uses little endian, only VAX floating-point is different. Anyway mixed endian writes 1234 as 2143 and not 3214Corcoran
A
6

Because you're passing a by value and therefore it isn't changed by either of those functions.

You're printing what htonl() and ntohl() are returning.

Edit to add: I missed where you thought one would be a no-op. It's not. Both are going to do the exact same thing on a LE machine; reverse the byte ordering. ntohl() is expecting you to be passing it a network byte ordered int

Anna answered 10/7, 2012 at 23:13 Comment(0)
F
0

In your program when you write int a; you know that a contains an host ordered integer, the program doesn't know that. You could as easily provide an int already containing a value in network order. Of course if you use any arithmetic operator on a value that is not in host order, the result will be incorrect from a network point of view if network order is not the same as host order.

But this is not so far fetched, the network ordered values are often kept exactly that way in low level structures, before being sent or just after being received.

What is wrong with your program is that when you call ntohl() your are promising to the ntohl() function that the int you are providing is some value stored in memory in network order. That's the contract. If it's not true, the function won't perform what you expect, and that is what you are seing.

As other explained on most systems (big or little but not stupid-endians) the two functions are usually identical, either a bytes reverse or a no-op.

Falkner answered 31/10, 2014 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.