Serwer WWW z elementami grafiki 3D – praktyczne wykorzystanie pakietów Node.js oraz Three.js w systemach wbudowanych (część 1)
Serwer WWW z podziałem na funkcje front-end/back-end
Bezpośrednie umieszczenie „kodu strony” – w postaci napisu „Hello World!” – w funkcji response.end() nie wpływa znacząco na czytelność kodu, jednak nietrudno wyobrazić sobie sytuację, że budowany przez nas serwis zaczyna się rozrastać, a wprowadzane znaczniki HTML znacznie zwiększają objętość kodu strony, powodując że skrypt main.js może stać się mało czytelny i trudny w zarządzaniu. W takiej sytuacji niezbędne jest wprowadzenie jasnego podziału na front-end (czyli właściwą stronę udostępnianą użytkownikowi) oraz back-end (czyli kod realizujący zadania stawiane przed serwerem). Wprowadzenie podziału na front-end i back-end wymaga jedynie kosmetycznych zmian w skrypcie main.js z listingu 1. Zmodyfikowany skrypt przedstawiono na listingu 2.

var http = require ('http');
var fs = require ('fs');
var index = fs.readFileSync (__dirname + '/index.html');
var PORT = 8080;
var server = http.createServer (function handler (request, response) {
response.writeHead (200, {'Content-Type': 'text/html'});
response.end (index);
});
server.listen (PORT);
Listing 2. Serwer WWW z podziałem na funkcje fron-end/back-end
Z wykorzystaniem wbudowanego modułu fs (pozwalającego na przeprowadzanie szeregu operacji I/O na plikach) w sposób synchroniczny wczytujemy zawartość pliku index.html, umieszczonego w tym samym folderze co skrypt main.js. Ponieważ wczytany plik jest prostą stroną HTML, zmieniamy zawartość pola Content-Type na text/html. W wywołaniu response.end() przesyłamy użytkownikowi zawartość pliku index.html.
Dla kompletności zadania, utwórzmy również najprostszy plik HTML – index.html – jak przedstawiono to na listingu 3.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
Listing 3. Zawartość pliku index.html
Po zakończonej edycji plików main.js oraz index.html, sprawdźmy poprawność naszego kodu poprzez ponowne uruchomienie serwera:
nodejs main.js
Komunikacja front-end – back-end z wykorzystaniem socket.io
Wprowadzenie wyraźnego podziału na sekcje front-end i back-end stawia przed nami kolejne zadanie do wykonania – zapewnienie sprawnej komunikacji i wymiany danych w „czasie rzeczywistym” pomiędzy tymi modułami. Dlaczego w czasie rzeczywistym? Protokół HTTP jest typowym protokołem typu żądanie-odpowiedź, w którym to rolę żądającego pełni klient/przeglądarka internetowa. Rozwiązanie to spełnia swoje zadanie w przypadku gdy to klient chce przesłać dane do serwera. Niestety w sytuacji gdy serwer chce poinformować odbiorcę o aktualizacji danych (np. nowych odczytach z czujników temperatury, których wyniki powinny być wyświetlane w czasie rzeczywistym w interfejsie przeglądarkowym), nie może on zainicjować połączenia z klientem. Modyfikacja „w locie” pliku index.html przez kod serwera oraz cykliczne odświeżanie strony przez klienta nie brzmią jak idealne rozwiązanie problemu. W takiej sytuacji pomocną dłoń wyciąga do nas biblioteka socket.io [5] zapewniająca połączenie pomiędzy stroną WWW (front-endem) a skryptem uruchomionym na serwerze (back-endem). Socket.io jest biblioteką języka JavaScript, której zadaniem jest ułatwienie pracy z protokołem WebSocket (który to natomiast jest częścią specyfikacji HTML5, umożliwiającą dwustronną komunikację klient-serwer w czasie rzeczywistym). Biblioteka socket.io składa się z tzw. części serwerowej (będącej modułem dla platformy Node.js) oraz klienckiej (dla przeglądarek internetowych). Bazując na kodzie skryptu main.js oraz strony index.html z poprzedniego podrozdziału, przejdźmy do praktycznej implementacji.
Rozbudowę skryptu main.js rozpoczynamy od zaimportowania modułu socket.io (szczegóły dotyczące instalacji zewnętrznego pakietu socket.io przedstawiono
w ramce poniżej):
var io = require ('socket.io').listen(server);
Pakiet socket.io nie jest częścią platformy Node.js i wymaga dodatkowej instalacji. Do instalacji dodatkowych pakietów można wykorzystać dystrybuowany wraz z Node.js, manager pakietów npm:
npm install socket.io
W następnym kroku utwórzmy event-handler dla zdarzenia connection (które jest wywoływane każdorazowo, gdy do serwera podłączony zostanie nowy klient), wyświetlający krótki komunikat na standardowym wyjściu:
io.on ('connection', function (socket) {
console.log ('We have new connection!');
});
W docelowym rozwiązaniu aplikacja serwera będzie przesyłała do przeglądarki użytkownika informacje odczytane z modułu żyroskopu. Sposób w jaki zostanie zrealizowana komunikacja pomiędzy procesem obsługującym żyroskop a serwerem WWW, zostanie omówiony w kolejnym podrozdziale artykułu. Na potrzeby obecnego etapu prac, przygotujmy prostą funkcję send_time(), która z interwałem jednej sekundy, prześle do wszystkich podłączonych klientów aktualny czas:
function send_time() {
io.emit ('time', {message: new Date().toISOString()});
}
setInterval (send_time, 1000);
W ciele funkcji send_time() wysyłamy rozgłoszeniową wiadomość time z aktualnym czasem serwera, skierowaną do wszystkich aktualnie podłączonych klientów. Pełny kod skryptu main.js został przedstawiony na listingu 4.
var http = require ('http');
var fs = require ('fs');
var index = fs.readFileSync (__dirname + '/index.html');
var PORT = 8080;
var server = http.createServer (function handler (request, response) {
response.writeHead (200, {'Content-Type': 'text/html'});
response.end (index);
});
var io = require ('socket.io').listen(server);
io.on ('connection', function (socket) {
console.log ('We have new connection!');
});
function send_time() {
io.emit ('time', {message: new Date().toISOString()});
}
setInterval (send_time, 1000);
server.listen (PORT);
Listing 4. Skrypt main.js zintegrowany z biblioteką socket.io
Ostatnim etapem zadania jest integracja biblioteki socket.io z udostępnianą przez serwer stroną index.html. Integrację biblioteki rozpoczniemy od dołączenia w sekcji <head> biblioteki socket.io:
<script src='/socket.io/socket.io.js'></script>
Również w sekcji <head> utwórzmy prosty skrypt realizujący nawiązanie połączenia z serwerem oraz odbiór komunikatów (należy pamiętać, że kod zawarty w tagach <script> </script> zostanie uruchomiony przez przeglądarkę, a więc komputer PC użytkownika):
var socket = io();
socket.on ('time', function (data) {
/* TODO */
});
Zanim przystąpimy do uzupełnienia kodu event-handler’a dla zdefiniowanego przez nas zdarzenia time, w sekcji <body> strony HTML utwórzmy akapit z identyfikatorem test, w miejscu którego wyświetlone zostaną dane otrzymane z serwera WWW:
<p id="test">JavaScript can change HTML content.</p>
Mając określone pole w którym otrzymane dane będą wyświetlane, możemy uzupełnić implementację event-handler’a dla zdarzenia time:
socket.on ('time', function (data) {
document.getElementById("test").innerHTML = data.message;
});
Pełna zawartość pliku index.html została przedstawiona na listingu 5.
<!DOCTYPE html>
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script>
var socket = io();
socket.on ('time', function (data) {
document.getElementById("test").innerHTML = data.message;
});
</script>
</head>
<body>
<h1>Hello World!</h1>
<p id="test">JavaScript can change HTML content.</p>
</body>
</html>
Listing 5. Strona index.html zintegrowana z biblioteką socket.io
Przy ponownym uruchomieniu serwera poleceniem:
nodejs main.js
oraz odświeżeniu zawartości strony WWW, powinniśmy uzyskać efekt przedstawiony na rysunku 2.
Rys. 2. Przykład komunikacji między serwer WWW a przeglądarką internetową

Technologie End of Life i bezpieczeństwo sieci – wyzwania Europy związane z tzw. długiem technologicznym
Najczęstsze błędy firm przy wyborze dostawcy energii i jak ich uniknąć
Fotorezystor, czyli czujnik światła dwojakiego działania. Przykład innowacji w automatyce i elektronice możliwej dzięki technologii fotooporników 



