{"id":41,"date":"2009-05-05T23:00:00","date_gmt":"2009-05-05T21:00:00","guid":{"rendered":"http:\/\/46.105.150.90\/tstnunix\/?p=41"},"modified":"2016-08-21T19:38:14","modified_gmt":"2016-08-21T17:38:14","slug":"un-serveur-tcpip-multi-clients-en-shell","status":"publish","type":"post","link":"https:\/\/nunix.fr\/?p=41","title":{"rendered":"Un serveur tcp\/ip multi-clients en shell&#8230;."},"content":{"rendered":"<p>Dans cet article nous allons d\u00e9velopper un script shell permettant de r\u00e9aliser un serveur TCP\/IP multi-clients \u00e0 l&rsquo;aide de NetCat <img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-12\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/04\/smiley-tongue-out.gif\" border=\"0\" alt=\"Tongue out\" title=\"Tongue out\" width=\"18\" height=\"18\" \/>. <\/p>\n<p>Pour partir sur de bonnes bases, je vous recommande donc la lecture de mon article, sur &#8230;.\u00a0 <a href=\"http:\/\/46.105.150.90\/tstnunix\/?p=37\">NetCat<\/a>&#8230; (pfheu, le m&rsquo;ssieur &lsquo;y fait dla pub&rsquo; pour ses articles&#8230;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-13\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/04\/smiley-wink.gif\" border=\"0\" alt=\"Wink\" title=\"Wink\" width=\"18\" height=\"18\" \/>). <\/p>\n<p> <!--more--> <\/p>\n<p>Le but de cet article est d&rsquo;illustrer l&rsquo;utilisation de netcat, surtout l&rsquo;option \u00ab\u00a0-c\u00a0\u00bb qui permet de rediriger les entr\u00e9es et sorties d&rsquo;un programme vers une socket. Cette option permet donc de s&rsquo;affranchir de la programmation des sockets.<\/p>\n<p>Comme il faut bien donner quelque chose \u00e0 faire au serveur vis-\u00e0-vis des clients, on va lui soumettre des chaines de caract\u00e8res et il en donnera un anagramme.<\/p>\n<p class=\"caption\"><em>Gn\u00e9 <\/em>? Un ana-koi ?<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-38\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/05\/smiley-undecided.gif\" border=\"0\" alt=\"Undecided\" title=\"Undecided\" width=\"18\" height=\"18\" \/>\u00a0 Un anagramme. C&rsquo;est lorsqu&rsquo;on \u00e9change al\u00e9atoirement les lettres d&rsquo;un mot, ou d&rsquo;une phrase. Bon un exemple : nunix, peut donner uxnin, nxinu, xiunn, etc&#8230;<\/p>\n<p class=\"caption\">On va donc \u00e9crire un programme qui lit une chaine de caract\u00e8res sur son entr\u00e9e standard et en donne son anagramme sur la sortie standard : {codecitation class=\u00a0\u00bbbrush: c;\u00a0\u00bb}#define _GNU_SOURCE\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \/* pour strfry *\/<br \/>#include\u00a0\u00a0\u00a0 &lt;stdlib.h&gt;<br \/>#include\u00a0\u00a0\u00a0 &lt;stdio.h&gt;<br \/>#include\u00a0\u00a0\u00a0 &lt;signal.h&gt;<br \/>#include\u00a0\u00a0\u00a0 &lt;string.h&gt;<\/p>\n<p>#define FREE(x) do{ free(x); x=NULL; } while (0)<\/p>\n<p>char *read_from_file (FILE * f, size_t max_size);<br \/>static void gestionnaire_signaux (int ignore);<\/p>\n<p>\/* chaine doit \u00eatre une globale pour pouvoir <br \/>lib\u00e9rer la m\u00e9moire lors de la terminaison *\/<br \/>char *chaine = NULL;<\/p>\n<p>int<br \/>main (void)<br \/>{<\/p>\n<p>\u00a0\u00a0\u00a0 \/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\u00a0\u00a0\u00a0\u00a0 *\u00a0 gestionnaire de signaux qui capture SIGTERM et SIGINT.<br \/>\u00a0\u00a0\u00a0\u00a0 *&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-*\/<br \/>\u00a0 struct sigaction action;<br \/>\u00a0 action.sa_handler = gestionnaire_signaux;<br \/>\u00a0 sigemptyset (&#038;(action.sa_mask));<br \/>\u00a0 action.sa_flags = 0;<br \/>\u00a0 if ((sigaction (SIGTERM, &#038;action, NULL) != 0)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 || (sigaction (SIGINT, &#038;action, NULL) != 0))<br \/>\u00a0\u00a0\u00a0 {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 fprintf (stderr, \u00ab\u00a0Signal non captur\u00e9.n\u00a0\u00bb);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 exit (EXIT_FAILURE);<br \/>\u00a0\u00a0\u00a0 }<\/p>\n<p>\u00a0 do<br \/>\u00a0\u00a0\u00a0 {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 \/* lecture de la chaine sur stdin *\/<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 chaine = read_from_file (stdin, 1024);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 \/* il faut enlever le &lsquo;n&rsquo; *\/<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 chaine[strlen (chaine) &#8211; 1] = &lsquo;\u0000&rsquo;;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 \/* cr\u00e9ation de l&rsquo;anagramme *\/<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 strfry (chaine);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 fprintf (stdout, \u00ab\u00a0%sn\u00a0\u00bb, chaine);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 fflush (stdout);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 FREE (chaine);<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 while (1);<\/p>\n<p>\u00a0 \/* Jamais atteint *\/<br \/>\u00a0 return EXIT_SUCCESS;<br \/>}<\/p>\n<p>char *<br \/>read_from_file (FILE * f, size_t max_size)<br \/>{<br \/>\u00a0 char temp[max_size];<br \/>\u00a0 if (fgets (temp, max_size, f) == NULL)<br \/>\u00a0\u00a0\u00a0 \/* fgets echoue. On sort par le gestionnaire de signaux *\/<br \/>\u00a0\u00a0\u00a0 raise (SIGTERM);<br \/>\u00a0 return strdup (temp);<br \/>}<\/p>\n<p>static void<br \/>gestionnaire_signaux (int ignore)<br \/>{<br \/>\u00a0 printf (\u00ab\u00a0nbye byen\u00a0\u00bb);<br \/>\u00a0 FREE (chaine);<br \/>\u00a0 exit (EXIT_SUCCESS);<br \/>\u00a0 \/* s&rsquo;affranchir du warning \u00ab\u00a0unused parameter \u2018ignore\u2019\u00a0\u00bb *\/<br \/>\u00a0 (void) ignore;<br \/>}{\/codecitation}<\/p>\n<p class=\"caption\">\u00a0On peut compiler facilement le programme avec la commande suivante :\u00a0 <strong>gcc -Wall -Wextra -o anagramme serveur_anagramme.c<\/strong><\/p>\n<p class=\"caption\"><em>Testons le :\u00a0\u00a0<\/em><\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-39\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image1.png\" border=\"0\" alt=\"screenshot\" width=\"397\" height=\"162\" srcset=\"https:\/\/nunix.fr\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image1.png 657w, https:\/\/nunix.fr\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image1-300x122.png 300w\" sizes=\"auto, (max-width: 397px) 85vw, 397px\" \/> <\/p>\n<p class=\"caption\">Voila, on a un programme simple&#8230; Transformons le maintenant en serveur TCP\/IP sur le port 1234 :<\/p>\n<p class=\"caption\">\u00a0<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-40\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image2.png\" border=\"0\" alt=\"screenshot\" width=\"310\" height=\"359\" srcset=\"https:\/\/nunix.fr\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image2.png 657w, https:\/\/nunix.fr\/wp-content\/uploads\/2009\/05\/Linux-shell-serveur_netcat-image2-259x300.png 259w\" sizes=\"auto, (max-width: 310px) 85vw, 310px\" \/><\/p>\n<p class=\"caption\">Le shell du haut lance le serveur, tandis que les deux du dessous sont des clients : on peut voir les requ\u00eates qu&rsquo;ils \u00e9mettent et les anagrammes qu&rsquo;ils re\u00e7oivent en r\u00e9ponse. <\/p>\n<p class=\"caption\">On passe en param\u00e8tre du script serveur.sh la commande \u00e0 ex\u00e9cuter lors de la connection d&rsquo;un client (notre programme d&rsquo;anagramme donc) ainsi que le port sur lequel le serveur doit s&rsquo;\u00e9tablir (1234 dans notre exemple). Le nombre de clients est illimit\u00e9. <\/p>\n<p class=\"caption\">Voici le script serveur.sh :{codecitation class=\u00a0\u00bbbrush: bash;\u00a0\u00bb}<\/p>\n<p class=\"caption\">#!\/bin\/sh<\/p>\n<p>#&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-fonctions&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<\/p>\n<p>#r\u00e9cup\u00e9rer une liste de processus, selon un motif de recherche<br \/>#$1 le motif de recherche dans les processus<br \/>get_pid_list () {<br \/>\u00a0\u00a0\u00a0 LISTE_PID=`ps aux | grep \u00ab\u00a0$1\u00a0\u00bb\u00a0 | grep -v \u00ab\u00a0grep\u00a0\u00bb | awk &lsquo;{print $2}&rsquo;`<br \/>\u00a0\u00a0\u00a0 echo $LISTE_PID<br \/>}<\/p>\n<p>#r\u00e9cup\u00e9rer le nb de mots d&rsquo;une liste<br \/>#$1 la variable contenant la liste<br \/>get_nb_word () {<br \/>\u00a0\u00a0\u00a0 NB_WORD=`echo \u00ab\u00a0$1\u00a0\u00bb | wc -w`<br \/>\u00a0\u00a0\u00a0 echo $NB_WORD<br \/>}<\/p>\n<p>#r\u00e9cup\u00e9rer un mot d&rsquo;une liste<br \/>#$1 la variable contenant la liste<br \/>#$2 le numero du mot \u00e0 r\u00e9cup\u00e9rer<br \/>get_word () {<br \/>\u00a0\u00a0\u00a0 WORD=`echo \u00ab\u00a0$1\u00a0\u00bb | awk -v numMot=$2 &lsquo;{print $numMot;}&rsquo;`<br \/>\u00a0\u00a0\u00a0 echo $WORD<br \/>}<\/p>\n<p>#r\u00e9cup\u00e9rer le dernier processus d&rsquo;une liste de processus trouv\u00e9e selon un motif de recherche<br \/>#$1 le motif de recherche des processus<br \/>get_last_pid () {<br \/>\u00a0\u00a0\u00a0 LISTE_PID=`get_pid_list \u00ab\u00a0$1&Prime;`<br \/>\u00a0\u00a0\u00a0 NB_WORD=`get_nb_word \u00ab\u00a0$LISTE_PID\u00a0\u00bb`<br \/>\u00a0\u00a0\u00a0 WORD=`get_word \u00ab\u00a0$LISTE_PID\u00a0\u00bb \u00ab\u00a0$NB_WORD\u00a0\u00bb`<br \/>\u00a0\u00a0\u00a0 echo $WORD<br \/>}<\/p>\n<p>#&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-code serveur&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<\/p>\n<p>#verification des arguments<br \/>if [ $# != 2 ]<br \/>then<br \/>\u00a0\u00a0\u00a0 echo \u00ab\u00a0Usage : $0 commande listening_portn\u00a0\u00bb<br \/>\u00a0\u00a0\u00a0 echo \u00a0\u00bb\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 commande : commande \u00e0 \u00e9xecuter lors de la connexion d&rsquo;un client. C&rsquo;est cette commande qui va dialoguer avec le client.\u00a0\u00bb<br \/>\u00a0\u00a0\u00a0 echo \u00a0\u00bb\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 listening_port : port sur lequel le serveur attend des clients\u00a0\u00bb<br \/>\u00a0\u00a0\u00a0 exit<br \/>else<br \/>\u00a0\u00a0\u00a0 echo \u00ab\u00a0commande : \u00ab\u00a0$1&Prime;\u00a0\u00bb<br \/>\u00a0\u00a0\u00a0 echo \u00ab\u00a0listening_port\u00a0 : $2\u00a0\u00bb<br \/>\u00a0\u00a0\u00a0 <br \/>fi<\/p>\n<p>COMMANDE=\u00a0\u00bb$1&Prime;<\/p>\n<p>#lancement du premier serveur :<br \/>(nc -l -p $2 -c \u00ab\u00a0$COMMANDE\u00a0\u00bb &#038;&gt; \/dev\/null ) &#038;<br \/>PID_LAST_PROCESS=`get_last_pid \u00ab\u00a0nc -l -p $2 \u00ab\u00a0`<\/p>\n<p>while true<br \/>do<br \/>\u00a0\u00a0\u00a0 #on \u00e9tablit la liste des processus serveur <br \/>\u00a0\u00a0\u00a0 LIST_PROCESS=`get_pid_list \u00ab\u00a0sh -c $COMMANDE\u00a0\u00bb`<br \/>\u00a0\u00a0\u00a0 for i in $LIST_PROCESS<br \/>\u00a0\u00a0\u00a0 do<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 CURRENT_PROCESS=$i\u00a0 <br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ##d\u00e9tecter la connexion d&rsquo;un client et lancer un nouveau serveur<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if [ \u00ab\u00a0$CURRENT_PROCESS\u00a0\u00bb -eq \u00ab\u00a0$PID_LAST_PROCESS\u00a0\u00bb ]<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 then<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 (nc -l -p $2 -c \u00ab\u00a0$COMMANDE\u00a0\u00bb &#038;&gt; \/dev\/null ) &#038;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PID_LAST_PROCESS=`get_last_pid \u00ab\u00a0nc -l -p $2 \u00ab\u00a0`<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 fi<br \/>\u00a0\u00a0\u00a0 done<br \/>\u00a0\u00a0\u00a0 sleep 0.2<br \/>done <\/p>\n<p class=\"caption\">\u00a0{\/codecitation}<\/p>\n<p class=\"caption\">Le principe du script est le suivant : puisque netcat accepte un seul client \u00e0 la fois, on d\u00e9tecte la connection d&rsquo;un client et on lance un autre serveur netcat afin qu&rsquo;un nouveau client puisse se connecter (Et on fait cela en boucle). <\/p>\n<p class=\"caption\">Pour d\u00e9tecter la connexion d&rsquo;un client, le script se base sur le fait que le processus serveur\u00a0 (s&rsquo;appelant donc nc -l -p <em>port<\/em> -c \u00ab\u00a0<em>commande<\/em>\u00ab\u00a0)\u00a0 change de nom (pour devenir \/bin\/sh -c <em>commande) <\/em>lorsque le client se connecte.<\/p>\n<p class=\"caption\">&nbsp;<\/p>\n<p class=\"caption\">Ce script a des avantages et des inconv\u00e9nients : <img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-38\" src=\"http:\/\/46.105.150.90\/tstnunix\/wp-content\/uploads\/2009\/05\/smiley-undecided.gif\" border=\"0\" alt=\"Undecided\" title=\"Undecided\" width=\"18\" height=\"18\" \/><\/p>\n<p class=\"caption\">++ permet en 10 secondes de cr\u00e9er un serveur TCP\/IP multi-clients.<\/p>\n<p class=\"caption\">++ permet de transformer en serveur n&rsquo;importe quel programme, m\u00eame s&rsquo;il n&rsquo;est pas con\u00e7u pour. <\/p>\n<p class=\"caption\">&#8211; Cr\u00e9\u00e9 un processus serveur par client.<\/p>\n<p class=\"caption\">&#8211; Pas de connexions simultan\u00e9es (elles doivent \u00eatre espac\u00e9es d&rsquo;au moins 1\/5iem de seconde \u00e0 cause du sleep 0.2 pour ne pas utiliser le processeur \u00e0 100%).<\/p>\n<p class=\"caption\"><strong>En r\u00e9sum\u00e9 ce script est utile pour cr\u00e9er rapidement des petits serveurs, qui ne sont pas destin\u00e9s \u00e0 recevoir trop de clients.<\/strong><\/p>\n<p>Nous verrons dans un prochain article comment r\u00e9aliser un serveur similaire en C : un serveur TCP\/IP multi-client qui redirige les entr\u00e9es\/sorties d&rsquo;un programme vers la socket&#8230;.. <a href=\"http:\/\/46.105.150.90\/tstnunix\/?p=59\">[C&rsquo;est i\u00e7i !]<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans cet article nous allons d\u00e9velopper un script shell permettant de r\u00e9aliser un serveur TCP\/IP multi-clients \u00e0 l&rsquo;aide de NetCat . Pour partir sur de bonnes bases, je vous recommande donc la lecture de mon article, sur &#8230;.\u00a0 NetCat&#8230; (pfheu, le m&rsquo;ssieur &lsquo;y fait dla pub&rsquo; pour ses articles&#8230;).<\/p>\n","protected":false},"author":1,"featured_media":12,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-41","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-shell"],"_links":{"self":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/41","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=41"}],"version-history":[{"count":1,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/41\/revisions"}],"predecessor-version":[{"id":120,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/41\/revisions\/120"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/media\/12"}],"wp:attachment":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=41"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=41"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=41"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}