Structs

struct addrinfo

addrinfo es una estructura utilizada en programación de sockets para almacenar información sobre direcciones de red y servidores. Esta estructura es útil cuando estás escribiendo código que necesita configurar sockets de red, como crear un servidor o un cliente, y deseas almacenar toda la información relevante en un solo lugar.

struct addrinfo {
    int              ai_flags;      // Opciones para la búsqueda
    int              ai_family;     // Familia de direcciones (IPv4 o IPv6 o AF_UNSPEC)
    int              ai_socktype;   // Tipo de socket (SOCK_STREAM o SOCK_DGRAM)
    int              ai_protocol;   // Protocolo utilizado (e.g., IPPROTO_TCP)
    size_t        ai_addrlen;    // Longitud de la dirección
    struct sockaddr *ai_addr;       // Puntero a la dirección
    char            *ai_canonname;  // Nombre canónico del host
    struct addrinfo *ai_next;       // Puntero al siguiente nodo en la lista
};
  • ai_flags: Se utiliza para especificar opciones adicionales durante la resolución de direcciones mediante getaddrinfo.

    • AI_PASSIVE: Para usar con servidores, indica que la dirección IP del host local debe asignarse a las estructuras de socket.

    • AI_CANONNAME: Para obtener el nombre canónico del host.

    • AI_NUMERICHOST: Para evitar la resolución de nombres de dominio, si ya se tiene una dirección IP numérica.

    • AI_NUMERICSERV: Similar a AI_NUMERICHOST, pero para el número de puerto

  • ai_family: Especifica la familia de direcciones, como AF_INET para IPv4 o AF_INET6 para IPv6.

    • AF_UNSPEC: No especifica IPv4 o IPv6, permitiendo cualquiera.

    • AF_INET: Para IPv4.

    • AF_INET6: Para IPv6.

  • ai_socktype: Especifica el tipo de socket, como SOCK_STREAM para sockets TCP o SOCK_DGRAM para sockets UDP.

    • SOCK_STREAM: Para sockets de flujo (TCP).

    • SOCK_DGRAM: Para sockets de datagramas (UDP).

  • ai_protocol: Especifica el protocolo a utilizar, como IPPROTO_TCP para TCP o IPPROTO_UDP para UDP.

    • IPPROTO_TCP: Para el protocolo TCP.

    • IPPROTO_UDP: Para el protocolo UDP.

  • ai_addr: Un puntero a una estructura sockaddr que contiene la información de dirección.

  • ai_addrlen: La longitud de la estructura sockaddr.

  • ai_canonname: Un nombre canónico asociado con la dirección.

  • ai_next: Un puntero al siguiente elemento en la lista enlazada de addrinfo. Esto es útil cuando getaddrinfo() devuelve múltiples resultados.

Es posible que no necesites escribir en estas estructuras con frecuencia; en muchos casos, una llamada a getaddrinfo() para completar tu struct addrinfo será todo lo que necesites. Sin embargo, antes de realizar la llamada a getaddrinfo() es recomendable vaciar completamente la estructura mediante memset().

struct addrinfo hints;
memset(&hints, 0, sizeof hints);
getaddrinfo(NULL, PORT, &hints, &servinfo)

struct sockaddr

struct sockaddr es una estructura que actúa como un tipo de dato genérico para representar direcciones de red en la programación de sockets en C. Puede adaptarse tanto a direcciones IPv4 como a direcciones IPv6, lo que la hace versátil y esencial en el desarrollo de aplicaciones de red.

La estructura struct sockaddr almacena información de dirección de socket para varios tipos de sockets.

struct sockaddr {
    unsigned short sa_family; // Familia de direcciones, AF_xxx
    char sa_data[14];         // Almacena la dirección (y otros datos en algunos casos)
};

sa_family puede ser una variedad de cosas, pero en este documento será AF_INET (IPv4) o AF_INET6 (IPv6) para todo lo que hacemos. sa_data contiene una dirección de destino y un número de puerto para el socket. Esto puede ser un tanto engorroso ya que no deseas empaquetar manualmente la dirección en sa_data.

struct sockaddr_in (IPv4)

Para lidiar con struct sockaddr, los programadores crearon una estructura paralela: struct sockaddr_in ("in" de "Internet") para usar con IPv4.

Y aquí está la parte importante: un puntero a una struct sockaddr_in se puede convertir en un puntero a una struct sockaddr y viceversa. Entonces, incluso si connect() requiere un struct sockaddr*, todavía puedes usar una struct sockaddr_in y convertirla en el último momento.

// Solo IPv4
struct sockaddr_in {
    short int          sin_family;  // familia de direcciones, AF_INET
    unsigned short int sin_port;    // número de puerto
    struct in_addr     sin_addr;    // dirección de Internet
    unsigned char      sin_zero[8]; // mismo tamaño que struct sockaddr
};

Esta estructura facilita la referencia a los elementos de la dirección del socket. Ten en cuenta que sin_zero (que se incluye para rellenar la estructura al tamaño de una struct sockaddr) debe configurarse con todos los ceros utilizando la función memset(). Además, observa que sin_family corresponde a sa_family en una struct sockaddr y debe configurarse como 'AF_INET'. Finalmente, el sin_port debe estar en Network Byte Order (utilizando htons()).

Observa que el campo sin_addr es una estructura struct in_addr. ¿Qué es eso? Bueno, sin dramatizar demasiado, es uno de los unions más impresionantes de todos los tiempos

// (IPv4 only--see struct in6_addr for IPv6)

// Internet address (a structure for historical reasons)
struct in_addr {
    uint32_t s_addr; // that's a 32-bit int (4 bytes)
};

¡Vaya! Bueno, solía ser un union, pero parece que esos días han quedado atrás. Menos mal. Entonces, si has declarado 'ina' como del tipo struct sockaddr_in, entonces 'ina.sin_addr.s_addr' hace referencia a la dirección IP de 4 bytes (en Network Byte Order). Ten en cuenta que incluso si tu sistema aún utiliza el unión espantoso para struct in_addr, aún puedes hacer referencia a la dirección IP de 4 bytes de exactamente la misma manera que mencioné anteriormente (esto se debe a las definiciones con '#defines').

sockaddr_in6 (IPv6)

sockaddr_in6 es una estructura utilizada en programación de sockets para representar direcciones de red en el contexto de IPv6. A diferencia de la estructura sockaddr_in, que se utiliza para direcciones IPv4, sockaddr_in6 está diseñada específicamente para IPv6.

// (IPv6 only--see struct sockaddr_in and struct in_addr for IPv4)

struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct in6_addr {
    unsigned char   s6_addr[16];   // IPv6 address
};

Observa que IPv6 tiene una dirección IPv6 y un número de puerto, al igual que IPv4 tiene una dirección IPv4 y un número de puerto.

struct sockaddr_storage

struct sockaddr_storage está diseñada para ser lo suficientemente grande como para contener tanto estructuras IPv4 como IPv6. Verás, en algunas llamadas, a veces no sabes de antemano si se completará tu struct sockaddr con una dirección IPv4 o IPv6. Por lo tanto, pasas esta estructura paralela, muy similar a struct sockaddr pero más grande, y luego la conviertes al tipo que necesitas.

struct sockaddr_storage {
    sa_family_t  ss_family;     // address family

    // all this is padding, implementation specific, ignore it:
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

Lo importante es que puedes ver la familia de direcciones en el campo ss_family. Verifícalo para ver si es AF_INET o AF_INET6 (para IPv4 o IPv6). Luego puedes convertirlo en struct sockaddr_in o struct sockaddr_in6 si así lo deseas.

Last updated