2012-01-12 21 views
19

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.API usando sockaddr_storage

Y fundir en sockaddr_in y sockaddr_in6 por separado, algo así como el intento de usar sockaddr_storage.

Cualquiera que haya utilizado efectivamente sockaddr_storage en devloping una aplicación de socket de servidor cliente simple.

Respuesta

23

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.

3

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 llama al getaddrinfo() y le da un montón de struct sockaddr * s, y no importa si son sockaddr_in o , simplemente páselos como están a bind() y connect() (no se requiere conversión).

En el código típico de cliente/servidor, el lugar principal en el que puedo pensar donde struct sockaddr_storage es útil es reservar espacio para el segundo parámetro en accept(). En este caso, acepto que es feo tener que enviarlo al 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.

+0

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. –

+0

@JonWatte: verdadero, y esa es solo una razón más para no usar 'gethostbyname()'. Use 'getaddrinfo()' en su lugar. – Celada

+0

Sí, en bases de código donde tengo esa libertad, eso es mejor. –

Cuestiones relacionadas