iosobjective-cmacosintelhtonl

Is byte ordering the same across iOS devices, and does this make using htonl and ntohl unnecessary between iOS devices?


I was reading this example on how to use NSData for network messages.

When creating the NSData, the example uses:

unsigned int state = htonl(_state);
[data appendBytes:&state length:sizeof(state)];

When converting the NSData back, the example uses:

  [data getBytes:buffer range:NSMakeRange(offset, sizeof(unsigned int))];
  _state = ntohl((unsigned int)buffer);

Isn't it unnecessary to use htonl and ntohl in this example? - since the data is being packed / unpacked on iOS devices, won't the byte ordering be the same, making it unnecessary to use htonl and ntohl. - Isn't the manner in which it is used incorrect? The example uses htonl for packing, and ntohl for unpacking. But in reality, shouldn't one only do this if one knows that the sender or receiver is using a particular format?


Solution

  • The example uses htonl for packing, and ntohl for unpacking.

    This is correct.

    When a sender transfers data (integers, floats) over the network, it should reorder them to "network byte order". The receiver performs the decoding by reordering from network byte order to "host byte order".

    But in reality, shouldn't one only do this if one knows that the sender or receiver is using a particular format?

    Usually, a sender doesn't know the byte order of the receiver, and vice versa. Thus, in order to avoid ambiguity, one needs to define the byte order of the "network". This works well, provided sender and receiver actually do correctly encode/decode for the network.

    Edit:

    If you are concerned about performance of the encoding:

    On modern CPUs the required machine code for byte swapping is quite fast.

    On the language level, functions to encode and decode a range of bytes can be made quite fast as well. The Objective-C example in our post doesn't belong to those "fast" routines, though.

    For example, since the host byte order is known at compile time, ntohl becomes an "empty" function (aka "NoOp") if the host byte order equals the network byte order.

    Other byte swap utility functions, which extend on the ntoh family of macros for 64-bit, double and float values, may utilize C++ template tricks which may also become "NoOp" functions.

    These "empty" functions can then be further optimized away completely, which effectively results in machine code which just performs a move from the source buffer to the destination buffer.

    However, since the additional overhead for byte swapping is pretty small, these optimizations in case where swapping is not needed are only perceptible in high performance code. But your first statement:

    [data getBytes:buffer range:NSMakeRange(offset, sizeof(unsigned int))];
    

    is MUCH more expensive than the following statement

    _state = ntohl((unsigned int)buffer);
    

    even when byte swapping is required.