Node.js i socket.io

node-js

Poziom zaawansowany

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'; // http://my.host.com 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.

Wydawnictwo Strefa Kursów

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:

2015-04-27_1234

W konsoli natomiast powinniśmy zobaczyć dane wysłane ze strony klienta:

2015-04-27_1235

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:

node-js-socket-io

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:

http://socket.io/docs

Dziękuję za uwagę!

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem. Bloger 🙂 Anty-lewak 🙂 Pasjonat programowania, nowych technologii, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook3Tweet about this on TwitterShare on Google+0Share on LinkedIn0Share on Tumblr0Digg thisEmail this to someonePin on Pinterest0
Możesz skomentować leave a response, lub podać trackback z własnej strony.