1

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?

Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190

1 Answers1

3

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.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • Remark: On all big-endian platforms that I have seen, `ntohl` is defined as `#define ntohl(x) (x)`, so that becomes a "no op" even without C++ templates :-) – Martin R Nov 21 '13 at 08:50
  • In this case, if know that the sender and receiver are both iOS, can we do away with htonl and ntohl ? – Rahul Iyer Nov 21 '13 at 09:03
  • @MartinR You are right ;) - clarified what I really intended to say. – CouchDeveloper Nov 21 '13 at 10:59
  • @John byte order depends on the hardware, not the OS (well mostly, since ARM hardware can be configured). AFAIK, all devices with iOS do have the same byte order (and probably future devices will have the same too). That is, you CAN - but you SHOUL NOT for better compatibility in the future. – CouchDeveloper Nov 21 '13 at 11:06
  • So I guess the takeaway / best practice is, that one must always assume that data sent / to be sent over the network is in network byte order ? – Rahul Iyer Nov 21 '13 at 13:24
  • @John best practice: yes. The more experienced network programmers may expect that and will follow this rule, unless otherwise explicitly stated in the documentation. – CouchDeveloper Nov 21 '13 at 13:55