c++ - servidor - ¿Cuál es la diferencia entre sockaddr, sockaddr_in y sockaddr_in6?
socket stream c (2)
Sé que sockaddr_in es para IPv4 y sockaddr_in6 para IPv6. La confusión para mí es la diferencia entre sockaddr y sockaddr_in [6].
Algunas funciones aceptan sockaddr
y algunas funciones aceptan sockaddr_in
o sockaddr_in6
, así que:
- ¿cual es la regla?
- ¿Y por qué hay una necesidad de dos estructuras diferentes?
Y porque el sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in)
.
- ¿Eso significa que siempre debemos usar sockaddr_in6 para asignar memoria en la pila y convertirla en sockaddr y sockaddr_in si necesitamos soportar ipv4 e ipv6?
Un ejemplo es: tenemos un socket y queremos obtener la dirección IP de la cadena (puede ser ipv4 o ipv6).
Primero llamamos a getsockname
para obtener un addr
y luego llamamos a inet_ntop
función de addr.sa_family
.
¿Hay algún problema con este fragmento de código?
sockaddr_in6 addr_inv6;
sockaddr* addr = (sockaddr*)&addr_inv6;
sockaddr_in* addr_in = (sockaddr_in*)&addr_inv6;
socklen_t len = sizeof(addr_inv6);
getsockname(_socket, addr, &len);
char ipStr[256];
if (addr->sa_family == AF_INET6)
{
inet_ntop(addr_inv6.sin6_family, &addr_inv6.sin6_addr, ipStr, sizeof(ipStr));
// <<<<<<<<IS THIS LINE VALID, getsockname expected a sockaddr, but we use it output parameter as sockaddr_in6.
}
else
{
inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipStr, sizeof(ipStr));
}
No quiero responder mi pregunta Pero para dar más información aquí que podría ser útil para otras personas, decido responder mi pregunta.
Después de profundizar en el código fuente de linux
. Lo que sigue es mi descubrimiento, hay un posible protocolo múltiple que implementa el nombre getsockname
. Y cada uno tiene una estructura de datos de dirección subyacente, por ejemplo, para IPv4 es sockaddr_in
, e IPV6 sockaddr_in6
, y AF_UNIX
para el socket AF_UNIX
. sockaddr
se utilizan como el puntero de datos común en la firma de esas API.
Esas API copiarán socketaddr_in o sockaddr_in6 o sockaddr_un a sockaddr base en otra length
parámetro mediante memcpy.
Y toda la estructura de datos comienza con el mismo tipo de campo sa_family.
En base a esos motivos, el fragmento de código es válido, porque sa_family
y sa_family
tienen sa_family
y luego podemos convertirlo a la estructura de datos correcta para su uso después de comprobar sa_family
.
BTY, no estoy seguro de por qué el tamaño de sizeof(sockaddr_in6) > sizeof(sockaddr)
, que causa asignar memoria en el tamaño de sockaddr no es suficiente para ipv6 (que es propenso a errores), pero supongo que es por razones de historial .
sockaddr_in
y sockaddr_in6
son estructuras donde el primer miembro es una estructura de sockaddr
.
De acuerdo con el estándar C, la dirección de una estructura y su primer miembro son iguales, por lo que puede convertir el puntero a sockaddr_in(6)
en un puntero a sockaddr
.
Las funciones que toman sockaddr_in(6)
como parámetro pueden modificar la parte sockaddr
, y las funciones que toman sockaddr
como parámetro solo se preocupan por esa parte.
Es un poco como herencia.