Nous travaillons sur le programme
prog02_ws_perf.cpp.
La solution employée pour les échanges applicatifs de la réalisation
précédente est contraignante pour plusieurs raisons :
- le fait que le protocole HTTP soit sans état nous oblige à recourir à des
astuces de programmation (mémoriser et rechercher un état en fonction
d'un identifiant) pour donner l'illusion d'une séquence d'étapes (début,
milieu, fin), ce qui complique l'écriture et l'exécution du code,
- le fait que chaque nouvel échange applicatif implique la rédaction et
l'analyse textuelle d'une paire d'en-têtes HTTP ajoute un surcoût de
traitement non négligeable devant le traitement des données applicatives.
Le second programme proposé ici vise à réaliser une application similaire
à la précédente mais en utilisant le protocole
websocket afin de
contourner les inconvénients qui viennent d'être évoqués.
Il s'agit dans un premier temps de compléter le programme
prog02_ws_perf.cpp en reprenant (par copier/coller puis adaptation)
les éléments principaux du programme précédent :
- la mise en place du serveur TCP dans la fonction main(),
- l'analyse de l'en-tête de la requête HTTP au début de la fonction
dialogThread() (attention, les options à prendre en considération
sont différentes),
- la fourniture du fichier HTML vers la fin de la fonction dialogThread()
(attention il s'agit du fichier "ws_perf.html" cette fois-ci),
- le signalement d'une erreur à la fin de la fonction dialogThread().
En l'état, vous devriez constater que lorsque le navigateur sollicite votre
serveur avec l'URI
http://localhost:9876/ (en supposant que votre
serveur écoute sur le port 9876), il affiche bien la page HTML attendue
(ou votre message d'erreur si vous demandez une autre URI).
Toutefois, l'application en
javascript ne parvient pas encore à obtenir
une
websocket puisque votre serveur ne sait pas encore les gérer.
Étudiez alors la fonction d'initialisation
window.onload du fichier
ws_perf.html afin de constater la mise en place du moyen de
communication (
WebSocket) et son usage (émission/réception).
Pour que cette application fonctionne, il faut à nouveau compléter la fonction
dialogThread() de façon à ce qu'elle exploite le protocole
websocket.
Ceci suit le déroulement suivant :
- Le serveur confirme au client que sa demande de basculement vers le
protocole websocket aura bien l'effet attendu,
- Le client envoie de manière répétée un message websocket binaire
contenant un bloc de données que le serveur doit récupérer et traiter.
Le client attend en retour ce même bloc de données dont les valeurs
auront été modifiées.
Le nombre de tels blocs à échanger successivement par de nouveaux messages
websocket similaires est à la discrétion du client ; lorsqu'il
n'est plus possible d'en extraire, c'est que l'application est finie.
Remarquez que, cette fois-ci, cette succession de messages a lieu sur la
même connexion ; de simples variables locales suffisent à représenter
l'état courant de l'application.
Une nouvelle sollicitation de votre serveur par le navigateur doit maintenant
conduire à une application fonctionnelle qui exploite une
websocket
pour échanger des blocs de données avec le serveur.
Veillez à ne pas trop produire d'affichages superflus dans la console car
le grand nombre d'échanges risque de rendre illisibles les quelques
affichages utiles.
En particulier, un message doit vous rappeler que le code a été compilé pour
la mise au point et que dans ce cas les mesures de performances et d'énergie
ne sont pas significatives.
Si nous nous débarrassons des affichages superflus et que nous reconstruisons
notre programme exécutable en demandant au compilateur d'optimiser le code
généré
$ make rebuild opt=1 ↵
nous devrions voir le serveur afficher des mesures plus significatives.
Pour cela, il faut se connecter par
ssh à une machine voisine afin
d'y exécuter le serveur, par exemple :
[poivre10] $ ./prog02_ws_perf 9876 ↵
host 'poivre10.enib.fr' waiting for connections on port '9876'...
new connection from 192.168.73.211:54198
--> sending file: ws_perf.html
new connection from 192.168.73.211:54204
--> upgrading to websocket
2x4096 blocks in 8.38273 s (2x488.623 block/s, 45.644 Joules)
client disconnected
Ce serveur distant peut alors être sollicité par le navigateur de notre
machine locale
[poivre11] $ firefox http://poivre10:9876/ ↵
Nous constatons sur ces nouvelles mesures, qu'au delà de la simplification
du fonctionnement du serveur, nous obtenons des gains de performance et
d'énergie grâce au protocole
websocket.
Remarquez toutefois que, pour les raisons déjà évoquées à la fin de
cette section, le gain constaté
dans les salles de Labo de l'ENIB est bien moindre que ce que nous
pourrions observer si les machines étaient interconnectées par des
switches plus rapides.
En effet, une nouvelle fois nos programmes passent beaucoup de temps à
attendre que les données circulent.
N'oublions pas également que les réalisations envisagées ici sont à la base
très favorables pour les performances car elles reposent dans les deux cas
sur des échanges de données binaires.
Comme nous l'avons déjà étudié dans
ce sujet, l'échange de données binaires
est bien plus efficace que si nous nous étions reposé sur des échanges
textuels (que ce soit avec HTTP ou avec
websocket).
Cela mérite d'être rappelé car il est très courant, dans les applications
reposant sur HTTP, de s'appuyer sur des formats de données textuels
(XML, JSON...) faciles à exploiter grâce à des API déjà fournies mais
qui imposent au matériel un surcoût considérable en temps et en énergie.