Pour  recevoir des  appels téléphoniques,  il nous  faut installer  le
téléphone.  De même,  nous devons  créer une  socket qui  attendra des
connexions. Cela se fait en plusieurs étapes. En premier lieu, il faut
créer une nouvelle socket, ce  qui est analogue à l'installation d'une
nouvelle ligne  téléphonique; l'appel socket() est  destiné à
cette opération.
Comme ces sockets peuvent être de plusieurs types, vous devez préciser celui que vous désirez lors de leur création. L'une des options, c'est le format d'adressage: tout comme un service postal utilise une méthode d'adressage fort différente qu'une compagnie de téléphone pour trouver les correspondants, les sockets peuvent également employer plusieurs méthodes. Les plus connues et les plus utilisées sont AF_UNIX et AF_INET. L'adressage AF_UNIX utilise des chemins d'accès de fichiers pour identifier les sockets\,; c'est très pratique pour les communications entre processus fonctionnant sur une même machine. AF_INET, par contre, emploie des adresses de type Internet, qui sont composées de quatre octets, généralement notés sous forme décimale, séparés par des points (comme 192.9.200.10 par exemple). En plus de l'adresse de la machine destinataire, il lui est associé un numéro de port, qui permet d'avoir plusieurs sockets AF_INET sur chaque machine. Ici, nous ne parlerons que de l'adressage AF_INET, celui qui permet la communication entre systèmes distants sur un réseau.
Lors de la création des sockets, vous devez spécifier une seconde option, le type désiré: SOCK_STREAM et SOCK_DGRAM. Dans le premier, les données sont transmises comme un flux de caractères, alors que dans le second elles transitent par blocs, appelés datagrammes. Nous ne verrons ici que le plus courant et le plus souple, SOCK_STREAM.
Après la création, nous devons  indiquer à notre socket quelle adresse
écouter, exactement comme le numéro de téléphone que vous obtenez vous
permettra  de  recevoir des  appels.  C'est  le  rôle de  la  fonction
bind(), qui ``attache'' une socket à une adresse.
Les  sockets de  type SOCK_STREAM  ont  la possibilité  de mettre  les
requêtes  de  connexion  dans  une  queue,  en  attendant  de  pouvoir
les  traiter; exactement  comme le  service de  ``signal d'appel''  du
téléphone. Si vous êtes en train  de traiter une connexion, toutes les
autres  requêtes attendront  que  vous puissiez  vous  en occuper.  La
fonction  listen  permet  d'indiquer  le  nombre  maximum  de
requêtes possibles  dans la  file d'attente (cinq,  généralement), les
autres  se  verront refuser  la  connexion.  Bien  qu'il ne  soit  pas
nécessaire d'utiliser  listen(), c'est malgré tout  une bonne
pratique, prenez-en l'habitude.
L'exemple   suivant   montre   comment   utiliser   socket(),
bind()  et listen()  afin  de créer  une socket  qui
pourra accepter des connexions.
/* Code permettant d'etablir une socket */
int establish(portnum)
u_short portnum;
{
        char   myname[MAXHOSTNAMELEN+1];
        int    s;
        struct sockaddr_in sa;
        struct hostent *hp;
        memset(&sa, 0, sizeof(struct sockaddr_in)); /* Nettoyage de notre adresse */
        gethostname(myname, MAXHOSTNAMELEN);            /* Qui sommes-nous            */
        hp = gethostbyname(myname);                     /* Recuperons notre adresse   */
        if (hp == NULL)                                 /* Nous n'existons-pas ???    */
                return(-1);
        sa.sin_family = hp->h_addrtype;                 /* Notre adresse              */
        sa.sin_port =   htons(portnum);                 /* Notre numero de port       */
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)  /* Creation de la socket      */
                return(-1);
        if (bind(s,&sa,sizeof(struct sockaddr_in)) < 0) {
                close(s);
                return(-1);                             /* Attache la socket au port  */
                }
        listen(s, 3);                                   /* Taille max de la queue     */
        return(s);
  }
 
Après  cette création  de socket  permettant de  recevoir des  appels,
vous  devez  répondre  aux  demandes  de  connexions.  C'est  le  rôle
de  la fonction  accept(),  qui est  analogue  à l'action  de
décrocher  le  combiné  lorsque  le téléphone  sonne.  Cette  fonction
accept() va retourner une nouvelle socket, qui sera connectée
à l'appelant.
La   fonction  suivante   peut   être  utilisée   pour  accepter   les
connexions  sur   une  socket  créée  par   notre  exemple  précédent,
establish().
int get_connection(s)
int s;                                  /* Socket creee par establish() */
{
        struct sockaddr_in isa;         /* Adresse de la  socket        */
        int i;                          /* Taille de l'adresse          */
        int t;                          /* Nouvelle socket connectee    */
        i = sizeof(struct sockaddr_in);
        if ((t = accept(s, &isa, &i)) < 0)   /* accepte si connexion    */
                return(-1);
        return(t);                      /* Retourne la nouvelle socket  */
}
 
Contrairement  au   téléphone,  vous  pouvez  toujours   accepter  des
connexions  pendant  que  vous  traitez les  précédentes.  Pour  cette
raison,   il  est   de   coutume  d'effectuer   le  traitement   après
un  fork.   L'exemple  qui   suit  montre   comment  utiliser
establish  et get_connection  afin  de permettre  de
multiples connexions:
#include <stdio.h>              /* En-tetes obligatoires pour les declarations */
#include <errno.h>      
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#define PORTNUM 50000 /* Il nous faut un numero de port. Pour l'instant, nous en */
                      /* prenons un totalement arbitraire.                       */
void fireman(), travaille();
main()
{
        int s, t;
        if ((s = establish(PORTNUM)) < 0) {            /* Branchons le telephone. */
                perror("establish");
                exit(1);
                }
        signal(SIGCHLD, fireman);                       /* Evite les zombies      */
        for (;;) {                                      /* Boucle d'attente       */
                if ((t = get_connection(s)) < 0) {      /* Realisation connexion  */
                        if (errno == EINTR) continue;   /* Essayons encore...     */
                        perror("accept");               /* Pas bon du tout.       */
                        exit(1);
                        }
                switch(fork()) {                /* Essayons de gerer la connexion */
                        case -1 :               /* Houla, grave. Arretons tout.   */
                                perror("fork");
                                close(s);                                       
                                close(t);
                                exit(1);
                        case 0 :                /* Nous sommes le fils; au travail*/
                                close(s);
                                travaille(t);
                                exit(0);
                        default :               /* Nous sommes le pere, donc nous */
                                close(t);       /* attendons d'autres connexions  */
                                continue;
                        }
                }
}
/* 
 *   Nous devons capturer le retour des fils qui sont tues, car sinon
 *   nous aurons des processus zombies. Cette fonction s'en occupe.
 */
     
void fireman()
{
        union wait wstatus;
        while(wait3(&wstatus, WNOHANG, NULL) > 0);
}
/*
 *   Voici la fonction qui utilise la socket. 
 *   Elle sera appelee apres l'etablissement de la connexion. 
 */
void travaille(s)
int s;
{
        /* Ici, faites ce que vous voulez avec votre socket.
        :
        :
        */
}
 
Bien. Vous savez  maintenant comment créer une socket  qui accepte les
demandes  de  connexion. Mais  comment  l'appeler  ? Pour  passer  un
appel  téléphonique, vous  devez  avoir le  téléphone.  Et bien,  vous
allez utiliser  la fonction  socket(), exactement  comme pour
l'établissement d'une socket qui doit attendre les connexions.
Mais vous  utiliserez la  fonction connect() pour  essayer de
vous ``brancher'' sur une socket qui attend des connexions; on dit qui
``écoute'' un port.  La fonction suivante appelle  un port particulier
d'une certaine machine, qu'on lui passe en paramètres:
int call_socket(hostname, portnum)
char *hostname;
int portnum;
{
        struct sockaddr_in sa;
        struct hostent *hp;
        int a, s;
        if ((hp = gethostbyname(hostname)) == NULL) { /* Connaissons-nous l'adresse */
                errno = ECONNREFUSED;                 /* de  la machine ?           */
                return(-1);                           /* Non, introuvable.          */
                }
                                                      /* Initialisation de l'adresse*/
        bzero(&,sa, sizeof(sa));                   /* et du port a utiliser      */
        bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length); 
        sa.sin_family = hp->h_addrtype;
        sa.sin_port = htons((u_short)portnum);
        if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)   /* creation socket */
                return(-1);
        if (connect(s, &sa, sizeof sa) < 0) {               /* puis connexion. */
                close(s);
                return(-1);
                }
        return(s);
}                                      
 
Cette fonction retourne une socket connectée, par laquelle les données peuvent alors transiter.