[C] gestion des timeout sous Unix avec select()

gestion des timeout sous Unix avec select() [C] - C - Programmation

Marsh Posté le 28-02-2004 à 10:40:27    

bonjour,
je suis en train décrire un client TFTP avec de l'UDP sous Unix.
Jusque là mon client marche, mais sans gestion des timeout. C'est-à-dire que pour recevoir un datagramme du serveur, je fais un appel bloquant avec recvfrom(). J'aimerai donc mettre en place un timeout.  
On m'a parlé de la fonction select(). Après étude de la page man correspondante, et qlq essais, je ne comprend pas bien quel type de descripteur pourrait être modifié (et donc sur quel descripteur select() pourrait scruter). Les 3 types de descripteurs (en lecture , ecriture et en cas d'exception) s'appliquent à des fichiers il me semble.  
Donc  :
* ou je ne connais pas vraiment Unix, et je devrais savoir que quand un datagramme est décapsulé de la couche 3, c'est un fichier qui est modifié.
*  ou  ... je ne sais pas comment faire (!)
 
Sinon si d'autres possibilités existent pour faire des timeout (à part y aller à coup de fork() et Cie) ,  je suis preneur. merci.

Reply

Marsh Posté le 28-02-2004 à 10:40:27   

Reply

Marsh Posté le 28-02-2004 à 13:43:21    

va pour les fork, ca va claker comme solution...
 
nan serieux, le select c'est la vie, c'est pas compliqué :
avant de faire un recv (ou send), si on veut pas que l'appel soit bloquant, faut toujours faire un select avant, avec comme descripteur readfds (respectivement writefds) pour savoir si des données sont prêtes a être reçues (respectivement envoyées).
selon ce que select te retourne tu peux tranquillement appeler recv (ou send) sans que celui si ne bloque.
 
la gestion du timeout se fait avec le timeval passé au select.
 
(les descripteurs n'ont rien a voir avec des fichiers il me semble, enfin chuis pas expert SR non plus, quand tu parles de couche 3 je vois pas trop quoi ca etre)

Reply

Marsh Posté le 28-02-2004 à 14:14:15    

Konar a écrit :

va pour les fork, ca va claker comme solution...
 
nan serieux, le select c'est la vie, c'est pas compliqué :
avant de faire un recv (ou send), si on veut pas que l'appel soit bloquant, faut toujours faire un select avant, avec comme descripteur readfds (respectivement writefds) pour savoir si des données sont prêtes a être reçues (respectivement envoyées).
selon ce que select te retourne tu peux tranquillement appeler recv (ou send) sans que celui si ne bloque.
 
la gestion du timeout se fait avec le timeval passé au select.
 
(les descripteurs n'ont rien a voir avec des fichiers il me semble, enfin chuis pas expert SR non plus, quand tu parles de couche 3 je vois pas trop quoi ca etre)


 
Ok, donc il faudrait utiliser le descripteur en lecture. Mais  comment l'initaliser correctement ? Dans la page man il y a un exemple d'utilisation, avec des macros (genre FD_SET et FD_CLR) permettant de d'effacer des éléments. Alors j'ai testé cet exemple sur mon prog, mais apparemment l'initialisation du descripteur doit être mal faite, puisque le timeout s'écoule toujours. Il doit donc falloir utiliser autrement ces macros pour initialiser correctment, mais celles-ci ne sont dérites précisément nulle part. A moins que je ne l'ai pas vu (?).
 
Quand je parle de couche 3, c'est pour le modele OSI. Je me demandais si le soft qui décapsule le paquet IP, et en sort le datagramme UDP, ne modifierait pas un fichier.

Reply

Marsh Posté le 28-02-2004 à 15:03:39    

un exemple :

Code :
  1. fd_set set_read, set_write, set_exc;
  2. // init sets
  3. FD_ZERO(&set_read);
  4. FD_ZERO(&set_write);
  5. FD_ZERO(&set_exc);
  6. // read set
  7. FD_SET(Socket1, &set_read);
  8. FD_SET(Socket2, &set_read);
  9. // write set
  10. FD_SET(Socket1, &set_write);
  11. // exception set
  12. FD_SET(Socket1, &set_exc);
  13. FD_SET(Socket2, &set_exc);
  14. int err = select(nfds, &set_read, &set_write, &set_exc, &tv);


 
fais gaffe aussi au timeval, le tv_sec est en secondes, mais le tv_usec est en micro-secondes, pas en milli-secondes, c'est souvent une source de confusion.


Message édité par Konar le 28-02-2004 à 15:04:44
Reply

Marsh Posté le 28-02-2004 à 15:40:00    

ok merci je vais essayer ça.
 
juste un truc, dans ton exemple : à quoi correspondent socket1 et socket2 ?
si moi je n'ai qu'un socket sur lequel je reçois , je peux faire juste un truc comme ça ? (si Socket est mon n° de socket, et en remplissant correctement la struct tv)

Code :
  1. FD_ZERO(&set_read);
  2. FD_ZERO(&set_write);
  3. FD_ZERO(&set_exc);
  4. FD_SET(Socket, &set_read);
  5. FD_SET(Socket, &set_write);
  6. FD_SET(Socket, &set_exc);
  7. int err = select(nfds, &set_read, &set_write, &set_exc, &tv);


Message édité par mani le 28-02-2004 à 15:46:38
Reply

Marsh Posté le 28-02-2004 à 17:13:12    

Socket1 et Socket2 correspondaient aux sockets a tester l'état.
si tu n'as qu'une seule socket, alors il n'y a qu'un seul FD_SET par descripteur.
de plus si tu veux uniquement recevoir des données, inutile d'utiliser un descripteur write.

Reply

Marsh Posté le 29-02-2004 à 10:43:29    

alors j'ai testé un truc comme ça :  

Code :
  1. #define N_MAX 3
  2. #define TIMEOUT_SEC 3
  3. #define TIMEOUT_USEC 0
  4. main(){
  5. ...
  6. fd_set set_read,set_write,set_exc;
  7. struct timeval tv;
  8. int retval;
  9. int paquetRecu=0;
  10. int n=0;  /* nombre d'essais de timeout*/
  11. ...
  12. while (!paquetRecu && n<N_MAX)
  13. {
  14.  FD_ZERO(&set_read);
  15.     FD_ZERO(&set_write);
  16.     FD_ZERO(&set_exc);
  17.          FD_SET(socketClient, &set_read);
  18.  FD_SET(socketClient, &set_write);
  19.   FD_SET(socketClient, &set_exc);
  20.          tv.tv_sec = TIMEOUT_SEC;
  21.          tv.tv_usec = TIMEOUT_USEC;
  22.          retval = select(3, &set_read, &set_write,&set_exc, &tv);
  23.          if (retval)
  24.         {
  25.   tailleReception=recvfrom(socketClient,trameReception,BLOCK_SIZE+HEADER_SIZE,MSG_WAITALL,(struct sockaddr *)&donneeServeur,(socklen_t *) &tailleDonnServ);
  26.   paquetRecu=1;
  27.   }
  28.          else
  29.           {
  30.   printf("Pas de paquet reçu depuis %d secondes\n",TIMEOUT_SEC);
  31.   n++;
  32.   }
  33. }
  34. if (!paquetRecu)
  35.  {
  36.  printf("la durée du timeout s'est écoulée %d fois, sans qu'aucun paquet de soit reçu.\nVeuillez vérifier que la connexion avec le serveur est bien valide.\n",n);
  37.  return (-1);}
  38. ...
  39. }


 
socketClient est mon n° de socket, et trameReception mon tableau dans lequel je stocke les données reçues.
 
 
et bien ça ne marche pas. Le timeout s'écoule à chaque fois. (J'ai laissé le descripteur en écriture 'au cas où')
où est l'erreur ?


Message édité par mani le 29-02-2004 à 10:44:09
Reply

Marsh Posté le 29-02-2004 à 17:12:50    

détail : si le select retourne > 0, donc qu'une ou plusieurs sockets a déclenché le select, il faut tester chacun des descripteurs pour savoir quel est l'état (lecture, écriture, exception), et ce avec la macro FD_ISSET(Socket, descripteur).
par exemple dans ton code :

Code :
  1. if (retval)
  2. {
  3. if ((FD_ISSET(socketClient, &set_read)))
  4.  ; // données pretes a etre recues
  5. if ((FD_ISSET(socketClient, &set_write)))
  6.  ; // données pretes a etre écrites
  7. if ((FD_ISSET(socketClient, &set_exc)))
  8.  ; // exception
  9. }


 
sinon ton code a l'air bon, je vois pas ce qui pourrait faire que ca marche pas. essaye plusieurs trucs genre virer les descripteurs en écriture et exception, regarde du coté du 1er paramètre du select (le man de ta machine dis quoi a son propos ?), et teste bien sans select pour voir si tu reçois bien des données...
 
sinon teste mieux le retour du select : 0 pour timeout, SOCKET_ERROR pour une erreur, ou un nb égal au nb de sockets prêtes et contenues dans les descripteurs.

Reply

Marsh Posté le 01-03-2004 à 18:19:08    

merci pour tes conseils. Je n'ai pas réussi à le faire marcher, tant pis ... c'était pour un travail à rendre aujourd'hui. merci quand même.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed