2010-09-07 31 views
16

Me pregunto si hay una manera fácil de iterar a través de un fd_set? La razón por la que quiero hacer esto es no tener que pasar por todos los sockets conectados, ya que select() altera estos fd_sets para incluir solo aquellos que me interesan. También sé que el uso de una implementación de un tipo que no se pretende acceder directamente es generalmente una mala idea, ya que puede variar en diferentes sistemas. Sin embargo, necesito alguna forma de hacer esto, y me estoy quedando sin ideas. Entonces, mi pregunta es:Cómo iterar a través de un fd_set

¿Cómo puedo iterar a través de un fd_set? Si esta es una práctica realmente mala, ¿hay alguna otra manera de resolver mi "problema", salvo que se ejecute en todos los zócalos conectados?

Gracias

+0

Para enfatizar lo que quiero decir. No quiero utilizar el enfoque FD_ISSET ya que me obliga a recorrer todos los sockets conectados. Pero dado que, por definición, select() elimina los descriptores de archivos no relevantes del conjunto, quiero recorrer el conjunto. – Andreas

+6

No necesariamente significa "todos conectados". Puede pasar un subconjunto de sus sockets conectados para seleccionar y luego usar FD_ISSET solo en ese subconjunto luego de seleccionar devoluciones. Además, ¿hay un problema real con el bucle sobre todos ellos? A menos que tenga que lidiar con muchos miles de enchufes conectados, es probable que el ciclo tarde un tiempo intrascendente. – Rakis

+1

De acuerdo con Rakis. Esta es una de esas cosas que parece ser ineficiente, pero en la mayoría de los casos realmente no lo es. El tiempo para pasar por el ciclo se verá empequeñecido por el tiempo que tarda en atender solo uno de los FD establecidos. – Duck

Respuesta

5

Seleccionar establece el un bit correspondiente al descriptor de archivo en el conjunto, por lo tanto, no necesita iterar a través de todos los fds si está interesado en unos pocos (y puede ignorar otros) simplemente pruebe solo los descriptores de archivo que le interesan.

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { 
    perror("select"); 
    exit(4); 
} 

if(FD_ISSET(fd0, &read_fds)) 
{ 
    //do things 
} 

if(FD_ISSET(fd1, &read_fds)) 
{ 
    //do more things 
} 

EDITAR
Aquí está la estructura fd_set:

typedef struct fd_set { 
     u_int fd_count;    /* how many are SET? */ 
     SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */ 
} fd_set; 

Cuando, fd_count es el número de sockets establecidos (por lo que, se puede añadir una optimización de utilizar este) y fd_array es una bit-vector (del tamaño FD_SETSIZE * sizeof (int) que depende de la máquina). En mi máquina, es 64 * 64 = 4096.

Entonces, su pregunta es esencialmente: ¿cuál es la forma más eficiente de encontrar las posiciones de bit de 1s en un vector de bits (de tamaño alrededor de 4096 bits)?

Quiero aclarar una cosa aquí:
"bucle a través de todos los enchufes conectados" no significa que realmente está leyendo/haciendo cosas para una conexión. FD_ISSET() solo verifica que el tiempo en el fd_set ubicado en el número asignado de file_descriptor asignado a la conexión esté configurado o no. Si su objetivo es la eficiencia, ¿no es esto lo más eficiente? usando heurística?

Cuéntanos qué le pasa a este método y qué intentas lograr con el método alternativo.

+0

Gracias a ti también. Pero, por favor, vea mi comentario, tal vez no estaba explicando explícitamente que este es el enfoque que no deseo tomar. – Andreas

+3

Si esta no es la respuesta que [es correcta/desea], ¿por qué se ha marcado como la respuesta? –

+0

por dos razones. a) la edición proporcionó la información que estaba buscando b) Cambié de opinión y, por lo tanto, la respuesta se volvió relevante. – Andreas

1

