/
Inicio :: Foros

 F.A.Q.F.A.Q.                  Conéctese para revisar sus mensajesConéctese para revisar sus mensajes   

fd_count puesto a 0 despues de llamar a select ()

 
      Índice del Foro elrincondelc.com -> Programación en Servidores
Ver tema anterior :: Ver siguiente tema  
AutorMensaje
quique



Registrado: 30 Mar 2004
Mensajes: 720
Ubicación: Por ahí

MensajePublicado: 10/06/2005 12:00 am
Título: fd_count puesto a 0 despues de llamar a select ()

Hola muchachos!

Estoy realizando una aplicación bajo Windows (XP) utilizando Visual C++ 6.0. La aplicación se comunica mediante UDP con otra aplicación. Utilizo la librería winsockets 2

El problema surge cuando llamo a la funcion "select" para comprobar si hay información a leer en el socket. Pues bien, depurando el programa me he dado cuenta que en algún momento, select pone la variable fd_count a 0. Así que la siguiente vez que llamo a select, me dice que sí hay información disponible (pues no tiene que esperar a nada!) pero no es cierto, y eso hace que mi programa se quede colgado en recvfrom (ver el código al final)

He buscado en google pero lo único que encuentro es un página a la que hay que subscribirse (en "experts exchange"...).

Gracias a todos y ahí va el código, la parte que interesa:

Código:
  // UDP Socket control
  timeval my_timeout;
  my_timeout.tv_usec = 0;
  my_timeout.tv_sec = 1; // Wait for a second
  fd_set fdSocket;
  FD_ZERO(&fdSocket);
  FD_SET(myUDPSocket, &fdSocket);

  // Yes, we are in a thread...
  while (!bCommStat && m_tthis->bKeepThreadsAlive)
  {
    // Wait until we can read
    if (!select (NULL, &fdSocket, NULL, NULL, &my_timeout))
        continue;   // Time out

    // Sometimes, here, fdSocket.fd_count is 0
    // and then, the recvfrom will wait forever, i.e. if the far end is dead
    if ((iResult = recvfrom (myUDPSocket, pMessage, 5000, 0,
                                       (sockaddr *) &m_tthis->saddr, iLength))
        bCommStat = true;
}   

_________________
/* no comment */
Volver arriba
quique



Registrado: 30 Mar 2004
Mensajes: 720
Ubicación: Por ahí

MensajePublicado: 10/06/2005 4:37 am
Título:

Creo que lo que pasa es lo siguiente:

select pone en la variable fd_set también el número del socket que cumple la condición para seleccionar. Así, cuando hay un error, select escribe en fd_count un 0.

Lo que tengo que hacer (creo), es antes de utilizar select, volver a inicializar el valor fd_set, y además, preguntar si durante select, efectivamente el socket está disponible. Sería algo así:

Código:
  // UDP Socket control
  timeval my_timeout;
  my_timeout.tv_usec = 0;
  my_timeout.tv_sec = 1;  // Time in seconds
  fd_set fdSocket;

  while (!bCommStat && m_tthis->bKeepThreadsAlive)
  {
      // Initialize the socket descriptor
      FD_ZERO(&fdSocket);
      FD_SET(myUDPSocket, &fdSocket);

      // Check the status
      if (select (NULL, &fdSocket, NULL, NULL, &my_timeout) > 0 &&
          FD_ISSET (myUDPSocket, &fdSocket))
      {
          // Now, we can get the data
          if ((iResult = recvfrom (myUDPSocket, pMessage, 5000, 0,
                                   (sockaddr *) &m_tthis->saddr, &iLength)) > 0)
              bCommStat = true;
          else
              iError = WSAGetLastError ();
      }
}



Cualquier tipo de comentario es bienvenido

Salu2
_________________
/* no comment */
Volver arriba
NELVIANT



Registrado: 04 Abr 2005
Mensajes: 151

MensajePublicado: 10/06/2005 7:31 am
Título:

Hola,

creo que lo que te hace select es(cuando no sale por TIMEOUT) disminuirte el fdcount en tantas unidades como sockets esten en situacion "de lectura" y luego tienes que ir con el FD_ISSET para comprobar cual esta en situacion de lectura.

Cuando recibes, por ejemplo, por dos sockets diferentes y compruebas el estado de lectura de los sockets mediante la funcion select, si cualquiera de los dos recibe datos, la funcion select solo te disminuye el fdcount en una unidad(pasaria de 2 a 1 y para ver cual de los dos sockets a recibido se usaria FD_ISSET). Para volver a llamar a select basta con volver a colocar el contador fdcount en el valor inicial.

Creo que la documentacion del FD_ISSET es bastante confusa porque parece que solo sirve para comprobar si un socket ya esta contenido en un conjunto fd_set, cuando en realidad sirve para comprobar si un socket dentro de un fd_set esta en situacion "activa"(de lectura en este caso).

De todas formas, por curiosidad, en vez de usar select, por que no utilizar la funcion recv y que permanezca en espera hasta que se reciban datos por el socket?

Un saludo
Volver arriba
quique



Registrado: 30 Mar 2004
Mensajes: 720
Ubicación: Por ahí

MensajePublicado: 12/06/2005 10:30 pm
Título:

Nelvian, lo primero, gracias por la respuesta.

El problema es la definicion de TimeOuts para recv o recvfrom. Si me quedo en espera hasta que reciba datos y no los recibo nunca (porque la comunicación se interrumpe) entonces, la aplicación se queda colgada a la espera de datos. Y eso no puede ser así.

Respecto a lo del FD_ISSET es eso lo que he propuesto en mi segundo mensaje: esperar con select y comprobar si lo que he recibido es lo esperado. Te referías a eso?

Salu2
_________________
/* no comment */
Volver arriba
NELVIANT



Registrado: 04 Abr 2005
Mensajes: 151

MensajePublicado: 13/06/2005 1:58 am
Título:

Hola,

el post que te envie era solo para comentar por que se modificaba el fdcount.

En cuanto a lo del FD_ISSET, es tal y como lo utilizas en el segundo mensaje que enviaste. El comentario lo inclui porque creo que la documentacion sobre FD_ISSET(la que te viene con el Visual C++ 6) es bastante confusa.

En el bucle de recepcion que tienes me parece que no hace falta poner:
FD_ZERO(&fdSocket);
FD_SET(myUDPSocket, &fdSocket);
en cada iteracion, bastaria con volver a poner fdcount a 1, pero bueno tampoco es una cosa que importe mucho.

Un saludo
Volver arriba
quique



Registrado: 30 Mar 2004
Mensajes: 720
Ubicación: Por ahí

MensajePublicado: 13/06/2005 3:59 am
Título:

Nelviant, esto es como lo dejé al final. La verdad es que la información me costó encontrarla y por eso dejo aquí la solución que hemos hablado, por si alguien más busca en internet.

Como premisas, el socket está inicializado y se trata de lecturas que bloquean el programa. Por tanto, hay que hacer lo siguiente:
- Declarar un "fd_set" e inicializarlo con FD_ZERO y FD_SET
- Antes de llamar a las funciones recv/recvfrom, llamar a select
- Hay que tener en cuenta que select modifica el valor de fd_count de entrada, con lo que hay que comprobar si lo que se ha modificado es nuestro socket, con la macro FD_ISSET
- Con el valor de vuelta de select y de la macro, realizar las acciones pertinentes (leer o terminar o lo que sea)

Salu2

Código:
// UDP Socket control
  timeval my_timeout;
  my_timeout.tv_usec = 0;
  my_timeout.tv_sec = 1;  // Time in seconds
  fd_set fdSocket;

  // Initialize the socket descriptor
  FD_ZERO(&fdSocket);
  FD_SET(myUDPSocket, &fdSocket);

  while (!bCommStat && m_tthis->bKeepThreadsAlive)
  {
      fdSocket.fd_count = 1;   
      // Check the status
      if (select (NULL, &fdSocket, NULL, NULL, &my_timeout) > 0 &&
          FD_ISSET (myUDPSocket, &fdSocket))
      {
          // Now, we can get the data
          if ((iResult = recvfrom (myUDPSocket, pMessage, 5000, 0,
                                   (sockaddr *) &m_tthis->saddr, &iLength)) > 0)
              bCommStat = true;
          else
              iError = WSAGetLastError ();
      }
}

_________________
/* no comment */
Volver arriba
      Índice del Foro elrincondelc.com -> Programación en Servidores
Página 1 de 1Todas las horas están en GMT - 8 Horas

 
No puede crear mensajes
No puede responder temas
No puede editar sus mensajes
No puede borrar sus mensajes
No puede votar en encuestas

(c) ElRincondelC.com

Un proyecto de UrlanHeat.com