sockets - socket - winsock windows 7
API usando sockaddr_storage (2)
Estoy tratando de hacer un poco de codificación agnóstico de IP y como lo sugirieron varias fuentes intenté usar sockaddr_storage. Sin embargo, todas las llamadas API (getaddrinfo, getnameinfo) aún dependen de struct sockaddr. Y elegir entre ellos no es exactamente una buena opción, los gves se enfrentan a muchos otros problemas.
Y fundir en sockaddr_in y sockaddr_in6 separadamente derrota el propósito de mí al intentar usar sockaddr_storage.
Cualquiera que haya utilizado efectivamente sockaddr_storage en devloping una aplicación de socket de servidor cliente simple.
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 esté usando 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 el código de red que debe funcionar tanto para IPV4 como para IPV6, puede llegar fácilmente a la trampa de tener una gran cantidad de instrucciones de conmutación para manejar los diferentes tipos de red. El código se vuelve desordenado 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 luego ese tipo de expresión "si familia == AF_INET" puede comenzar fácilmente a repetirse una y otra vez. Eso es lo que quieres evitar.
Suponiendo que está usando 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 aquí y aquí . 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 el código de la siguiente manera:
CSocketAddress addr;
...
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength());
Del mismo modo, una declaración de "aceptación" se ve así:
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 fue increíblemente útil para las rutas de código que llaman bind, send 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 examinar las funciones de ayuda aquí . 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.
Ya 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.
Generalmente no veo la necesidad de struct sockaddr_storage
. Su propósito es asignar suficiente espacio para la estructura sockaddr de cualquier protocolo dado, pero ¿con qué frecuencia necesita hacerlo en un código independiente de la versión de IP? Generalmente llamas a getaddrinfo()
y te da un montón de struct sockaddr *
s, y no te importa si son sockaddr_in
o sockaddr_in6
, simplemente los pasas como están a bind()
y connect()
(no fundición requerida).
En el típico código cliente / servidor, el lugar principal en el que puedo pensar que struct sockaddr_storage
es útil es reservar espacio para el segundo parámetro para accept()
. En este caso, estoy de acuerdo en que es feo tener que convertirlo a struct sockaddr *
una vez para accept()
y nuevamente para getnameinfo()
. Pero no puedo ver una forma de sortear esos moldes. Esto es C. La herencia de la estructura siempre involucra muchos moldes.