Changes between Initial Version and Version 1 of SujetTP4-2017


Ignore:
Timestamp:
Feb 16, 2017, 5:03:13 PM (8 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • SujetTP4-2017

    v1 v1  
     1= TP4 : Serveur WEB minimaliste pour la commande des LEDs et la lecture du bouton poussoir =
     2
     3== Objectif ==
     4
     5Le but de ce TME est de créer un site web consultable par un browser web permettant de changer l'état des leds. Le serveur web est écrit en Python, à la réception des requêtes du client, il exécute des scripts CGI (Common Gateway Interface) écrit également en Python pour produire des pages HTML dynamiques. Les scripts CGI devront communiquer avec le programme écrit en C contrôlant les LEDs. La communication entre les scripts et le programme de contrôle se fera par fifo.
     6
     7Pour ce faire, nous allons procéder en deux temps.
     81. Nous allons faire communiquer un programme python avec un programme C par FIFO.
     92. Nous allons créer un serveur local sur le PC de développement et le faire communiquer avec le programme C.
     103. Nous allons mettre le serveur sur une raspberry PI et communiquer avec le programme C
     114. Nous allons remplacer le programme C par le programme de contrôle des LEDs.
     12
     13== 1. Communication par FIFO ==
     14
     15Pour démarrer, vous allez récupérer une [attachment:writer_reader.tgz archive] constituer de 4 fichiers: 2 lecteurs et 2 écrivains.
     16Les deux lecteurs sont interchangeables, le premier est en C, le second en Python.
     17Les deux écrivains sont aussi interchangeables.
     18{{{
     19writer_reader
     20├── Makefile
     21├── reader.c    : lit une fifo et affiche le message reçu jusqu'à recevoir le message end
     22├── reader.py
     23├── writer.c    : écrit dans une fifo 5 fois et écrit le message end
     24└── writer.py
     25}}}
     26
     27Vous pouvez tester les programmes qui vous sont proposés.
     28Je vous demande de lire les codes en commençant par les programmes python, en répondant aux questions
     29
     30**writer.py**
     31- Dans quel répertoire est créee la fifo ?
     32- Quelle différence mkfifo et open ?
     33- Pourquoi tester que la fifo existe ?
     34- A quoi sert flush ?
     35- Pourquoi ne ferme-t-on pas la fifo ?
     36
     37{{{#!python
     38#!/usr/bin/env python
     39import os, time
     40
     41pipe_name = '/tmp/myfifo'
     42
     43if not os.path.exists(pipe_name):
     44    os.mkfifo(pipe_name)
     45
     46pipe_out = open(pipe_name,'w')
     47
     48i=0;
     49while i < 5:
     50    pipe_out.write("hello %d fois from python\n" % (i+1,))
     51    pipe_out.flush()
     52    time.sleep(1)
     53    i=i+1
     54
     55pipe_out.write("end\n")
     56}}}
     57
     58**reader.py**
     59- Que fait readline ?
     60
     61{{{#!python
     62#!/usr/bin/env python
     63import os, time
     64
     65pipe_name = '/tmp/myfifo'
     66
     67if not os.path.exists(pipe_name):
     68    os.mkfifo(pipe_name)
     69
     70pipe_in = open(pipe_name,'r')
     71while str != "end\n" :
     72    str = pipe_in.readline()
     73    print '%s' % str,
     74
     75}}}
     76Vous allez remarquer que lorsque le vous lancer un écrivain (en C ou en Pyhton) rien ne se passe tant que vous n'avez pas lancé un lecteur.
     77
     78- Expliquez le phénomème.
     79
     80== 2. Création d'un serveur fake ==
     81
     82Le but de cette première partie est de réaliser le programme suivant:
     83
     84[[Image(htdocs:png/fake2server.png,nolink,400px)]]
     85
     86- fake lit une valeur sur stdin et place la valeur lue dans une variable.
     87- Lorsque l'on tape plusieurs valeurs de suite la nouvelle valeur écrase l'ancienne.
     88- fake est toujours en fonctionnement.
     89- fake attends aussi un message de la fifo s2f.
     90- lorsqu'il reçoit un message, il l'affiche et il renvoie dans la fifo f2s la dernière valeur lue sur stdin.
     91
     92Vous commencez par récuperer l'[attachment:fake.tgz archive] qui donne un point de dépoart.
     93{{{
     94fake
     95├── Makefile
     96├── server.py
     97└── fake.c
     98}}}
     99
     100- Dans un premier terminal, compilez et démarrez fake.
     101- Dans un autre terminal, exécuter ./server.py
     102  - Le programme "server" Python est lancé et arrêté, il se comporte comme se comportera le script CGI.
     103- Quand le server python démarre,
     104  - il envoie un message sur la fifo s2f
     105  - puis il lit la fifo s2f et affiche le résultat.
     106
     107Vous devez :
     1081. modifier le select dans fake pour lire les deux fifos d'entrée stdin et s2f.
     1092. modifier server.py pour lire la valeur lue sur stdin.
     110
     111**fake.c**
     112{{{#!c
     113#include <fcntl.h>
     114#include <stdio.h>
     115#include <stdlib.h>
     116#include <sys/time.h>
     117#include <sys/stat.h>
     118#include <unistd.h>
     119#include <string.h>
     120
     121#define MAXServerResquest 1024
     122
     123int main()
     124{
     125    int     f2s, s2f;                                       // fifo file descriptors
     126    char    *f2sName = "/tmp/f2s_fw";                       // filo names
     127    char    *s2fName = "/tmp/s2f_fw";                       //
     128    char    serverRequest[MAXServerResquest];               // buffer for the request
     129    fd_set  rfds;                                           // flag for select
     130    struct  timeval tv;                                     // timeout
     131    tv.tv_sec = 1;                                          // 1 second
     132    tv.tv_usec = 0;                                         //
     133
     134    mkfifo(s2fName, 0666);                                  // fifo creation
     135    mkfifo(f2sName, 0666);
     136
     137    /* open both fifos */
     138    s2f = open(s2fName, O_RDWR);                            // fifo openning
     139    f2s = open(f2sName, O_RDWR);
     140
     141    do {
     142        FD_ZERO(&rfds);                                     // erase all flags
     143        FD_SET(s2f, &rfds);                                 // wait for s2f
     144
     145        if (select(s2f+1, &rfds, NULL, NULL, &tv) != 0) {   // wait until timeout
     146            if (FD_ISSET(s2f, &rfds)) {                     // something to read
     147                int nbchar;
     148                if ((nbchar = read(s2f, serverRequest, MAXServerResquest)) == 0) break;
     149                serverRequest[nbchar]=0;
     150                fprintf(stderr,"%s", serverRequest);
     151                write(f2s, serverRequest, nbchar);
     152            }
     153        }
     154    }
     155    while (1);
     156
     157    close(f2s);
     158    close(s2f);
     159
     160    return 0;
     161}
     162}}}
     163
     164**server.py**
     165{{{#!python
     166#!/usr/bin/env python
     167import os, time
     168
     169s2fName = '/tmp/s2f_fw'
     170if not os.path.exists(s2fName):
     171   os.mkfifo(s2fName)
     172s2f = open(s2fName,'w+')
     173
     174f2sName = '/tmp/f2s_fw'
     175if not os.path.exists(f2sName):
     176   os.mkfifo(f2sName)
     177f2s = open(f2sName,'r')
     178
     179s2f.write("w hello\n")
     180s2f.flush()
     181str = f2s.readline()
     182print '%s' % str,
     183
     184f2s.close()
     185s2f.close()
     186}}}
     187== 3. Création d'un serveur web ==
     188
     189Nous allons maintenant créer le vrai server http.
     190Dans l'[attachment:server-fake.tgz archive] vous trouvez un squelette de server que vous allez d'abord tester avant de l'exécuter sur la carte !RaspberryPi pour la commande des leds.
     191{{{
     192server-fake
     193├── fake
     194│   ├── Makefile
     195│   └── fake.c
     196└── server
     197    ├── server.py
     198    └── www
     199        ├── cgi-bin
     200        │   ├── led.py
     201        │   └── main.py
     202        ├── img
     203        │   └── peri.png
     204        └── index.html
     205}}}
     206
     207**Pour tester le server http**
     208- Dans un premier terminal, après l'avoir compiler, lancez le programme fake. Il s'agit du même programme fake.c que précédement, qui reçoit une requête depuis une fifo s2f et qui renvoi une réponse.
     209  {{{
     210  cd fake
     211  make
     212  ./fake
     213  }}}
     214- Dans un second terminal, lancez le server.py. C'est un server http en python.
     215  {{{
     216  cd server/www
     217  ../server.py
     218  }}}
     219- Sur votre navigateur préféré, visualisez la page index.html à l'adresse 127.0.0.1:8000
     220  - Vous devez voir apparaitre un logo et une case avec un bouton enter.
     221  - La page `index.html` contient deux "frames":
     222    - Le premier avec le logo.
     223    - Le second est contient la case et le bouton. Le code html de cette case est obtenu par l'exécution du programme Python `cgi-bin/main.py`.
     224      - Notez qu'il n'est pas très utile d'avoir produit cette page par un programme python car la page n'est pas dynamique (son code est toujours le même) mais c'est pour donner la possibilité de la rendre dynamique.
     225  - Lorsque vous écrivez quelque-chose dans la case, la page index.html demande l'exécution de script `cgi-bin/led.py`
     226  - le script `led.py` envoi le contenu de la case sur la fifo `s2f` attendue par `fake` et produit une page presque indentique à main.py avec deux différences.
     227    - Elle affiche ce qui a été reçu de la fifo `f2s`
     228    - Elle est remplacée au bout d'une seconde par la page `main.py` grace à une commande `<META>`
     229
     230**server.py**
     231Le server affiche la page index.htlm du présente dans le répetoire de lanceeltécoute le port 8000,
     232{{{#!python
     233#!/usr/bin/env python
     234import BaseHTTPServer
     235import CGIHTTPServer
     236import cgitb; cgitb.enable()
     237
     238server = BaseHTTPServer.HTTPServer
     239handler = CGIHTTPServer.CGIHTTPRequestHandler
     240server_address = ("", 8000)
     241handler.cgi_directories = ["/cgi-bin"]
     242
     243httpd = server(server_address, handler)
     244httpd.serve_forever()
     245}}}
     246
     247**index.html**
     248{{{
     249<html>
     250 <head><title>Peri Web Server</title></head>
     251 <frameset rows="100,*" frameborder=0>
     252  <frame src="img/peri.png">
     253  <frame src="cgi-bin/main.py">
     254 </frameset>
     255</html>
     256}}}
     257
     258**main.py**
     259{{{#!python
     260#!/usr/bin/env python
     261
     262html="""
     263<head>
     264  <title>Peri Web Server</title>
     265</head>
     266<body>
     267LEDS:<br/>
     268<form method="POST" action="led.py">
     269  <input name="val" cols="20"></input>
     270  <input type="submit" value="Entrer">
     271</form>
     272</body>
     273"""
     274
     275print html
     276}}}
     277
     278**led.py**
     279{{{#!python
     280#!/usr/bin/env python
     281import cgi, os, time,sys
     282form = cgi.FieldStorage()
     283val = form.getvalue('val')
     284
     285s2fName = '/tmp/s2f_fw'
     286f2sName = '/tmp/f2s_fw'
     287s2f = open(s2fName,'w+')
     288f2s = open(f2sName,'r',0)
     289
     290s2f.write("w %s\n" % val)
     291s2f.flush()
     292res = f2s.readline()
     293f2s.close()
     294s2f.close()
     295
     296html="""
     297<head>
     298  <title>Peri Web Server</title>
     299  <META HTTP-EQUIV="Refresh" CONTENT="1; URL=/cgi-bin/main.py">
     300</head>
     301<body>
     302LEDS:<br/>
     303<form method="POST" action="led.py">
     304  <input name="val" cols="20"></input>
     305  <input type="submit" value="Entrer">
     306  set %s
     307</form>
     308</body>
     309""" % (val,)
     310
     311print html
     312}}}
     313
     314**Pour l'exécution sur la carte !RaspberryPi**
     315- Vous devez copier tout `server-fake` sur la carte de votre choix. Vous ne pouvez pas être plus de deux binômes par carte.
     316- Vous lancez fake et le server http comme précédement.
     317- Sur votre navigateur, vous devez mettre une exception au proxy pour l'adresse du routeurs des !RaspberryPi `132.227.102.36`.
     318- Si vous êtes sur la carte **20**, vous mettez comme URL 132.227.102.36:8**020**. Vous faites de manière semblable pour les autres cartes.
     319- Si vous êtes le second binôme sur la carte *20*, l'URL est 132.227.102.36.8**120**, et dans le script `server.py` vous devez écouter le port **8001**.
     320- Le routeur des !RaspberryPi a été programmé pour renvoyer les requêtes http reçues sur le port 8xyy avec x=(0 ou 1) et yy=(20,21,...,25) sur le carte yy et le port 800x.
     321
     322**Travail demandé**
     323
     324- Modifier fake.c et led.py pour commander les leds.