Consulte esta sección 7.2 de Beej 's guía para la creación de redes -' 7.2. select() - Synchronous I/O Multiplexing 'usando FD_ISSET.

en definitiva, debe recorrer un fd_set con el fin de determinar si el descriptor de archivo está listo para la lectura/escritura ...

+0

Gracias por la respuesta. Sé que este es el enfoque estándar, sin embargo, estoy buscando influir en él, por favor vea mi comentario en mi propia publicación. – Andreas

4

Es bastante sencillo:

for(int fd = 0; fd < max_fd; fd++) 
    if (FD_ISSET(fd, &my_fd_set)) 
     do_socket_operation(fd); 
+0

Gracias por la respuesta. Por favor, mira mi comentario para aclarar lo que quiero hacer. – Andreas

0

No creo que lo que estás tratando de hacer sea una buena idea.

Primero depende de su sistema, pero creo que ya lo sabe.

En segundo lugar, en el nivel interno, estos conjuntos se almacenan como una matriz de enteros y fds se almacenan como bits establecidos. Ahora, de acuerdo con las páginas man de select, FD_SETSIZE es 1024. Incluso si quisiera iterar y obtener sus archivos fd interesados, debe pasar ese número junto con el desorden de la manipulación de bits. Así que, a menos que espere más de FD_SETSIZE fd en seleccionar lo que no creo que sea posible, no es una buena idea.

¡Oh, espera! En cualquier caso, no es una buena idea.

10

Tienes que completar una estructura fd_set antes de llamar a select(), no puedes pasar tu std :: conjunto original de sockets directamente. select() luego modifica el fd_set en consecuencia, eliminando los sockets que no están "configurados" y devuelve cuántos sockets quedan. Tienes que recorrer el fd_set resultante, no tu std :: set. No hay necesidad de llamar FD_ISSET() porque el fd_set resultante sólo contiene "set" enchufes están listos, por ejemplo:

fd_set read_fds; 
FD_ZERO(&read_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
     do_socket_operation(read_fds.fd_array[i]); 
} 

Dónde FD_ISSET() entra en juego más a menudo es cuando se utiliza la comprobación de errores con select() , por ejemplo:

fd_set read_fds; 
FD_ZERO(&read_fds); 

fd_set error_fds; 
FD_ZERO(&error_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

error_fds.fd_count = read_fds.fd_count; 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    error_fds.fd_array[i] = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
    { 
     if(!FD_ISSET(read_fds.fd_array[i], &error_fds)) 
      do_socket_operation(read_fds.fd_array[i]); 
    } 

    for(int i = 0; i < error_fds.fd_count; ++i) 
    { 
     do_socket_error(error_fds.fd_array[i]); 
    } 
} 
+0

+1, aunque, encontré algunos 'aray' s en tu código :) – Default

+0

He reparado los errores tipográficos –

+0

El [seleccionar página de manual] (http://linux.die.net/man/2/select) dice:' nfds es el descriptor de archivo con el número más alto en cualquiera de los tres conjuntos, más 1. '¡Utilice el * número más alto *, no el * recuento *! – MaPePeR

3

Este bucle es una limitación de la interfaz de select(). Las implementaciones subyacentes de fd_set generalmente están un poco configuradas, lo que obviamente significa que buscar un socket requiere escanear sobre los bits.

Es por esta razón que se han creado varias interfaces alternativas; lamentablemente, todas son específicas del sistema operativo. Por ejemplo, Linux proporciona epoll, que devuelve una lista de solo los descriptores de archivos que están activos. FreeBSD y Mac OS X ambos proporcionan kqueue, que logra el mismo resultado.

+0

Esta debería ser la respuesta aceptada. – Agis

0

No creo que pueda hacer mucho utilizando la llamada select() de manera eficiente. La información en "The C10K problem" sigue siendo válida.

Se necesita algo de soluciones específicas de la plataforma:

O usted podría utilizar una biblioteca de evento para ocultar el detalle plataforma para que libev

Cuestiones relacionadas