2010-07-24 15 views
5

Tengo un problema con la función de selección cuando trabajé en un programa de socket Linux. La función de selección funcionó bien ya que la página del manual dice si el cliente conectó el lado del servidor en el intervalo de tiempo configurado por el servidor. Si se produjo el tiempo de espera, la función de selección devolverá 0 por siempre. En ese momento, depuré el cliente y descubrí que el cliente se había conectado al servidor. Pero la función de selección aún devuelve 0. He buscado este problema pero no he encontrado ninguno útil. ¿Alguien podría saber por qué a select le gusta eso? Mi versión de Linux es RHEL5.4. Gracias por tu ayuda.por qué select() siempre devuelve 0 después del primer tiempo de espera

El código se ilustra a continuación.

static const int maxLog = 10000; 

int main() 
{ 
    int servSock; 
    signal(SIGPIPE, SIG_IGN); 
    if((servSock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    { 
     printf("socket create fail\n"); 
     exit(-1); 
    } 
    int val = 1; 
    if(setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))<0) 
    { 
     DieWithUserMessage("setsockopt error"); 
    } 

    struct sockaddr_in serverAddr; 
    memset(&serverAddr, 0, sizeof(serverAddr)); 
    serverAddr.sin_family = AF_INET; 
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serverAddr.sin_port = htons(22000); 

    if(bind(servSock, (struct sockaddr *) &serverAddr, 
       sizeof(serverAddr)) < 0) 
    { 
     printf("socket bind fail\n"); 
     exit(-1); 
    } 

    if(listen(servSock, maxLog) < 0) 
    { 
     printf("listen failed\n"); 
     exit(-1); 
    } 

    fd_set read_set; 
    FD_ZERO(&read_set); 
    FD_SET(servSock, &read_set); 
    int maxfd1 = servSock + 1; 
    std::set<int> fd_readset; 

    for(;;){  
     struct timeval tv; 
     tv.tv_sec = 5; 
     int ret = select(maxfd1, &read_set, NULL, NULL, tv);  
     if(ret == 0) 
      continue; 

     if(ret < 0) 
      DieWithUserMessage("select error"); 

     if(FD_ISSET(servSock, &read_set)) 
     { 
      struct sockaddr_in clntAddr; 
      socklen_t clntAddrlen = sizeof(clntAddr); 
      int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrlen); 
      if(clntSock < 0) 
      { 
       printf("accept failed()"); 
       exit(-1); 
      } 

      maxfd1 = 1 + (servSock>=clntSock? servSock:clntSock); 
      FD_SET(clntSock, &read_set); 
      fd_readset.insert(clntSock); 
     } 

    } 
} 
+0

consejos generales: use encuesta() en lugar de seleccionar(). – Dummy00001

+0

Debe usar '& tv' en lugar de' tv' en 'seleccionar' –

Respuesta

19

La función 'select()' es frustrante de usar; tiene que configurar sus argumentos cada vez antes de llamar porque los modifica. Lo que estás viendo es una demostración de lo que sucede si no configuras los fd_set (s) cada vez que pasas el ciclo.

+0

Gracias por su ayuda. Restablezco el fs_set al comienzo del ciclo como sugieres y funciona bien ahora. – terry

+1

Gracias! Me estaba arrancando los pelos en este – Frederik

+0

¡Eres un salvavidas! muchas gracias. –

1

Tiene que completar su FD_SET en cada iteración. La mejor manera de hacerlo es mantener una colección de sus FD en alguna parte y colocar la que necesita para la llamada de selección en un FD_SET temporal.

Si necesita manejar muchos clientes, es posible que tenga que cambiar la macro FD_SETSIZE (en /usr/include/sys/select.h).

programación de la red feliz :)

3

Usted tiene la respuesta correcta ya - re-init las fd_set s antes de cada llamada a select(2).

Me gustaría señalarle una mejor alternativa: Linux proporciona la instalación epoll(4). Si bien no es estándar, es mucho más conveniente ya que necesita configurar los eventos que espera solo una vez. El núcleo maneja las tablas de eventos del descriptor de archivos por ti, por lo que es mucho más eficiente. epoll también proporciona funcionalidad activada por flanco, donde solo se señala un cambio de estado en un descriptor.

Para completar: los BSD proporcionan kqueue(2), Solaris tiene /dev/poll.

Una cosa más: su código tiene una condición de carrera bien conocida entre un cliente y el servidor. Eche un vistazo al Stevens UnP: Nonblocking accept.

+0

'encuesta 'está estandarizado en POSIX. –

+1

Pero no es mejor que 'seleccionar'. –

1

El mismo efecto parece ocurrir si no restablece la estructura timeval antes de cada llamada para seleccionar.

+0

POSIX dice que la función 'select()' puede modificar el parámetro de tiempo de espera.Presumiblemente, si está en un sistema que sí lo modifica, su valor modificado registra el tiempo restante hasta que caduque el tiempo de espera, que es cero si expiró el tiempo de espera, y por lo tanto después de la primera iteración la llamada se degenera en una encuesta porque el tiempo de espera cero significa 'devolver inmediatamente' en lugar de 'esperar si es necesario'. –

0

Tengo el mismo problema en mis códigos similares. Seguí la sugerencia de hacer la inicialización cada vez antes de llamar a select() y funciona. En los códigos de este caso, simplemente al poner las dos líneas en bucle lo hará funcionar.

FD_ZERO(&read_set); 
FD_SET(servSock, &read_set); 
Cuestiones relacionadas