Next Previous Contents

6. Programación Multicast.

La programación Multicast... o cómo escribir sus propias aplicaciones multicast.

Se necesitan diversas extensiones al API de programación para poder soportar multicast. Todas ellas se manejan a través de dos llamadas al sistema: setsockopt() (usada para pasar información al kernel) y getsockopt() (para obtener información referente al comportamiento multicast). Esto no significa que se añadan dos nuevas llamadas al sistema para soportar multicast. El par setsockopt() / getsockopt() ha estado allí durante años. O por lo menos desde 4.2 BSD. Lo que se añaden son un nuevo conjunto de opciones (opciones de multicast) que se entregan a estas llamadas al sistema, y que el kernel debe entender.

Estos son los prototipos de las funciones setsockopt()getsockopt() :

 
int getsockopt(int s, int level, int optname, void*
optval, int* optlen);

int setsockopt(int s, int level, int optname, const void* optval, int
optlen); 

El primer parámetro, s, es el socket al que se aplica la llamada al sistema. Para multicast, debe ser un socket de la familia AF_INET y puede ser del tipo SOCK_DGRAM o SOCK_RAW. Se utiliza comúnmente el socket de tipo SOCK_DGRAM, pero si quiere implementar un demonio de encaminamiento, o modificar uno existente, posiblemente tenga que usar los sockets SOCK_RAW.

El segundo, nivel [level, n. del t.], identifica la capa que debe manejar la opción, mensaje, pregunta, o como quiera llamarlo. Así, SOL_SOCKET es para la capa del socket, IPPROTO_IP para la capa IP, etc... Para programación multicast, level será siempre IPPROTO_IP.

La variable optname identifica la opción que se quiere conocer/modificar. Su valor (el que entrega el programa o devuelve el kernel) es optval. Los nombres posibles para ésta variable, y que aparecen en programación multicast son los siguientes:

setsockopt() getsockopt() 
IP_MULTICAST_LOOP sí sí 
IP_MULTICAST_TTL sí sí 
IP_MULTICAST_IF sí sí 
IP_ADD_MEMBERSHIP sí no 
IP_DROP_MEMBERSHIP sí no

La siguiente, optlen, indica el tamaño de la estructura de datos a la que apunta optval. Observe que en getsockopt() éste es un resultado devuelto más que un parámetro: el kernel escribe el valor de optname en el buffer apuntado por optval, y nos indica su tamaño a través de optlen.

Tanto setsockopt() como getsockopt() devuelven 0 en caso de éxito o -1 si ha habido un error.

6.1 IP_MULTICAST_LOOP.

Tendrá que decidir, como escritor de aplicaciones, si quiere que los datos que envía sean enviados a su máquina a través del «loopback» o no. Si quiere tener más de un proceso de usuario «escuchando», se debe activar el loopback. Por otro lado, si está enviando las imágenes que produce su cámara de vídeo, posiblemente no quiera loopback, aunque quiera verse a sí mismo en la pantalla. En este caso, su aplicación probablemente recibe las imágenes de un dispositivo conectado a su ordenador y las envíe a un socket. Ya que la aplicación ya «tiene» estos datos, es improbable que quiera recibirlos de nuevo en el socket. El loopback está activado por defecto.

Observe que optval es un puntero. No puede escribir:

 
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, 0,1); 
para desactivar el loopback tienes que hacer:

 
u_char loop; setsockopt(socket, IPPROTO_IP,
IP_MULTICAST_LOOP, &loop, sizeof(loop)); 

y poner loop a 1 para activar el loopback o a 0 para desactivarlo.

Para saber si en un socket está activada o desactivada la opción de reenvío hacia atrás [looping back, n. del t.] use algo así:

 
u_char loop; int size;

getsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
&size) 

6.2 IP_MULTICAST_TTL.

Si no se le especifica de otra forma, los datagramas multicast se envían con un valor por defecto de 1, para impedir que sean reenviados más allá de la red local. Para cambiar el TTL al valor deseado (de 0 a 255), debe poner ese valor en una variable (aquí la llamo «ttl») y escribirla en algún lugar de su programa:

 u_char ttl; setsockopt(socket, IPPROTO_IP,
IP_MULTICAST_TTL, &ttl, sizeof(ttl)); 

El comportamiento con getsockopt() es similar al visto en IP_MULTICAST_LOOP.

6.3 IP_MULTICAST_IF.

