¿Por qué lanzamos sockaddr_in a sockaddr cuando llamamos a bind()?

4 minutos de lectura

los unir() La función acepta un puntero a un sockaddrpero en todos los ejemplos que he visto, un sockaddr_in en su lugar se utiliza la estructura, y se moldea para sockaddr:

struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...

No puedo entender por qué es un sockaddr_in estructura utilizada. ¿Por qué no simplemente preparar y aprobar un sockaddr?

¿Es solo convención?

  • NÓTESE BIEN. sockaddr_in6 también existe, y cualquier persona que escriba código nuevo debería incluirlo…

    – BRPocock

    13/01/2014 a las 19:00

  • Ha omitido una parte muy importante en su código: name.sa_family = AF_INET por struct sockaddr_in! Considerar struct sockaddr ser una unión de todos los demás tipos de sockaddr. Lo único en común es que tienen un primer miembro sa_family_t sa_family que debe corresponder al tipo de estructura real.

    –Ulrich Eckhardt

    25 de enero de 2015 a las 10:47

No, no es solo una convención.

sockaddr es un descriptor genérico para cualquier tipo de operación de socket, mientras que sockaddr_in es una estructura específica para la comunicación basada en IP (IIRC, “in” significa “InterNet”). Hasta donde yo sé, se trata de una especie de “polimorfismo”: el bind() función pretende tomar un struct sockaddr *, pero de hecho, asumirá que se pasa el tipo apropiado de estructura; es decir, uno que corresponda al tipo de socket que le das como primer argumento.

  • Solo para agregar: sockaddr_in6 es para direcciones IPv6, sockaddr_un para sockets de dominio Unix, …

    – Martín R.

    13/01/2014 a las 19:00

  • @MartinR También estaba pensando en Bluetooth (si no me equivoco, Linux hace RFCOMM sobre sockets), etc.

    usuario529758

    13 de enero de 2014 a las 19:01

  • Hay muchos tipos de enchufes que no conozco… Podría valer la pena mencionarlos struct sockaddr_storageque también es “genérico” en cierto sentido y es lo suficientemente grande como para contener cualquier tipo de dirección de socket.

    – Martín R.

    13 de enero de 2014 a las 19:06

avatar de usuario
Mihir Lutra

No sé si es muy relevante para esta pregunta, pero me gustaría proporcionar información adicional que pueda hacer que el encasillado sea más comprensible para muchas personas que no han pasado mucho tiempo con C confundirse al ver tal encasillamiento.

yo suelo macOSpor lo que estoy tomando ejemplos basados ​​en archivos de encabezado de mi sistema.

struct sockaddr se define de la siguiente manera:

struct sockaddr {
    __uint8_t       sa_len;         /* total length */
    sa_family_t     sa_family;      /* [XSI] address family */
    char            sa_data[14];    /* [XSI] addr value (actually larger) */
};

struct sockaddr_in se define de la siguiente manera:

struct sockaddr_in {
    __uint8_t       sin_len;
    sa_family_t     sin_family;
    in_port_t       sin_port;
    struct  in_addr sin_addr;
    char            sin_zero[8];
};

Comenzando desde lo básico, un puntero solo contiene una dirección. Entonces struct sockaddr * y struct sockaddr_in * son bastante iguales. Ambos solo almacenan una dirección. La única diferencia relevante es cómo el compilador trata sus objetos.

Así que cuando dices (struct sockaddr *) &namesolo está engañando al compilador y diciéndole que esta dirección apunta a un struct sockaddr escribe.


Así que digamos que el puntero está apuntando a una ubicación 1000. Si el struct sockaddr * almacena esta dirección, considerará la memoria de 1000 para sizeof(struct sockaddr) que poseen los miembros según la definición de la estructura. Si struct sockaddr_in * almacena la misma dirección que considerará la memoria de 1000 para sizeof(struct sockaddr_in).


Cuando encasilló ese puntero, considerará la misma secuencia de bytes hasta sizeof(struct sockaddr).

struct sockaddr *a = &name; // consider &name = 1000

Ahora si accedo a->sa_lenel compilador accedería desde la ubicación 1000 para sizeof(__uint8_t) que tiene el mismo tamaño de bytes que en el caso de sockaddr_in. Entonces esto debería acceder a la misma secuencia de bytes.

El mismo patrón es para sa_family.

Después de eso, hay una matriz de caracteres de 14 bytes en struct sockaddr que almacena datos de in_port_t sin_port (typedef‘d entero sin signo de 16 bits = 2 bytes), struct in_addr sin_addr (simplemente una dirección ipv4 de 32 bits = 4 bytes) y char sin_zero[8](8 bytes). Estos 3 se suman para hacer 14 bytes.

Ahora estos tres están almacenados en esta matriz de caracteres de 14 bytes y podemos acceder a cualquiera de estos tres accediendo a los índices apropiados y encasillándolos nuevamente.

La respuesta de user529758 ya explica la razón para hacer esto.

avatar de usuario
Magnus Reftel

Esto se debe a que bind puede vincular otros tipos de sockets además de los sockets IP, por ejemplo, sockets de dominio Unix, que tienen sockaddr_un como su tipo. La dirección de un socket AF_INET tiene el host y el puerto como su dirección, mientras que un socket AF_UNIX tiene una ruta del sistema de archivos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad