| Ver tema anterior :: Ver siguiente tema | | Autor | Mensaje |
|---|
quique
Registrado: 30 Mar 2004 Mensajes: 720 Ubicación: Por ahí
| Publicado: 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í
| Publicado: 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
| Publicado: 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í
| Publicado: 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
| Publicado: 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í
| Publicado: 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 | |  | | |
| No puede crear mensajes No puede responder temas No puede editar sus mensajes No puede borrar sus mensajes No puede votar en encuestas
|
|
| |