For network portability, you need the following:
Pack the structure. For gcc and compatible compilers, do this with __attribute__((packed)) .
Do not use any elements except unsigned integers of a fixed size, other packed structures that satisfy these requirements, or arrays of any of them. Signed integers are also OK, unless your machine uses a two-pad representation.
Decide whether your protocol will use encodings with more or more integers. Make conversions when reading and writing these integers.
In addition, do not accept pointers to elements of the packed structure , except those that have a size of 1 or other nested packed structures. See this answer .
The following is a simple example of encoding and decoding. It assumes that the byte conversion functions hton8() , ntoh8() , hton32() and ntoh32() (the first two are non-op, but there for consistency).
#include <stdint.h> #include <inttypes.h> #include <stdlib.h> #include <stdio.h> // get byte order conversion functions #include "byteorder.h" struct packet { uint8_t x; uint32_t y; } __attribute__((packed)); static void decode_packet (uint8_t *recv_data, size_t recv_len) { // check size if (recv_len < sizeof(struct packet)) { fprintf(stderr, "received too little!"); return; } // make pointer struct packet *recv_packet = (struct packet *)recv_data; // fix byte order uint8_t x = ntoh8(recv_packet->x); uint32_t y = ntoh32(recv_packet->y); printf("Decoded: x=%"PRIu8" y=%"PRIu32"\n", x, y); } int main (int argc, char *argv[]) { // build packet struct packet p; px = hton8(17); py = hton32(2924); // send packet over link.... // on the other end, get some data (recv_data, recv_len) to decode: uint8_t *recv_data = (uint8_t *)&p; size_t recv_len = sizeof(p); // now decode decode_packet(recv_data, recv_len); return 0; }
Regarding byte order conversion functions, your htons() / ntohs() and htonl() / ntohl() can be used for 16- and 32-bit integers, respectively, for converting to / from large byte ordering. However, I do not know any standard function for 64-bit integers or for converting to / from little endian. You can use my byte conversion functions ; if you do, you must specify the byte order of your computer by specifying BADVPN_LITTLE_ENDIAN or BADVPN_BIG_ENDIAN .
As for signed integers, conversion functions can be implemented safely in the same way as those that I wrote and linked (by directly changing bytes); just replace unsigned with the signed one.
UPDATE : if you want to use an efficient binary protocol, but donβt like to mess with bytes, you can try something like Protocol Buffers ( C-implementation ). This allows you to describe the format of your messages in separate files and generate the source code that you use to encode and decode messages of the format you specify. I also implemented something similar to myself, but greatly simplified; see my BProto generator and a few examples (see .bproto and addr.h for a usage example).
Ambroz Bizjak
source share