
Node.js to rewelacja sama w sobie. Dodajmy jeszcze do tego socket.io, a otrzymamy możliwości o których dawniej nawet nam się nie śniło.
Node.js i socket.io
Po omówieniu podstaw node.js oraz pracy z bazami danych z poziomu tej technologii, pora sięgnąć po jeszcze ciekawsze rzeczy.
Jedną z nich z pewnością jest socket.io. Wystarczy nawiązać połączenie pomiędzy naszą stroną (front end) a skryptem node uruchomionym na serwerze:
var socket = io.connect('//localhost:1337');
Zacznijmy jednak od podstaw node.js i socket.io, krok po kroku.
Wpierw dostosujmy naszą konfigurację o elementy potrzebne dla gniazdek. Konfigurację przedstawiliśmy w poprzednim artykule, teraz ją tylko rozszerzymy…
var config = {}; config.db = {}; config.general = {}; // added config.db.type = 'mysql'; config.db.charset = 'utf8'; config.db.username = 'user'; config.db.password = 'pass'; config.db.host = 'localhost'; config.db.dbname = 'node_tests'; // DB name config.db.comments_tbl = 'comments'; // added config.general.host = '//localhost'; // etc config.general.port = 1337; config.general.debug = false; // debug mode on/off // export module.exports = config;
Dodaliśmy nowe parametry konfiguracji. Dziś nie będziemy korzystać z baz danych; ta konfiguracja przyda nam się także w kolejnym tutorialu.
Aby wystartować potrzebujemy jeszcze tylko modułu socket.io. Zainstalujmy go więc w folderze z naszym kodem:
$ npm install socket.io
lub jeśli chcemy – globalnie:
$ npm -g install socket.io
Kodujmy! Stworzymy dwie części – front end oraz back end.
1. Strona HTML
<!DOCTYPE html> <html> <head> <title>Socket.io tests</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.2/socket.io.min.js"></script> <script> var socket = io.connect('//localhost:1337'); // emit some data socket.emit("user_online_data", { data: {id: 1, name: "Tester", msg: "Hello!"} }); // handle our events socket.on('welcome', function (data) { // process data from node script $('#messages').append('<li>' + data.message + '</li>'); // emit data to node script socket.emit("sayhi", { data: 'Hi node!' }); }); // another "event" we named "time" socket.on('time', function (data) { console.log(data); $('#messages').append('<li>' + data.time + '</li>'); }); // if someting went wront and node script sent error data socket.on('error', function () { console.error(arguments); }); </script> </head> <body> <ul id="messages"></ul> </body> </html>
Zawartość tego pliku możemy uruchomić zwyczajnie w przeglądarce, ale o tym za chwilę. Omówimy też cały kod po kolei.
Potrzebujemy jeszcze części działającej po stronie serwera.
2. Skrypt (serwer) node – test.js
/* * node.js and socket.io tests */ var http = require('http'), fs = require('fs'), index = fs.readFileSync(__dirname + '/index.html'); // setup var config = require('./config.js'); var PORT = config.general.port; var HOST = config.general.host; // send html content to all requests var app = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end(index); }); // socket.io server is listening ... var io = require('socket.io').listen(app); // sample function: // send current time to all connected clients function sendTime() { io.sockets.emit('time', { time: new Date().toJSON() }); } // send current time every 10 secs setInterval(sendTime, 10000); // emit welcome message on connection io.sockets.on('connection', function (socket) { socket.user_id = 0; // current user ID socket.emit('welcome', {message: 'Welcome!'}); socket.on('sayhi', console.log); // just log data in console // process the data socket.on('user_online_data', function (data) { socket.user_id = data.mydata.id; // "socket scope" var name = data.mydata.name; // local scope console.log("#:" + socket.user_id + ", name: " + name); }); }); app.listen(PORT); console.log('Server running at ' + HOST + ':' + PORT + '/'); // console.log('Debug: ' + config.main.debug);
Zakładamy że oba pliki znajdują się w tym samym folderze. W ten sposób serwer node, który właśnie napisaliśmy, zaserwuje nam via WWW właśnie treść naszej strony (index.html). Dzieje się to w linijce:
... index = fs.readFileSync(__dirname + '/index.html');
Zakładając że mamy już zainstalowany moduł socket.io (npm install socket.io), możemy uruchomić nasz serwer.
Uruchamiamy konsolę i w folderze z naszym kodem startujemy skrypt test.js:
$ node test.js
a następnie otwórzmy w przeglądarce stronę na naszym porcie, np.:
http://localhost:1337/
Powinniśmy zobaczyć zaserwowaną stronę, która co 10 sekund będzie aktualizowana o dane wysyłane przez serwer node.js:
W konsoli natomiast powinniśmy zobaczyć dane wysłane ze strony klienta:
Czysta, sprawna komunikacja i wymiana danych. To właśnie node.js i socket.io!
Co się dzieje w kodzie
Użyliśmy tutaj podstawowych elementów potrzebnych do efektywnej pracy z node.js i socket.io. Omówmy więc po kolei.
Plik index.html
Wpierw dodajemy potrzebne biblioteki: socket.io.min.js oraz dodatkowo jQuery.
Po nawiązaniu połączenia przechodzimy do właściwej komunikacji. Uwaga: aby skutecznie się połączyć zawsze pamiętajmy o porcie (dla kodu po stronie klient i serwera musi być użyty ten sam).
Wysyłamy dane metodą emit w postaci: socket.emit(„nazwa”, { dane });
W kolejnych linijkach obsługujemy przypadek, gdy serwer wysyła nam wiadomość „welcome”:
socket.on('welcome', function (data) { ...
Kolejnym zdarzeniem, które obsługujemy, jest wysłanie przez serwer dokładnej informacji o aktualnym czasie, którą serwer wysyła co 10 sekund. To oczywiście tylko w celach demonstracji:
... socket.on('time', function (data) { console.log(data); $('#messages').append('<li>' + data.time + '</li>'); }); ...
Przejdźmy teraz do kodu po stronie serwera.
Serwer – plik test.js
Pierwszą rzeczą tutaj jest „set up” potrzebnych modułów:
var http = require('http'), fs = require('fs'), index = fs.readFileSync(__dirname + '/index.html');
Ostatnia linijka to odczyt naszego pliku index.html celem zaserwowania go.
Oczywiście nie musimy tego robić; zwykle gdy programujemy aplikację web, mamy osobny kod (np. bazujący na frameworku PHP), więc kod do komunikacji poprzez socket.io umieszczamy na przykład w szablonie danej strony.
Wystarczy wtedy zaserwować cokolwiek, np.:
var http = require('http'), index = "<html><body>Listening ...</body></html>";
Kolejne operacje w naszym kodzie to pobranie ustawień i stworzenie serwera.
Następnie mamy funkcję sendTime(), wywoływaną co 10 sekund, która emituje dane – tutaj obiekt z bieżącą datą w formacie JSON.
I przechodzimy do bloku kodu, który obsługuje komunikację:
io.sockets.on('connection', function (socket) { ...
W przypadku poprawnego połączenia socket.io zostanie wykonany kod z tego bloku. W naszym przypadku jest to
– wysłanie wiadomości welcome:
socket.emit('welcome', {message: 'Welcome!'});
– obsługa zdarzenia „sayhi” wyemitowanego po stronie klienta:
socket.on('sayhi', console.log);
– na końcu coś ciekawszego – przetworzenie otrzymanych danych:
socket.on('user_online_data', function (data) { socket.user_id = data.mydata.id; // "socket scope" var name = data.mydata.name; // local scope console.log("User #:" + socket.user_id + ", name: " + name); });
Jak widać pobranie danych jest bardzo proste. Warto także zwrócić uwagę na zmienną lokalną (var) oraz zmienną w zasięgu gniazdka. Gdy chcemy np. posługiwać się wartością ID bieżącego użytkownika w danym połączeniu, możemy jest przypisać do gniazdka (socket.user_id = data.mydata.id).
Zerwanie połączenia
Gdy mamy naszą stronę w przeglądarce i gdy wyłączymy skrypt po stronie serwera (CTRL + C w konsoli), wystąpią oczywiście błędy komunikacji.
Otwierając np. Firebug będziemy widzieć nieudane połączenia:
To naturalny efekt wyłączenia skryptu, z którym kod po stronie klienta próbuje się komunikować.
Następuje wiele prób – aby to ograniczyć, wystarczy zmienić linijkę odpowiedzialną za połączenie:
var socket = io.connect('//localhost:1337', {reconnection: false});
Zablokuje to próby ponownego połączenia.
Pełny kod przykładu do pobrania tutaj:
https://github.com/dominik-w/js_html5_com/tree/master/node-socket-io-tutorial
Omówiliśmy podstawowe elementy na których możemy budować nasze rozwiązania bazujące na node i socket.io. Wkrótce kolejny tutorial z małym przykładem praktycznym – komentarze w czasie rzeczywistym.
Node.js i socket.io – podsumowanie
Cóż – to super technologie, o czym powinna świadczyć dzisiejsza próbka możliwości, choć to wciąż jedynie podstawowe informacje.
Zachęcam również do sprawdzenia dokumentacji socket.io:
Dziękuję za uwagę!