Provided by: manpages-fr-dev_3.17.1-1_all bug

NOM

       select,  pselect,  FD_CLR,  FD_ISSET,  FD_SET,  FD_ZERO  - Multiplexage
       d’entrées-sorties synchrones.

SYNOPSIS

       /* D’après POSIX.1-2001 */
       #include <sys/select.h>

       /* D’après les standards précédents */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *utimeout);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

       #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *ntimeout,
                   const sigset_t *sigmask);

   Exigences de  macros  de  test  de  fonctionnalités  pour  la  glibc  (voir
   feature_test_macros(7)) :

       pselect() : _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >=600

DESCRIPTION

       select()  (ou  pselect())  est  la  fonction  pivot  de  la plupart des
       programmes en C qui gèrent simultanément et de façon efficace plusieurs
       descripteurs  de  fichier  (ou  sockets). Ses principaux arguments sont
       trois tableaux de  descripteurs  de  fichier :  readfds,  writefds,  et
       exceptfds.  select()  est  généralement  utilisé  de façon à bloquer en
       attendant un « changement d’état » d’un ou  plusieurs  descripteurs  de
       fichiers.  Un  « changement  d’état »  est  signalé lorsque de nouveaux
       caractères sont mis à disposition sur le descripteur  de  fichier ;  ou
       bien  lorsque  de  l’espace  devient  disponible  au niveau des tampons
       internes du noyau permettant de nouvelles écritures dans le descripteur
       de  fichier,  ou  bien  lorsqu’un  descripteur de fichier rencontre une
       erreur (dans le cas d’une socket ou d’un tube,  une  telle  erreur  est
       levée lorsque l’autre extrémité de la connexion est fermée).

       Pour  résumer,  select() surveille simplement de multiples descripteurs
       de fichiers, et constitue l’appel Unix  standard  pour  réaliser  cette
       tâche.

       Les  tableaux  de  descripteurs  de  fichier  sont appelés ensembles de
       descripteurs de fichier. Chaque ensemble est de  type  fd_set,  et  son
       contenu  peut  être  modifié  avec  les  macros  FD_CLR(),  FD_ISSET(),
       FD_SET(), et FD_ZERO(). On commence généralement par utiliser FD_ZERO()
       sur  un  ensemble  venant  d’être  créé.  Ensuite,  les descripteurs de
       fichiers individuels qui vous intéressent peuvent être ajoutés un à  un
       à  l’aide  de  FD_SET().  select()  modifie le contenu de ces ensembles
       selon les règles ci-dessous. Après un appel  à  select(),  vous  pouvez
       vérifier  si  votre  descripteur  de  fichier est toujours présent dans
       l’ensemble à l’aide de la macro FD_ISSET(). FD_ISSET() renvoie zéro  si
       le  descripteur  de  fichier  est absent et une valeur non nulle sinon.
       FD_CLR() retire un descripteur de fichier de l’ensemble.

   Arguments
       readfds
              Cet ensemble est examiné afin de déterminer si des données  sont
              disponibles  en  lecture  à  partir  d’un de ses descripteurs de
              fichier. Suite à un appel à select(), readfds ne  contient  plus
              aucun  de ses descripteurs de fichiers à l’exception de ceux qui
              sont immédiatement disponibles pour une  lecture  via  un  appel
              recv(2)  (pour les sockets) ou read(2) (pour les tubes, fichiers
              et sockets).

       writefds
              Cet ensemble est examiné afin de déterminer s’il y a de l’espace
              afin  d’écrire  des  données  dans  un  de  ses  descripteurs de
              fichier. Suite à un appel à select(), writefds ne contient  plus
              aucun  de ses descripteurs de fichiers à l’exception de ceux qui
              sont immédiatement disponibles pour une écriture via un appel  à
              send(2) (pour les sockets) ou write(2) (pour les tubes, fichiers
              et sockets).

       exceptfds
              Cet ensemble est examiné pour  les  exceptions  ou  les  erreurs
              survenues sur les descripteurs de fichier. Néanmoins, ceci n’est
              véritablement rien d’autre qu’une rumeur. exceptfds est en  fait
              utilisé afin de détecter l’occurrence de données hors-bande (Out
              Of Band). Les données hors bande sont celles qui  sont  envoyées
              sur  une socket en utilisant le drapeau MSG_OOB, ainsi exceptfds
              s’applique en réalité uniquement aux sockets.  Voir  recv(2)  et
              send(2)  à  ce  sujet. Suite à un appel à select(), exceptfds ne
              contient plus aucun de ses descripteurs de fichier à l’exception
              de  ceux  qui  sont  disponibles  pour  une  lecture  de données
              hors-bande.  Cependant,  vous  pouvez  presque   toujours   lire
              uniquement un octet de données hors bande (à l’aide de recv(2)),
              et l’écriture de données hors bande  (avec  send(2))  peut  être
              effectuée à n’importe quel moment et n’est pas bloquante. Il n’y
              a donc pas de besoin d’un quatrième ensemble afin de vérifier si
              une  socket  est  disponible  pour  une écriture de données hors
              bande.

       nfds   Il s’agit d’un entier valant un de plus que n’importe lequel des
              descripteurs  de  fichier  de  tous  les  ensembles. En d’autres
              termes, lorsque vous ajoutez des descripteurs de fichier  à  vos
              ensembles,  vous  devez déterminer la valeur entière maximale de
              tous ces derniers, puis ajouter un à cette valeur, et la  passer
              en argument nfds à select().

       utimeout
              Il  s’agit  du temps le plus long que select() pourrait attendre
              avant de rendre  la  main,  même  si  rien  d’intéressant  n’est
              arrivé.  Si cette valeur est positionnée à NULL, alors, select()
              bloque indéfiniment dans l’attente d’un événement. utimeout peut
              être  positionné  à  zéro  seconde,  ce  qui  provoque le retour
              immédiat de select(). La structure struct  timeval  est  définie
              comme :

                  struct timeval {
                      long tv_sec;    /* secondes */
                      long tv_usec;   /* microsecondes */
                  };

       ntimeout
              Cet  argument  a  la même signification que utimeout mais struct
              timespec a  une  précision  à  la  nanoseconde  comme  explicité
              ci-dessous :

                  struct timespec {
                      long tv_sec;    /* secondes */
                      long tv_nsec;   /* nanosecondes */
                  };

       sigmask
              Cet argument renferme un ensemble de signaux non bloqués pendant
              un appel pselect() (voir  sigaddset(3)  et  sigprocmask(2)).  Il
              peut  valoir NULL, et, dans ce cas, il ne modifie pas l’ensemble
              des signaux non bloqués à l’entrée et la sortie de la  fonction.
              Il se comporte alors de façon identique à select().

   Combinaison dévénements de signaux et de données
       pselect()  doit être utilisé si vous attendez tout aussi bien un signal
       que des  données  d’un  descripteur  de  fichier.  Les  programmes  qui
       reçoivent  les  signaux  comme des événements utilisent généralement le
       gestionnaire de signal uniquement pour  lever  un  drapeau  global.  Le
       drapeau  global indique que l’événement doit être traité dans la boucle
       principale du programme. Un signal provoque l’arrêt de l’appel select()
       (ou  pselect())  avec  errno  positionnée  à EINTR. Ce comportement est
       essentiel afin que les signaux puissent être  traités  dans  la  boucle
       principale  du  programme, sinon select() bloquerait indéfiniment. Ceci
       étant,  la  boucle  principale  implante  quelque  part  une  condition
       vérifiant  le  drapeau  global,  et l’on doit donc se demander : que se
       passe-t-il si un signal est levé après la condition mais avant  l’appel
       à  select() ? La réponse est que select() bloquerait indéfiniment, même
       si un signal est en fait en attente. Cette "race condition" est résolue
       par  l’appel  pselect().  Cet appel peut être utilisé afin de débloquer
       des signaux qui ne sont pas  censés  être  reçus  si  ce  n’est  durant
       l’appel  à  pselect().  Par exemple, disons que l’événement en question
       est la fin d’un  processus  fils.  Avant  le  démarrage  de  la  boucle
       principale, nous bloquerions SIGCHLD en utilisant sigprocmask(2). Notre
       appel pselect() débloquerait SIGCHLD en utilisant le masque  de  signal
       initial. Le programme ressemblerait à ceci :

       int child_events = 0;

       void
       child_sig_handler(int x)
       {
           child_events++;
           signal(SIGCHLD, child_sig_handler);
       }

       int
       main(int argc, char **argv)
       {
           sigset_t sigmask, orig_sigmask;

           sigemptyset(&sigmask);
           sigaddset(&sigmask, SIGCHLD);
           sigprocmask(SIG_BLOCK, &sigmask, &orig_sigmask);

           signal(SIGCHLD, child_sig_handler);

           for (;;) { /* boucle principale */
               for (; child_events > 0; child_events--) {
                   /* traiter les événements ici */
               }
               r = pselect(nfds, &rd, &wr, &er, 0, &orig_sigmask);

               /* corps principal du programme */
           }
       }

   Pratique
       Quelle  est  donc  la  finalité de select() ? Ne peut on pas simplement
       lire et écrire dans les descripteurs chaque fois  qu’on  le  souhaite ?
       L’objet  de  select()  est  de  surveiller  de  multiples  descripteurs
       simultanément et d’endormir proprement le  processus  s’il  n’y  a  pas
       d’activité.  Il fait ceci tout en vous permettant de gérer de multiples
       tubes et sockets simultanément. Les  programmeurs  UNIX  se  retrouvent
       souvent  dans  une  situation  dans  laquelle ils doivent gérer des E/S
       provenant de plus d’un descripteur de fichier et dans laquelle le  flux
       de données est intermittent. Si vous deviez créer une séquence d’appels
       read(2) et write(2), vous vous retrouveriez potentiellement bloqué  sur
       un  de  vos  appels  attendant  pour  lire  ou  écrire  des  données  à
       partir/vers un descripteur de fichier, alors qu’un autre descripteur de
       fichier  est  inutilisé bien qu’il soit disponible pour lire/écrire des
       données. select() gère efficacement cette situation.

       Un exemple classique de select() se trouve dans la page  de  manuel  de
       select(2).

   Règles de select
       De  nombreuses personnes qui essaient d’utiliser select() obtiennent un
       comportement difficile à comprendre et  produisent  des  résultats  non
       portables  ou  des  effets de bord. Par exemple, le programme ci-dessus
       est écrit avec précaution afin de ne bloquer nulle part, même  s’il  ne
       positionne pas du tout ses descripteurs de fichier en mode non bloquant
       (voir ioctl(2)). Il est facile d’introduire des  erreurs  subtiles  qui
       annuleraient l’avantage de l’utilisation de select(), aussi, cette page
       présente  une  liste  de  points  essentiels  à   contrôler   lors   de
       l’utilisation de l’appel select().

       1.  Vous  devriez  toujours  essayer  d’utiliser select() sans timeout.
           Votre programme ne devrait rien avoir à faire s’il  n’y  a  pas  de
           données disponibles. Le code dépendant de timeouts n’est en général
           pas portable et difficile à déboguer.

       2.  La valeur nfds doit être calculée  correctement  pour  des  raisons
           d’efficacité comme expliqué plus haut.

       3.  Aucun  descripteur  de  fichier ne doit être ajouté à un quelconque
           ensemble si vous ne projetez pas de  vérifier  son  état  après  un
           appel  à  select(),  et  de réagir de façon adéquate. Voir la règle
           suivante.

       4.  Après le retour de select(), tous les descripteurs de fichier  dans
           tous  les  ensembles  devraient  être testés pour savoir s’ils sont
           prêts.

       5.  Les fonctions read(2), recv(2), write(2) et send(2)  ne  lisent  ou
           n’écrivent  pas  forcément la quantité totale de données spécifiée.
           Si elles lisent/écrivent la quantité totale, c’est parce  que  vous
           avez  une  faible  charge de trafic et un flux rapide. Ce n’est pas
           toujours le cas.  Vous  devriez  gérer  le  cas  où  vos  fonctions
           traitent seulement l’envoi ou la réception d’un unique octet.

       6.  Ne  lisez/n’écrivez  jamais  seulement  quelques octets à la fois à
           moins que vous ne soyez absolument sûr  de  n’avoir  qu’une  faible
           quantité de données à traiter. Il est parfaitement inefficace de ne
           pas lire/écrire autant de données que  vous  pouvez  en  stocker  à
           chaque  fois.  Les  tampons de l’exemple ci-dessus font 1024 octets
           bien qu’ils aient facilement pu être rendus plus grands.

       7.  Les fonctions read(2), recv(2),  write(2)  et  send(2)  tout  comme
           l’appel  select() peuvent renvoyer -1 avec errno positionné à EINTR
           ou EAGAIN (EWOULDBLOCK) ce qui ne  relève  pas  d’une  erreur.  Ces
           résultats  doivent  être  correctement  gérés  (cela n’est pas fait
           correctement  ci-dessus).  Si  votre  programme  n’est  pas   censé
           recevoir  de  signal,  alors,  il est hautement improbable que vous
           obteniez EINTR. Si votre programme n’a pas  configuré  les  E/S  en
           mode non bloquant, vous n’obtiendrez pas de EAGAIN. Néanmoins, vous
           devriez tout de même gérer ces erreurs dans un souci de complétude.

       8.  N’appelez  jamais  read(2),  recv(2),  write(2)  ou send(2) avec un
           tampon de taille nulle.

       9.  Si l’une des fonctions read(2), recv(2), write(2) et send(2) échoue
           avec  une  erreur autre que celles indiquées en 7., ou si l’une des
           fonctions d’entrée renvoie 0, indiquant une fin de fichier, vous ne
           devriez  pas  utiliser  ce  descripteur  à  nouveau pour un appel à
           select().   Dans   l’exemple   ci-dessus,   le   descripteur    est
           immédiatement  fermé  et  ensuite est positionné à -1 afin qu’il ne
           soit pas inclus dans un ensemble.

       10. La valeur de timeout doit être initialisée à chaque nouvel appel  à
           select(),   puisque   des   systèmes  d’exploitation  modifient  la
           structure. Cependant, pselect() ne  modifie  pas  sa  structure  de
           timeout.

       11. Il   paraît   que  la  couche  socket  de  Windows  ne  traite  pas
           correctement les données hors bande (OOB). Il ne gère pas non  plus
           les  appels  select()  lorsqu’aucun  descripteur  de  fichier n’est
           positionné. N’avoir aucun descripteur de fichier positionné est  un
           moyen  utile  afin  d’endormir  le  processus  avec  une  précision
           inférieure à la seconde en utilisant le timeout. (Voir plus  loin.)

   Émulation de usleep
       Sur  les  systèmes  qui  ne  possèdent  pas la fonction usleep(3), vous
       pouvez appeler  select()  avec  un  timeout  à  valeur  finie  et  sans
       descripteur de fichier de la façon suivante :

           struct timeval tv;
           tv.tv_sec = 0;
           tv.tv_usec = 200000;  /* 0.2 secondes */
           select(0, NULL, NULL, NULL, &tv);

       Le fonctionnement n’est cependant garanti que sur les systèmes Unix.

