Un serveur tcp/ip multi-clients en shell….

Tongue out

Dans cet article nous allons développer un script shell permettant de réaliser un serveur TCP/IP multi-clients à l’aide de NetCat Tongue out.

Pour partir sur de bonnes bases, je vous recommande donc la lecture de mon article, sur ….  NetCat… (pfheu, le m’ssieur ‘y fait dla pub’ pour ses articles…Wink).

Le but de cet article est d’illustrer l’utilisation de netcat, surtout l’option « -c » qui permet de rediriger les entrées et sorties d’un programme vers une socket. Cette option permet donc de s’affranchir de la programmation des sockets.

Comme il faut bien donner quelque chose à faire au serveur vis-à-vis des clients, on va lui soumettre des chaines de caractères et il en donnera un anagramme.

Gné ? Un ana-koi ?Undecided  Un anagramme. C’est lorsqu’on échange aléatoirement les lettres d’un mot, ou d’une phrase. Bon un exemple : nunix, peut donner uxnin, nxinu, xiunn, etc…

On va donc écrire un programme qui lit une chaine de caractères sur son entrée standard et en donne son anagramme sur la sortie standard : {codecitation class= »brush: c; »}#define _GNU_SOURCE        /* pour strfry */
#include    <stdlib.h>
#include    <stdio.h>
#include    <signal.h>
#include    <string.h>

#define FREE(x) do{ free(x); x=NULL; } while (0)

char *read_from_file (FILE * f, size_t max_size);
static void gestionnaire_signaux (int ignore);

/* chaine doit être une globale pour pouvoir
libérer la mémoire lors de la terminaison */
char *chaine = NULL;

int
main (void)
{

    /*————————————————————————-
     *  gestionnaire de signaux qui capture SIGTERM et SIGINT.
     *————————————————————————-*/
  struct sigaction action;
  action.sa_handler = gestionnaire_signaux;
  sigemptyset (&(action.sa_mask));
  action.sa_flags = 0;
  if ((sigaction (SIGTERM, &action, NULL) != 0)
      || (sigaction (SIGINT, &action, NULL) != 0))
    {
      fprintf (stderr, « Signal non capturé.n »);
      exit (EXIT_FAILURE);
    }

  do
    {
      /* lecture de la chaine sur stdin */
      chaine = read_from_file (stdin, 1024);
      /* il faut enlever le ‘n’ */
      chaine[strlen (chaine) – 1] = ‘’;
      /* création de l’anagramme */
      strfry (chaine);
      fprintf (stdout, « %sn », chaine);
      fflush (stdout);
      FREE (chaine);
    }
  while (1);

  /* Jamais atteint */
  return EXIT_SUCCESS;
}

char *
read_from_file (FILE * f, size_t max_size)
{
  char temp[max_size];
  if (fgets (temp, max_size, f) == NULL)
    /* fgets echoue. On sort par le gestionnaire de signaux */
    raise (SIGTERM);
  return strdup (temp);
}

static void
gestionnaire_signaux (int ignore)
{
  printf (« nbye byen »);
  FREE (chaine);
  exit (EXIT_SUCCESS);
  /* s’affranchir du warning « unused parameter ‘ignore’ » */
  (void) ignore;
}{/codecitation}

 On peut compiler facilement le programme avec la commande suivante :  gcc -Wall -Wextra -o anagramme serveur_anagramme.c

Testons le :  

screenshot

Voila, on a un programme simple… Transformons le maintenant en serveur TCP/IP sur le port 1234 :

 screenshot

Le shell du haut lance le serveur, tandis que les deux du dessous sont des clients : on peut voir les requêtes qu’ils émettent et les anagrammes qu’ils reçoivent en réponse.

On passe en paramètre du script serveur.sh la commande à exécuter lors de la connection d’un client (notre programme d’anagramme donc) ainsi que le port sur lequel le serveur doit s’établir (1234 dans notre exemple). Le nombre de clients est illimité.

Voici le script serveur.sh :{codecitation class= »brush: bash; »}

#!/bin/sh

#——————-fonctions——————-

#récupérer une liste de processus, selon un motif de recherche
#$1 le motif de recherche dans les processus
get_pid_list () {
    LISTE_PID=`ps aux | grep « $1 »  | grep -v « grep » | awk ‘{print $2}’`
    echo $LISTE_PID
}

#récupérer le nb de mots d’une liste
#$1 la variable contenant la liste
get_nb_word () {
    NB_WORD=`echo « $1 » | wc -w`
    echo $NB_WORD
}

#récupérer un mot d’une liste
#$1 la variable contenant la liste
#$2 le numero du mot à récupérer
get_word () {
    WORD=`echo « $1 » | awk -v numMot=$2 ‘{print $numMot;}’`
    echo $WORD
}

#récupérer le dernier processus d’une liste de processus trouvée selon un motif de recherche
#$1 le motif de recherche des processus
get_last_pid () {
    LISTE_PID=`get_pid_list « $1″`
    NB_WORD=`get_nb_word « $LISTE_PID »`
    WORD=`get_word « $LISTE_PID » « $NB_WORD »`
    echo $WORD
}

#——————-code serveur——————-

#verification des arguments
if [ $# != 2 ]
then
    echo « Usage : $0 commande listening_portn »
    echo  »           commande : commande à éxecuter lors de la connexion d’un client. C’est cette commande qui va dialoguer avec le client. »
    echo  »           listening_port : port sur lequel le serveur attend des clients »
    exit
else
    echo « commande : « $1″ »
    echo « listening_port  : $2 »
   
fi

COMMANDE= »$1″

#lancement du premier serveur :
(nc -l -p $2 -c « $COMMANDE » &> /dev/null ) &
PID_LAST_PROCESS=`get_last_pid « nc -l -p $2 « `

while true
do
    #on établit la liste des processus serveur
    LIST_PROCESS=`get_pid_list « sh -c $COMMANDE »`
    for i in $LIST_PROCESS
    do
        CURRENT_PROCESS=$i 
        ##détecter la connexion d’un client et lancer un nouveau serveur
        if [ « $CURRENT_PROCESS » -eq « $PID_LAST_PROCESS » ]
        then
            (nc -l -p $2 -c « $COMMANDE » &> /dev/null ) &
            PID_LAST_PROCESS=`get_last_pid « nc -l -p $2 « `
        fi
    done
    sleep 0.2
done

 {/codecitation}

Le principe du script est le suivant : puisque netcat accepte un seul client à la fois, on détecte la connection d’un client et on lance un autre serveur netcat afin qu’un nouveau client puisse se connecter (Et on fait cela en boucle).

Pour détecter la connexion d’un client, le script se base sur le fait que le processus serveur  (s’appelant donc nc -l -p port -c « commande« )  change de nom (pour devenir /bin/sh -c commande) lorsque le client se connecte.

 

Ce script a des avantages et des inconvénients : Undecided

++ permet en 10 secondes de créer un serveur TCP/IP multi-clients.

++ permet de transformer en serveur n’importe quel programme, même s’il n’est pas conçu pour.

– Créé un processus serveur par client.

– Pas de connexions simultanées (elles doivent être espacées d’au moins 1/5iem de seconde à cause du sleep 0.2 pour ne pas utiliser le processeur à 100%).

En résumé ce script est utile pour créer rapidement des petits serveurs, qui ne sont pas destinés à recevoir trop de clients.

Nous verrons dans un prochain article comment réaliser un serveur similaire en C : un serveur TCP/IP multi-client qui redirige les entrées/sorties d’un programme vers la socket….. [C’est içi !]

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *