Laboratoire 11 - Sockets

Sommaire

Remarque: n’oubliez pas, lors de l’utilisation d’appels systèmes, de traiter les cas d’erreur.

Echo

  1. Le programme srv_echo.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<signal.h>

void gere(int sock) {
  fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
  close(sock);
  unlink("sock");
  exit(EXIT_SUCCESS);
}

int main(int argc, char** argv){
  struct sigaction action;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  action.sa_handler = gere;
  sigaction(SIGINT, &action, NULL);

  int sock = socket(AF_UNIX, SOCK_STREAM, 0);

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);

  int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
  if (res == -1) {
    perror("bind");
    exit(1);
  }
  listen(sock, SOMAXCONN);
  //listen(sock, 0);

  char buf[100];
  int cli, resRead, resWrite, compteur=0;
  while(1){
    fprintf(stderr, "**En écoute..**\n");
    cli = accept(sock, NULL, NULL);
    if(cli == -1){
      perror("accept");
      exit(1);
    }
    fprintf(stderr, "**Client n°%d: Début session**\n",++compteur);
    while((resRead = read(cli, buf, sizeof(buf))) ){
      if(resRead == -1){
        perror("read cli");
        break;
      }
      resWrite = write(cli, buf, resRead);
      if(resWrite==-1){
        perror("write cli");
        break;
      }
    }
    close(cli);
    fprintf(stderr, "**Client n°%d: Fin session**\n",compteur);
  }
}
  1. Le Programme cli.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<signal.h>

void gere(int sig) {
  fprintf(stderr, "Connexion perdue\n");
  exit(1);
}

int main(int argc, char **argv)
{
  struct sigaction action;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  action.sa_handler = gere;
  sigaction(SIGPIPE, &action, NULL);

  int sock = socket(AF_UNIX, SOCK_STREAM, 0);

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);

  fprintf(stderr, "**Connexion en cours**\n");
  int res = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
  if(res==-1) {
    perror("connect");
    exit(1);
  }
  fprintf(stderr, "**Connecté**\n");
  int resWrite, resRead;
  char buf[100];
  while(fgets(buf, sizeof(buf)-1, stdin)!=NULL){
    fprintf(stderr, "**Envoi en cours**\n");
    resWrite=write(sock, buf, strlen(buf));
    if(resWrite==-1){
      perror("write cli");
      exit(1);
    }
    fprintf(stderr, "**Message envoyé**\n");
    fprintf(stderr, "**En attente du serveur...**\n");
    resRead = read(sock, buf, sizeof(buf)-1 );
    if(resRead == -1){
      perror("read cli");
      return 1;
    }
    buf[resRead]='\0';
    printf("Serveur: %s",buf);
  }

  close(sock);
  fprintf(stderr, "**Connexion teminée**\n");
  return 0;
}
  1. La valeur du backlog de listen(2) mise à 0

    • En mettant backlog à 0, au maximum 2 clients arrivent à se connecter: un client en cours de traitement et l’autre attend dans la file d’attente du serveur, les autres clients vont essayer de se connecter(le code(côté client) bloque à l’appel système connect) jusqu’à ce qu’une place se libère. NB: lorsqu’un client est accepté il ne compte plus dans le backlog
    • En écrivant une ligne dans le client n°2, le serveur ne répond pas car il est en train de traiter avec le client n°1, lorsqu’on termine le client n°1 avec Ctrl-d, le serveur commence à servir le client n°2 et le client n°3 se connecte au serveur.
  2. La valeur du backlog de listen(2) mise à SOMAXCONN

  • En mettant backlog au max, plusieurs clients arrivent à se connecter et attendent que le client 1 soit servi.

Poll (multiplexage)

  1. Le programme srv_poll.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<poll.h>
#include<signal.h>

void gere(int sock) {
  fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
  close(sock);
  unlink("sock");
  exit(EXIT_SUCCESS);
}

int main(int argc, char** argv){
  struct sigaction action;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  action.sa_handler = gere;
  sigaction(SIGINT, &action, NULL);

  int sock = socket(AF_UNIX, SOCK_STREAM, 0);

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);

  int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
  if (res == -1) {
    perror("bind");
    exit(1);
  }
  //listen(sock, 10);
  listen(sock, 0);

  //PRÉPARATION DU POLL
  struct pollfd *fds=NULL;
  nfds_t nfds=1;
  int resPoll;
  fds = malloc(sizeof(struct pollfd));
  if(fds==NULL){
    perror("malloc fds");
    exit(1);
  }
  fds[0].fd = sock;
  fds[0].events = POLLIN;

  char buf[100];
  int resRead, resWrite, ligne_log=0, nbr_clients=0, nbre_client_max=10, cli;
  while(1){
    fprintf(stderr, "%d **En écoute..**\n", ++ligne_log);
    resPoll = poll(fds, nfds, -1);
    if(resPoll==-1){
      perror("poll");
      exit(1);
    }
    for(int i=0; i<nfds ;i++){
      if(i==0 && fds[i].revents != 0 && fds[i].revents == POLLIN ){
        if(nbr_clients<nbre_client_max){
          fprintf(stderr, "%d **Un nouveau client vient de se connecter**\n", ++ligne_log);
          nfds++;
          fds = realloc(fds, sizeof(struct pollfd)*nfds);
          if(fds==NULL){
            perror("realloc fds");
            exit(1);
          }
          fds[nfds-1].fd = accept(sock, NULL, NULL);
          if(fds[nfds-1].fd == -1){
            perror("accept");
            exit(1);
          }
          fds[nfds-1].events = POLLIN | POLLHUP;
          nbr_clients++;
        }else{
          cli = accept(sock, NULL, NULL);
          if(cli == -1){
            perror("accept");
            exit(1);
          }
          close(cli);
          fprintf(stderr, "%d **Un client vient d'être refusé(max clients=%d)**\n", ++ligne_log, nbre_client_max);
        }
      }else if(fds[i].revents != 0){
        if((fds[i].revents & POLLHUP) != 0 ){
          fprintf(stderr, "%d **Un client s'est deconnecté**\n", ++ligne_log);
          close(fds[i].fd);
          fds[i].fd = -1;//pour que poll ignore cette case
          nbr_clients--;
        }else if(fds[i].revents == POLLIN){
          fprintf(stderr, "%d **Le serveur répond**\n", ++ligne_log);
          resRead = read(fds[i].fd, buf, sizeof(buf));
          if(resRead == -1){
            perror("read");
            return 1;
          }
          resWrite = write(fds[i].fd, buf, resRead);
          if(resWrite == -1){
            perror("write");
            exit(1);
          }
        }
      }
    }
  }
}

