Estoy viendo funciones como connect()
y bind()
en sockets C y observe que llevan un puntero a un sockaddr
estructura He estado leyendo y para hacer su aplicación AF-Independiente, es útil usar el sockaddr_storage
puntero de estructura y convertirlo en un sockaddr
puntero debido a todo el espacio adicional que tiene para direcciones más grandes.
Lo que me pregunto es cómo funciona como connect()
y bind()
que pide un sockaddr
puntero accede a los datos desde un puntero que apunta a una estructura más grande que la que espera. Claro, le pasa el tamaño de la estructura que le está proporcionando, pero ¿cuál es la sintaxis real que usan las funciones para obtener la dirección IP de los punteros a estructuras más grandes que ha enviado a struct *sockaddr
?
Probablemente sea porque vengo de los lenguajes OOP, pero parece un truco y un poco desordenado.
el prole
Funciones que esperan un puntero para struct sockaddr
probablemente encasille el puntero al que los envía sockaddr
cuando les envías un puntero a struct sockaddr_storage
. De esa forma, acceden a él como si fuera un struct sockaddr
.
struct sockaddr_storage
está diseñado para encajar tanto en un struct sockaddr_in
y struct sockaddr_in6
Tu no creas el tuyo struct sockaddr
normalmente creas un struct sockaddr_in
o un struct sockaddr_in6
dependiendo de la versión de IP que estés usando. Para evitar tratar de saber qué versión de IP usará, puede usar un struct sockaddr_storage
que puede contener cualquiera. Esto a su vez se encasillará en struct sockaddr
por las funciones connect(), bind(), etc. y se accede de esa manera.
Puede ver todas estas estructuras a continuación (el relleno es específico de la implementación, con fines de alineación):
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
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 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];
};
Entonces, como puede ver, si la función espera una dirección IPv4, solo leerá los primeros 4 bytes (porque asume que la estructura es del tipo struct sockaddr
. De lo contrario, leerá los 16 bytes completos para IPv6).
-
Así que supongamos que tengo un puntero a un
struct sockaddr_storage
llamósas
y todos los campos de la estructura han sido debidamente rellenados con la dirección y la familia de direcciones. Ahora realizo esto:struct sockaddr *s = (struct sockaddr*)sas
. ¿Cómo obtendría la dirección des
¿ahora?–Matt Vaughan
15 de abril de 2013 a las 8:36
-
no usarías un
struct sockaddr
directamente. Lo que harías es encasillarlo de nuevo asockaddr_storage
osockaddr_in
y luego leerlo. Como puedes ver en mi publicación.sockaddr
tiene suficiente espacio para una dirección IPv4 o v6. Habiendo dicho eso, no sé por qué querrías encasillar astruct sockaddr
para cualquier otra cosa que no sean los parámetros cuyas funciones lo requieren.struct sockaddr
no está diseñado para ‘uso del programador’.– el prole
15 de abril de 2013 a las 8:43
-
Estoy leyendo un libro que dice que sockaddr no es lo suficientemente grande para contener sockaddr_in6.
–Matt Vaughan
15 de abril de 2013 a las 9:08
-
Bueno, el tamaño de sockaddr es de 16, mientras que el tamaño de sockaddr_storage es de 128 bytes. Dado que la dirección IPv6 es de 16 bytes, es difícil ver cómo sockaddr acomodaría la dirección IPv6.
– flautista de Hamelín
8 dic 2013 a las 20:38
-
Si alguien está confundido por sa_len / sin_len: consulte stackoverflow.com/a/23627149/2013911 (en resumen: estos atributos son específicos de la implementación => no portátiles)
– Niklas Peter
30 de enero de 2016 a las 11:24
alexis wilke
En C++, las clases con al menos una función virtual reciben una ETIQUETA. Esa etiqueta te permite dynamic_cast<>()
a cualquiera de las clases de las que deriva su clase y viceversa. El TAG es lo que permite dynamic_cast<>()
trabajar. Más o menos, esto puede ser un número o una cadena…
En C estamos limitados a las estructuras. Sin embargo, a las estructuras también se les puede asignar una ETIQUETA. De hecho, si miras todas las estructuras que el prole publicado en su respuesta, notará que todos comienzan con 2 bytes (un corto sin firmar) que representa lo que llamamos la familia de la dirección. Esto define exactamente qué es la estructura y, por lo tanto, su tamaño, campos, etc.
Por lo tanto, puedes hacer algo como esto:
int bind(int fd, struct sockaddr *in, socklen_t len)
{
switch(in->sa_family)
{
case AF_INET:
if(len < sizeof(struct sockaddr_in))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in *p = (struct sockaddr_in *) in;
...
}
break;
case AF_INET6:
if(len < sizeof(struct sockaddr_in6))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in6 *p = (struct sockaddr_in6 *) in;
...
}
break;
[...other cases...]
default:
errno = EINVAL; // family not supported
return -1;
}
}
Como puede ver, la función puede comprobar la len
parámetro para asegurarse de que la longitud es suficiente para adaptarse a la estructura esperada y, por lo tanto, pueden reinterpret_cast<>()
(como se llamaría en C++) su puntero. Si los datos son correctos en la estructura depende de la persona que llama. No hay muchas opciones en ese extremo. Se espera que estas funciones verifiquen todo tipo de cosas antes de usar los datos y devolver -1 y errno
siempre que se encuentre un problema.
Entonces, en efecto, usted tiene un struct sockaddr_in
o struct sockaddr_in6
que tú (reinterpretas) echas a un struct sockaddr
y el bind()
(y otras) lanzan ese puntero de vuelta a un struct sockaddr_in
o struct sockaddr_in6
después de que comprobaron el sa_family
miembro y verificó el tamaño.
-
Aunque esta no es una pregunta de c ++, tal vez podría aclarar que no todas las clases de c ++ tienen una ETIQUETA, sino solo las que tienen al menos una función virtual.
– Mike MB
23 de junio de 2016 a las 22:51
-
@MikeMB El OP escribió: “Probablemente sea porque vengo de lenguajes orientados a objetos”lo que estoy pensando significa que quiere entender cómo funciona C en comparación con lo que aprendió antes.
– Alexis Wilke
24 de junio de 2016 a las 18:23
-
Lo siento, probablemente debería haberme expresado más claramente: no he tenido ningún problema con que hagas referencia a c++ en absoluto. Solo quería asegurarme de que…a pesar de que no es una pregunta dirigida a los programadores de c++ – las partes que hacer referir a c++ son lo más precisos posible (en caso de que un principiante de c++ tropiece con él, como lo hice yo),
– Mike MB
24/06/2016 a las 22:44