![]() |
![]() |
TP WebSockets / Rabat 2014-2015De $1IntroductionDans ce TP nous allons étudier les WebSockets faisant partie de ce qu'on appelle WebRTC (Real Time Communication), c'est "à côté de HTML5" et c'est un nouveau protocole (et une API JavaScript que doivent implémenter les navigateurs) synchrone, à faible latence, bi-directionnel. Pour faire des WebSockets il faut un serveur supportant ce protocole. Aujourd'hui presque tous les serveurs (apache, glassfish, tomcat, jetty, etc.) supportent ce protocole nativement ou à l'aide d'extensions. Comme l'application que nous développons est un jeu multi-participants, nous allons utiliser un micro serveur très à la mode en ce moment: NodeJS. Ce serveur intègre l'interpréteur JavaScript V8 de Google et permet de coder la couche applicative côté serveur en JavaScript. On pourra ainsi échanger entre le serveur et les clients des objets JavaScript, sans devoir systématiquement les transformer dans un autre langage. Nous commencerons par étudier deux applications de chat synchrones basés sur la librairie socket.io très populaire dans le monde des WebSockets (il existe des clients pour Java, C#, etc. il est par exemple possible d'écrire une application Android qui parle avec un serveur nodeJS via des WebSockets). Livre gratuit sur NodeJS, en français
Installation de NodeJS et des modules express et socket.ioQuel que soit votre système d'exploitation, allez sur le site http://www.nodejs.org et suivez la procédure d'installation (click sur le bouton "install"). Installez la dernière version. Une fois nodeJS installé, vérifiez qu'il fonctionne en tapant dans une fenêtre dos/shell la commande "node --version", cela devrait afficher quelque chose comme: "node 0.10.x". Ok, tout va bien. Test d'une première application NodeJS
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/'); Sauvez le et exécutez la commande suivante : node test.js
Ouvrez cet URL dans votre navigateur ! Vous devriez avoir : Vous venez d'exécuter votre première application NodeJS ;-) Le fichier test.js contient le code pour un serveur web archi minimal qui n'est même pas capable de lire un fichier html. Il a juste renvoyé des Strings en parlant de protocole HTTP. Installation de modules complémentairesNodeJS est extensible grace à un système de "modules", sortes de "packages" à la linux. Lors de l'installation, si vous avez mis le répertoire d'installation dans le PATH, vous devriez avoir accès à la commande "npm" ou Node Package Manager, qui permet d'installer (il faut avoir une connexion qui fonctionne, évidemment...) des modules supplémentaires. Il en existe des centaines aujourd'hui, mais nous allons utiliser deux des plus populaires:
Travail à faire :
ATTENTION SI PROXY NECESSAIRE (parfois le cas en entreprise) : npm config set proxy http://login:password@internetpsa.inetpsa.com:80 et aussi npm config set https-proxy http://login:password@internetpsa.inetpsa.com:80 SI ERREUR "nodejs/windows Error: ENOENT, stat 'C:\Users\RT\AppData\Roaming\npm' sous windows, comme dans l'image ci-dessous, il faut créer à la main le répertoire manquant et relancer npm install. Dans votre répertoire vous devriez avoir un répertoire supplémentaires intitulé "node_modules" qui contient les modules que vous venez d'ajouter à votre projet. En général on ajoute les modules par projet, non pas globalement. Si vous développez une autre application, vous pourrez simplement copier ce répertoire si vous ne voulez pas re-télécharger les modules express et socket.io, vous pourrez éventuellement en ajouter. Test d'une application de chat "simple", utilisant express et socket.ioNous allons étudier un petit programme de chat mélangé avec un petit programme utilisant un canvas, un squelette d'un futur jeu multi-participants. Récupérez donc cette archive : chatWithSocketIO_withoutjQuery.zip Désarchivez là et faites en sorte pour qu'on ait dans ce répertoire le répertoire node_modules qui contient socket.io et express que vous avez créé juste avant en installant les modules. Vous devez avoir ceci dans le répertoire à la fin: Bon, maintenant, executez la commande "node simpleChatServer.js" et ouvrez dans votre navigateur "http://localhost:8082", si votre port 8082 est occupé, changez le port dans le fichier simpleChatServer.js. Ouvrez cet URL dans deux tabs, et regardez le résultat. Le chat devrait fonctionner. Bougez la souris dans le canvas, les dessins devraient être synchronisés, ouvrez d'autres onglets, fermez-en, cela devrait fonctionner... Ouvrez la console de debug de Chrome (y'a que Chrome qui permet de debugger efficacement les WS aujourd'hui, tab "network" et tout en bas "websocket", puis tab "frames"), chattez et regardez les trames échangées.... Maintenant, étudiez le code de simpleChatServer.js, de simpleChat.html, de jeu.js Principes de base de socket.io (on trouve de nombreux tutoriaux en français et en anglais sur le web, nous résumons ici l'essentiel):
simpleChatServer.js: // We need to use the express framework: have a real web servler that knows how to send mime types etc. var express=require('express'); // Init globals variables for each module required var app = express() , http = require('http') , server = http.createServer(app) , io = require('socket.io').listen(server); // launch the http server on given port server.listen(8082); // Indicate where static files are located. Without this, no external js file, no css... app.use(express.static(__dirname + '/')); // routing app.get('/', function (req, res) { res.sendfile(__dirname + '/simpleChat.html'); }); // usernames which are currently connected to the chat var usernames = {}; var listOfPlayers = {}; io.sockets.on('connection', function (socket) { // when the client emits 'sendchat', this listens and executes socket.on('sendchat', function (data) { // we tell the client to execute 'updatechat' with 2 parameters io.sockets.emit('updatechat', socket.username, data); }); // when the client emits 'sendchat', this listens and executes socket.on('sendpos', function (newPos) { // we tell the client to execute 'updatepos' with 2 parameters //console.log("recu sendPos"); socket.broadcast.emit('updatepos', socket.username, newPos); }); // when the client emits 'adduser', this listens and executes socket.on('adduser', function(username){ // we store the username in the socket session for this client // the 'socket' variable is unique for each client connected, // so we can use it as a sort of HTTP session socket.username = username; // add the client's username to the global list // similar to usernames.michel = 'michel', usernames.toto = 'toto' usernames[username] = username; // echo to the current client that he is connecter socket.emit('updatechat', 'SERVER', 'you have connected'); // echo to all client except current, that a new person has connected socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected'); // tell all clients to update the list of users on the GUI io.sockets.emit('updateusers', usernames); // Create a new player and store his position too... for that // we have an object that is a "list of players" in that form // listOfPlayer = {'michel':{'x':0, 'y':0, 'v':0}, // john:{'x':10, 'y':10, 'v':0}} // for this example we have x, y and v for speed... ? var player = {'x':0, 'y':0, 'v':0} listOfPlayers[username] = player; io.sockets.emit('updatePlayers',listOfPlayers); }); // when the user disconnects.. perform this socket.on('disconnect', function(){ // remove the username from global usernames list delete usernames[socket.username]; // update list of users in chat, client-side io.sockets.emit('updateusers', usernames); // Remove the player too delete listOfPlayers[socket.username]; io.sockets.emit('updatePlayers',listOfPlayers); // echo globally that this client has left socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected'); }); }); Et la page simpleChat.html <style> #myCanvas { border: 1px solid red; background-color:pink; } </style> <script src="jeu.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var username = prompt("What's your name?"); var conversation, data, datasend, users; var socket = io.connect(); // on connection to server, ask for user's name with an anonymous callback socket.on('connect', function(){ // call the server-side function 'adduser' and send one parameter (value of prompt) socket.emit('adduser', username); }); // listener, whenever the server emits 'updatechat', this updates the chat body socket.on('updatechat', function (username, data) { var chatMessage = "<b>" + username + ":</b> " + data + "<br>"; conversation.innerHTML += chatMessage; }); // just one player moved socket.on('updatepos', function (username, newPos) { updatePlayerNewPos(newPos); }); // listener, whenever the server emits 'updateusers', this updates the username list socket.on('updateusers', function(listOfUsers) { users.innerHTML = ""; for(var name in listOfUsers) { var userLineOfHTML = '<div>' + name + '</div>'; users.innerHTML += userLineOfHTML; } }); // update the whole list of players, useful when a player // connects or disconnects, we must update the whole list socket.on('updatePlayers', function(listOfplayers) { updatePlayers(listOfplayers); }); // on load of page window.addEventListener("load", function(){ // get handles on various GUI components conversation = document.querySelector("#conversation"); data = document.querySelector("#data"); datasend = document.querySelector("#datasend"); users = document.querySelector("#users"); // Listener for send button datasend.addEventListener("click", function(evt) { sendMessage(); }); // detect if enter key pressed in the input field data.addEventListener("keypress", function(evt) { // if pressed ENTER, then send if(evt.keyCode == 13) { this.blur(); sendMessage(); } }); // sends the chat message to the server function sendMessage() { var message = data.value; data.value = ""; // tell server to execute 'sendchat' and send along one parameter socket.emit('sendchat', message); } }); </script> <body onload="init();"> <p> <canvas width = 300 height = 300 id="myCanvas"></canvas> <p> <div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;"> <b>USERS</b> <div id="users"></div> </div> <div style="float:left;width:300px;height:250px;overflow:scroll-y;padding:10px;"> <div id="conversation"></div> <input id="data" style="width:200px;" /> <input type="button" id="datasend" value="send" /> </div> </body> |
Powered by MindTouch Deki Open Source Edition v.8.08 |