Chat

  1. Le programme srv_chat.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<poll.h>
#include<signal.h>

void gere(int sock) {
  fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
  close(sock);
  unlink("sock");
  exit(EXIT_SUCCESS);
}

int main(int argc, char** argv){
  struct sigaction action;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  action.sa_handler = gere;
  sigaction(SIGINT, &action, NULL);

  int sock = socket(AF_UNIX, SOCK_STREAM, 0);

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);

  int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
  if (res == -1) {
    perror("bind");
    exit(1);
  }
  //listen(sock, 10);
  listen(sock, 0);

  //PRÉPARATION DU POLL
  struct pollfd *fds=NULL;
  nfds_t nfds=1;
  int resPoll;
  fds = malloc(sizeof(struct pollfd));
  if(fds==NULL){
    perror("malloc fds");
    exit(1);
  }
  fds[0].fd = sock;
  fds[0].events = POLLIN;

  char buf[100], buf_copie[120];
  int resRead, resWrite, ligne_log=0, nbr_clients=0, nbre_client_max=10, cli;
  while(1){
    fprintf(stderr, "%d **En écoute..**\n", ++ligne_log);
    resPoll = poll(fds, nfds, -1);
    if(resPoll==-1){
      perror("poll");
      exit(1);
    }
    for(int i=0; i<nfds ;i++){
      if(i==0 && fds[i].revents != 0 && fds[i].revents == POLLIN ){
        if(nbr_clients<nbre_client_max){
          fprintf(stderr, "%d **Un nouveau client vient de se connecter**\n", ++ligne_log);
          nfds++;
          fds = realloc(fds, sizeof(struct pollfd)*nfds);
          if(fds==NULL){
            perror("realloc fds");
            exit(1);
          }
          fds[nfds-1].fd = accept(sock, NULL, NULL);
          if(fds[nfds-1].fd == -1){
            perror("accept");
            exit(1);
          }
          fds[nfds-1].events = POLLIN | POLLHUP;
          nbr_clients++;
        }else{
          cli = accept(sock, NULL, NULL);
          if(cli == -1){
            perror("accept");
            exit(1);
          }
          close(cli);
          fprintf(stderr, "%d **Un client vient d'être refusé(max clients=%d)**\n", ++ligne_log, nbre_client_max);
        }
      }else if(fds[i].revents != 0){
        if((fds[i].revents & POLLHUP) !=0) {
          fprintf(stderr, "%d **Un client s'est deconnecté**\n", ++ligne_log);
          close(fds[i].fd);
          fds[i].fd = -1;//pour que poll ignore cette case
          nbr_clients--;
        }else if(fds[i].revents == POLLIN){
          fprintf(stderr, "%d **Le serveur répond**\n", ++ligne_log);
          resRead = read(fds[i].fd, buf, sizeof(buf)-1);
          if(resRead == -1){
            perror("read");
            return 1;
          }
          for(int j=1; j<nfds ;j++){
            if(j!=i && fds[j].fd!=-1){
              buf[resRead] = '\0';
              snprintf(buf_copie, sizeof(buf_copie)-1, "Client %d: %s", i+1,buf);
              resWrite = write(fds[j].fd, buf_copie, resRead+10);
              if(resWrite == -1){
                perror("write");
                exit(1);
              }
            }
          }
        }
      }
    }
  }
}
  • Le programme cli.c ne permet pas une communication synchronisée, vue qu’il suit le shéma suivant en boucle lecture->envoie au serveur->attente d’une réponse.
  • /cli < /usr/share/dict/words remplace le stdin par le fichier /usr/share/dict/words et commence à lire une ligne après l’autre et l’envoyer au serveur, qui ensuite va l’envoyer aux autres clients.
  • strings /dev/urandom | ./cli crée un tube et on va lire une ligne après l’autre à partir du tube, et à chaque fois on va envoyer au serveur qui va ensuite envoyer aux autres clients.