Generalmente, el administrador del sistema especifica el interfaz por el que se envían, por defecto, los datagramas multicast. El programador puede modificar esto y elegir un interfaz concreto para un socket determinado con esta opción.

 struct in_addr interface_addr; setsockopt (socket,
IPPROTO_IP, IP_MULTICAST_IF, &interface_addr,
sizeof(interface_addr)); 

De ahora en adelante, todo el tráfico multicast generado en este socket, será enviado a través del interfaz generado. Para volver al comportamiento original y permitir al kernel elegir el interfaz de salida basado en la configuración del administrador del sistema, es suficiente llamar a setsockopt() con esta misma opción e INADDR_ANY en el campo de interfaz.

Para elegir el interfaz de salida, las siguientes ioctls pueden ser útiles: SIOCGIFADDR (para obtener la dirección de un interfaz), SIOCGIFCONF (para obtener una lista con todos los interfaces) y SIOCGIFFLAGS (para obtener las flags de un interfaz, y, por tanto determinar si el interfaz tiene capacidades multicast -el flag IFF_MULTICAST-).

Si el ordenador tiene más de un interfaz y la opción IP_MULTICAST_IF no está fijada, las transmisiones multicast se envían a través del interfaz por defecto, aunque los interfaces restantes pueden usarse para reenvío multicast si el ordenador está actuando como un encaminador multicast.

6.4 IP_ADD_MEMBERSHIP.

Recordemos que se necesita decir al kernel en qué grupos multicast está uno interesado. Si ningún proceso está interesado en un grupo, los paquetes destinados a éste que llegan al ordenador son descartados. Para informar al kernel de sus intereses y, por tanto, convertirse en miembro de ese grupo, debe primero llenar una estructura ip_mreq que se pasa más tarde al kernel en el campo optval de la llamada al sistema setsockopt().

La estructura ip_mreq (obtenido de /usr/include/linux/in.h) tiene los siguientes miembros:

 
struct ip_mreq {
        struct in_addr imr_multiaddr; /* Direccion multicast IP del
grupo */
        struct in_addr imr_interface; /* Direccion IP local del
interfaz */ }; 

(Observación: la definición «física» de la estructura está en el fichero arriba indicado. Sin embargo, no debería incluir <linux/in.h> si quiere que su código sea portable. En su lugar incluya <netinet/in.h> que incluye a su vez a <linux/in.h>).

El primer miembro, imr_multiaddr, mantiene la dirección del grupo al que deseas unirse. Recuerde que las asociaciones también están relacionadas con el interfaz, no sólo con los grupos. Esta es la razón por la que tienes que dar un valor al segundo miembro: imr_interface. De esta forma, si está en un ordenador «multihomed», puede unirse al mismo grupo en distintos interfaces. Siempre puede llenar el último miembro con la dirección comodín (INADRR_ANY) y entonces el kernel se encargará de elegir el interfaz apropiado.

Con esta estructura rellena (supongamos que la ha definido como struct ip_mreq mreq;) sólo tienes que llamar a setsockopt() de la siguiente manera:

 
setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)); 

Observe que puede unirse a distintos grupos en el mismo socket, no sólo a uno. El límite a esto es IP_MAX_MEMBERSHIPS y, en la versión 2.0.33, tiene el valor de 20.

6.5 IP_DROP_MEMBERSHIP.

El proceso es muy similar al de unirse a un grupo:

struct ip_mreq mreq; setsockopt (socket, IPPROTO_IP,
IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 

Donde mreq es la misma estructura con los mismos datos usados para unirse al grupo. Si el imr_interface está rellenado con INADRR_ANY, el primer grupo que encaje es abandonado.

Si se ha unido a muchos grupos en el mismo socket, no necesita abandonar todos los grupos para terminar. Cuando cierra un socket, todas las asociaciones de éste son abandonadas por el kernel. Lo mismo ocurre si el proceso que abrió el socket muere.

Finalmente, recuerde que porque un proceso abandone un grupo eso no significa que el ordenador deje de recibir datagramas para ese grupo. Si otro socket se unió al grupo en el mismo interfaz previamente a este IP_DROP_MEMBERSHIP, entonces el ordenador seguirá siendo un miembro del grupo.

Tanto ADD_MEMBERSHIP como DROP_MEMBERSHIP son operaciones no bloqueantes. Deberían retornar inmediatamente indicando éxito o fallo.


Next Previous Contents