VALEUR RENVOYÉE

       En  cas  de succès, select() renvoie le nombre total de descripteurs de
       fichiers encore présents dans les ensembles de descripteurs de fichier.

       En  cas  de  timeout  échu, alors les descripteurs de fichier devraient
       tous être vides (mais peuvent ne pas l’être sur certains systèmes). Par
       contre, la valeur renvoyée est zéro.

       Une  valeur  de  retour  égale à -1 indique une erreur, errno est alors
       positionné de façon adéquate. En cas d’erreur, le contenu des ensembles
       renvoyés  et le contenu de la structure de timeout sont indéfinis et ne
       devraient pas être exploités. pselect()  ne  modifie  cependant  jamais
       ntimeout.

NOTES

       De  façon  générale,  tous  les  systèmes d’exploitation qui gèrent les
       sockets, implantent également select(). De nombreux types de programmes
       deviennent  extrêmement  compliqués  sans cette fonction. select() peut
       être utilisé pour résoudre de façon portable et  efficace  de  nombreux
       problèmes  que  des  programmeurs  naïfs  essaient de résoudre avec des
       threads, des forks, des IPC, des signaux,  des  mémoires  partagées  et
       d’autres méthodes peu élégantes.

       L’appel  système poll(2) a les mêmes fonctionnalités que select(), tout
       en  étant  légèrement  plus  efficace  quand  il  doit  surveiller  des
       ensembles  de  descripteurs creux. Il est disponible sur la plupart des
       systèmes de nos jours, mais était  historiquement  moins  portable  que
       select().

       L’API  epoll(7)  spécifique à Linux fournit une interface plus efficace
       que select(2) et poll(2) lorsque l’on  surveille  un  grand  nombre  de
       descripteurs de fichier.

