2012-02-06 14 views
6

Soy nuevo en la programación de socket y tengo problemas para entender cómo funcionan select() y FD_SET().Cómo usar select y FD_SET en la programación de socket?

Modifico un ejemplo del tutorial de Beej en un intento de resolverlo. Lo que quiero hacer en el ciclo for es que en cada iteración espero 4 segundos. Si hay una lectura disponible, imprimirá "Se presionó una tecla" y, si se agota el tiempo de espera, se imprimirá "Tiempo de espera agotado". Luego borre el conjunto y repita el proceso 9 veces más. Pero parece que una vez que se establece el descriptor de archivo 0, nunca se desarma, incluso después de una llamada a FD_ZERO() y/o FD_CLR(). En otras palabras, después de presionar una tecla en la primera iteración del ciclo, el descriptor de archivo se establece para el resto de las iteraciones y ya no se hace más espera. Entonces debe haber algo que me falta, pero no sé qué.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 

#define SERVERPORT 4950 

int main(int argc, char *argv[]) { 
    struct sockaddr_in their_addr; // connector's address information 
    struct hostent *he; 
    int numbytes; 
    int broadcast = 1; 

    if ((he=gethostbyname(argv[1])) == NULL) { // get the host info 
     perror("gethostbyname"); 
     exit(1); 
    } 

    // this call is what allows broadcast packets to be sent: 
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, 
     sizeof broadcast) == -1) { 
     perror("setsockopt (SO_BROADCAST)"); 
     exit(1); 
    } 
    their_addr.sin_family = AF_INET;  // host byte order 
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order 
    their_addr.sin_addr = *((struct in_addr *)he->h_addr); 
    memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero); 

    struct timeval tv; 
    fd_set broadcastfds; 

    int i; 
    for(i=0; i < 10; i++) { 
     tv.tv_sec = 4; 
     tv.tv_usec = 500000; 

     FD_ZERO(&broadcastfds); 
     FD_CLR(0, &broadcastfds); 
     FD_SET(0, &broadcastfds); 
     if(select(0+1, &broadcastfds, NULL, NULL, &tv) == -1) perror("select"); 

     if (FD_ISSET(0, &broadcastfds)) printf("A key was pressed!\n"); 
     else printf("Timed out.\n"); 
     fflush(stdout); 
    } 
    close(sockfd); 
    return 0; 
} 

Respuesta

3

Está utilizando FD_SET correctamente. Usted está pidiendo select() para notificarle cuando el descriptor de archivo 0 (entrada estándar) está listo para leer. Hace esto El problema es que no está leyendo la entrada estándar para consumir la entrada disponible. Por lo tanto, cuando vuelva a realizar una pausa y llame de nuevo al select(), la entrada estándar todavía estará lista para su lectura y regresará inmediatamente.

La forma correcta de utilizar select() (o poll(), que suele ser una mejor opción) es:

  • establecer todos los descriptores de ficheros implicados en el modo sin bloqueo. Para la mayoría de los casos de uso, usted quiere esto porque quiere hacer todo su bloqueo dentro de select() (o poll()), no mientras realiza el mantenimiento de los descriptores de archivos individuales.
  • Establecer los argumentos a select() o poll() para registrar cuales descriptores de archivos le interesa.
  • llamada select() o poll().
  • Reaccione a las notificaciones que le proporciona mediante el consumo de entrada y salida de escritura.
  • bucle de nuevo al paso 2.

P.S .: ¿Qué hace su socket UDP sockfd tienen que ver con nada? Lo abres pero no se usa para nada.

+0

No tiene que ver con nada. Lo siento, hice un mal trabajo eliminando las partes innecesarias de mi pregunta. – user1161604

3

El problema es que nunca se leen los datos del descriptor del archivo.

select() informa el estado, no los eventos.

De modo que después de la primera vez que selecciona() regresa, siempre hay datos disponibles para leer, por lo que select() informa eso inmediatamente.

PS. De donde sea que obtengas ese código, parece que tiene alrededor de 15 años. poll() generalmente es más conveniente que select(), y getaddrinfo() es más conveniente que gethostbyname(). [Y funcionan mejor, también].

Cuestiones relacionadas