// Muestra cómo guarda los enteros esta máquina y qué hace htons y htonl.
#define _POSIX_C_SOURCE 200809L

#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void print_bytes(const char *label, const void *value, size_t n) {
    const unsigned char *bytes = value;
    printf("%-22s", label);
    for (size_t i = 0; i < n; i++) {
        printf(" %02x", bytes[i]);
    }
    printf("\n");
}

// Parece una cabecera de 3 bytes, pero el compilador puede agregar relleno.
struct wrong_header {
    uint8_t type;
    uint16_t port;
};

int main(void) {
    uint16_t port = 9002;  // 0x232a
    uint16_t net_port = htons(port);
    uint32_t addr = 0x0A020110;  // 10.2.1.16
    uint32_t net_addr = htonl(addr);

    print_bytes("9002 en memoria:", &port, sizeof port);
    print_bytes("htons(9002):", &net_port, sizeof net_port);
    print_bytes("0x0a020110 en memoria:", &addr, sizeof addr);
    print_bytes("htonl(0x0a020110):", &net_addr, sizeof net_addr);

    // Copiar una struct a la red también copia el relleno que puso el compilador.
    printf("sizeof(struct wrong_header) = %zu, aunque sus campos suman 3\n",
           sizeof(struct wrong_header));

    // Lo correcto: definir el formato byte a byte y armarlo campo por campo.
    unsigned char wire[3];
    wire[0] = 7;
    memcpy(wire + 1, &net_port, sizeof net_port);
    print_bytes("cabecera en 3 bytes:", wire, sizeof wire);
    return EXIT_SUCCESS;
}