EXEMPLE

       Voici un exemple qui montre mieux l’utilité réelle de select(). Le code
       ci-dessous consiste en un programme de « TCP forwarding » qui  redirige
       un port TCP vers un autre.

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

       static int forward_port;

       #undef max
       #define max(x,y) ((x) > (y) ? (x) : (y))

       static int
       listen_socket(int listen_port)
       {
           struct sockaddr_in a;
           int s;
           int yes;

           if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
               perror("socket");
               return -1;
           }
           yes = 1;
           if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                   (char *) &yes, sizeof(yes)) < 0) {
               perror("setsockopt");
               close(s);
               return -1;
           }
           memset(&a, 0, sizeof(a));
           a.sin_port = htons(listen_port);
           a.sin_family = AF_INET;
           if (bind(s, (struct sockaddr *) &a, sizeof(a)) < 0) {
               perror("bind");
               close(s);
               return -1;
           }
           printf("accepting connections on port %d\n", listen_port);
           listen(s, 10);
           return s;
       }

       static int
       connect_socket(int connect_port, char *address)
       {
           struct sockaddr_in a;
           int s;

           if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
               perror("socket");
               close(s);
               return -1;
           }

           memset(&a, 0, sizeof(a));
           a.sin_port = htons(connect_port);
           a.sin_family = AF_INET;

           if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) {
               perror("bad IP address format");
               close(s);
               return -1;
           }

           if (connect(s, (struct sockaddr *) &a, sizeof(a)) < 0) {
               perror("connect()");
               shutdown(s, SHUT_RDWR);
               close(s);
               return -1;
           }
           return s;
       }

       #define SHUT_FD1 do {                                \
                            if (fd1 >= 0) {                 \
                                shutdown(fd1, SHUT_RDWR);   \
                                close(fd1);                 \
                                fd1 = -1;                   \
                            }                               \
                        } while (0)

       #define SHUT_FD2 do {                                \
                            if (fd2 >= 0) {                 \
                                shutdown(fd2, SHUT_RDWR);   \
                                close(fd2);                 \
                                fd2 = -1;                   \
                            }                               \
                        } while (0)

       #define BUF_SIZE 1024

       int
       main(int argc, char **argv)
       {
           int h;
           int fd1 = -1, fd2 = -1;
           char buf1[BUF_SIZE], buf2[BUF_SIZE];
           int buf1_avail, buf1_written;
           int buf2_avail, buf2_written;

           if (argc != 4) {
               fprintf(stderr, "Utilisation\n\tfwd <listen-port> "
                        "<forward-to-port> <forward-to-ip-address>\n");
               exit(EXIT_FAILURE);
           }

           signal(SIGPIPE, SIG_IGN);

           forward_port = atoi(argv[2]);

           h = listen_socket(atoi(argv[1]));
           if (h < 0)
               exit(EXIT_FAILURE);

           for (;;) {
               int r, nfds = 0;
               fd_set rd, wr, er;
               FD_ZERO(&rd);
               FD_ZERO(&wr);
               FD_ZERO(&er);
               FD_SET(h, &rd);
               nfds = max(nfds, h);
               if (fd1 > 0 && buf1_avail < BUF_SIZE) {
                   FD_SET(fd1, &rd);
                   nfds = max(nfds, fd1);
               }
               if (fd2 > 0 && buf2_avail < BUF_SIZE) {
                   FD_SET(fd2, &rd);
                   nfds = max(nfds, fd2);
               }
               if (fd1 > 0
                   && buf2_avail - buf2_written > 0) {
                   FD_SET(fd1, &wr);
                   nfds = max(nfds, fd1);
               }
               if (fd2 > 0
                   && buf1_avail - buf1_written > 0) {
                   FD_SET(fd2, &wr);
                   nfds = max(nfds, fd2);
               }
               if (fd1 > 0) {
                   FD_SET(fd1, &er);
                   nfds = max(nfds, fd1);
               }
               if (fd2 > 0) {
                   FD_SET(fd2, &er);
                   nfds = max(nfds, fd2);
               }

               r = select(nfds + 1, &rd, &wr, &er, NULL);

               if (r == -1 && errno == EINTR)
                   continue;
               if (r < 0) {
                   perror("select()");
                   exit(EXIT_FAILURE);
               }
               if (FD_ISSET(h, &rd)) {
                   unsigned int l;
                   struct sockaddr_in client_address;

                   memset(&client_address, 0, l = sizeof(client_address));
                   r = accept(h, (struct sockaddr *) &client_address, &l);
                   if (r < 0) {
                       perror("accept()");
                   } else {
                       SHUT_FD1;
                       SHUT_FD2;
                       buf1_avail = buf1_written = 0;
                       buf2_avail = buf2_written = 0;
                       fd1 = r;
                       fd2 = connect_socket(forward_port, argv[3]);
                       if (fd2 < 0)
                           SHUT_FD1;
                       else
                           printf("connexion de %s\n",
                                   inet_ntoa(client_address.sin_addr));
                   }
               }

            /* NB : lecture des données hors bande avant les lectures normales */

               if (fd1 > 0)
                   if (FD_ISSET(fd1, &er)) {
                       char c;
                       errno = 0;
                       r = recv(fd1, &c, 1, MSG_OOB);
                       if (r < 1)
                           SHUT_FD1;
                       else
                           send(fd2, &c, 1, MSG_OOB);
                   }
               if (fd2 > 0)
                   if (FD_ISSET(fd2, &er)) {
                       char c;
                       errno = 0;
                       r = recv(fd2, &c, 1, MSG_OOB);
                       if (r < 1)
                           SHUT_FD1;
                       else
                           send(fd1, &c, 1, MSG_OOB);
                   }
               if (fd1 > 0)
                   if (FD_ISSET(fd1, &rd)) {
                       r = read(fd1, buf1 + buf1_avail,
                                 BUF_SIZE - buf1_avail);
                       if (r < 1)
                           SHUT_FD1;
                       else
                           buf1_avail += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET(fd2, &rd)) {
                       r = read(fd2, buf2 + buf2_avail,
                                 BUF_SIZE - buf2_avail);
                       if (r < 1)
                           SHUT_FD2;
                       else
                           buf2_avail += r;
                   }
               if (fd1 > 0)
                   if (FD_ISSET(fd1, &wr)) {
                       r = write(fd1, buf2 + buf2_written,
                                  buf2_avail - buf2_written);
                       if (r < 1)
                           SHUT_FD1;
                       else
                           buf2_written += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET(fd2, &wr)) {
                       r = write(fd2, buf1 + buf1_written,
                                  buf1_avail - buf1_written);
                       if (r < 1)
                           SHUT_FD2;
                       else
                           buf1_written += r;
                   }

            /* Vérifie si l’écriture de données a rattrapé la lecture de données */

               if (buf1_written == buf1_avail)
                   buf1_written = buf1_avail = 0;
               if (buf2_written == buf2_avail)
                   buf2_written = buf2_avail = 0;

               /* une extrémité a fermé la connexion, continue
                  d’écrire vers l’autre extrémité jusqu’à ce
                  que ce soit vide */

               if (fd1 < 0 && buf1_avail - buf1_written == 0)
                   SHUT_FD2;
               if (fd2 < 0 && buf2_avail - buf2_written == 0)
                   SHUT_FD1;
           }
           exit(EXIT_SUCCESS);
       }

       Le  programme  ci-dessus  redirige correctement la plupart des types de
       connexions TCP y compris les signaux de données hors bande OOB transmis
       par  les  serveurs  telnet.  Il  gère  le  problème épineux des flux de
       données bidirectionnels simultanés. Vous pourriez penser qu’il est plus
       efficace  d’utiliser  un  appel fork(2) et de dédier une tâche à chaque
       flux. Cela devient alors plus délicat que vous ne l’imaginez. Une autre
       idée  est  de  configurer  les E/S comme non bloquantes en utilisant un
       appel ioctl(2). Cela pose également problème puisque ça  vous  force  à
       utiliser des timeouts inefficaces.

       Le programme ne gère pas plus d’une connexion à la fois bien qu’il soit
       aisément extensible à une telle fonctionnalité en utilisant  une  liste
       chaînée  de  tampons  —  un  pour  chaque connexion. Pour l’instant, de
       nouvelles connexions provoquent l’abandon de la connexion courante.

VOIR AUSSI

       accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2),  select(2),
       send(2),    sigprocmask(2),   write(2),   sigaddset(3),   sigdelset(3),
       sigemptyset(3), sigfillset(3), sigismember(3), epoll(7)

COLOPHON

       Cette page fait partie de  la  publication  3.17  du  projet  man-pages
       Linux.  Une description du projet et des instructions pour signaler des
       anomalies      peuvent      être       trouvées       à       l’adresse
       http://www.kernel.org/doc/man-pages/.

TRADUCTION

       Cette  page  de  manuel  a  été traduite par Stéphan Rafin <stephan DOT
       rafin AT laposte DOT net> en 2002, puis a été mise  à  jour  par  Alain
       Portal  <aportal  AT  univ-montp2  DOT  fr>  jusqu’en  2006,  et mise à
       disposition sur http://manpagesfr.free.fr/.

       Les mises à jour et corrections de la version présente dans Debian sont
       directement gérées par Julien Cristau <jcristau@debian.org> et l’équipe
       francophone de traduction de Debian.

       Veuillez  signaler  toute  erreur   de   traduction   en   écrivant   à
       <debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
       paquet manpages-fr.

       Vous pouvez toujours avoir accès à la version anglaise de  ce  document
       en utilisant la commande « man -L C <section> <page_de_man> ».