El problema con la programación conjunta de IPV6 e IPV4 es que una estructura de sockaddr pura no es lo suficientemente grande como para contener un sockaddr_in6. Entonces, si necesita pasar ciegamente una dirección que podría ser sockaddr_in o sockaddr_in6, sockaddr_storage es un poco más fácil de usar.
Al final del día, ya sea que use sockaddr_in, sockaddr_in6 o sockaddr_storage, tendrá que convertir esos punteros para realizar una llamada a sendto, recvfrom, conectar, aceptar y muchas otras funciones de socket. Es solo un matiz conocido de la programación del zócalo. Solo suelta la sensación de hacer algo inseguro. Tu código estará bien.
Ahora, al escribir código de red que debe funcionar tanto para IPV4 como para IPV6, puede fácilmente caer en la trampa de tener una gran cantidad de instrucciones de conmutación para manejar los diferentes tipos de red. Código, a continuación se complica como el siguiente:
if (addr.ss_family == AF_INET)
sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in))
else (addr.ss_family == AF_INET6)
sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in6));
Y entonces ese tipo de "si la familia == AF_INET" expresión puede comenzar fácilmente a repetirse una y otra vez. Eso es lo que quieres evitar.
Suponiendo que está utilizando C++, encontrará que una clase de abstracción para un objeto de dirección de socket es increíblemente útil. Tengo un ejemplo en github here y here. La clase CSocketAddress está respaldada por una unión de {sockaddr, sockaddr_in, sockaddr_in6} y se puede construir con sockaddr_storage. Si hubiera sabido sobre sockaddr_storage antes de comenzar esta clase, lo hubiera usado en lugar de lo de la unión. En cualquier caso, me permite escribir código de la siguiente forma:
CSocketAddress addr;
...
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength());
Del mismo modo, un "acepta" la declaración es el siguiente:
sockaddr_storage addrstorage = {};
int len = sizeof(sockaddr_storage);
accept(sock, (sockaddr*)&addrstorage, &len);
CSocketAdddress addr(addrstorage); // construct an address object to pass around everywhere else
Esto era muy útil para las rutas de código que llaman se unen, enviar y recv. Ahora mis rutas de código de servidor y cliente STUN ya no tienen que saber nada sobre el tipo de familia de la dirección del socket.Simplemente funcionan con objetos "CSocketAddress". El único código específico de IPV4 e IPV6 es durante la inicialización del cliente y del servidor, cuando los objetos de dirección están realmente construidos. Afortunadamente, eso fue parcialmente abstraído también.
También es posible que desee leer las funciones de ayuda here. Hay algunas cosas más útiles para resolver nombres de host, enumerar adaptadores, etc. de una manera independiente de IP. Es un código de Linux, pero parte de él debe correlacionarse con Windows y winsock.
Casi termino de agregar soporte TCP a esta base de código. En el proceso de agregar soporte para SOCK_STREAM, no he tenido que hacer un solo cambio ni agregar ningún código nuevo para tratar las diferencias en las estructuras de direcciones IPV4 e IPV6.
Tenga en cuenta que lo que gethostbyname() devuelve no es compatible con sockaddr_in - devuelve los punteros a las direcciones de host de cuatro bytes, no a las instancias de sockaddr completas. Por lo tanto, copie el campo h_addr_list [0] en el campo sin_addr de un sockaddr_in para IPV6 y en el campo sin6_addr de un sockaddr_ipv6. –
@JonWatte: verdadero, y esa es solo una razón más para no usar 'gethostbyname()'. Use 'getaddrinfo()' en su lugar. – Celada
Sí, en bases de código donde tengo esa libertad, eso es mejor. –