11/08/2018, 22:17

Node.js và Socket.IO căn bản

I. Note.js là gì ? Node.js là một hệ thống phần được thiết kế để viết các ứng dụng internet có khả năng mở rộng, đặc biệt là máy chủ web. Chương trình được viết bằng JavaScript, sử dụng kỹ thật điều khiển theo sự kiện, nhập/xuất không đồng bộ để tối thiểu tổng chi phí và tối đại khả năng mở ...

I. Note.js là gì ? Node.js là một hệ thống phần được thiết kế để viết các ứng dụng internet có khả năng mở rộng, đặc biệt là máy chủ web. Chương trình được viết bằng JavaScript, sử dụng kỹ thật điều khiển theo sự kiện, nhập/xuất không đồng bộ để tối thiểu tổng chi phí và tối đại khả năng mở rộng. Node.js bao gồm có V8 JavaScript engine của Google,libUV, và vài thư viện khác.

Node.js được tạo bởi Ryan Dahl từ năm 2009, và phát triển dưới sự bảo trợ của Joyent.

Mục tiêu ban đầu của Dahl là làm cho trang web có khả năng push như trong một số ứng dụng web như Gmail. Sau khi thử với vài ngôn ngữ Dahl chọn Javascript vì một API Nhập/Xuất không đầy đủ. Điều này cho phép anh có thể định nghĩa một quy ước Nhập/Xuất điểu khiển theo sự kiện, non-blocking.

Vài môi trường tương tự được viết trong các ngôn ngữ khác bao gồm Twisted cho Python, Perl Object Environment cho Perl, libevent cho C và EventMachine cho Ruby. Khác với hầu hết các chương trình Javascript, Nodejs không chạy trên một trình duyệt mà chạy trên Server. Node.js sử dụng nhiều chi tiết kỹ thuật của CommonJs. Nó cung cấp một môi trường REPL cho kiểm thử tương tác.

Node.js được InfoWorld bình chọn là "Công nghệ của năm" năm 2012.

II. Sơ lược về Socket và SocketIO :

Trước tiên, Socket là 1 công nghệ. Đừng nhầm lẫn giữa Socket.IO và Socket. Socket.IO không phải là mô hình Socket duy nhất hiện nay, và cũng không phải là mô hình web socket duy nhất hiện nay.Socket là cách bạn tổ chức mô hình client-server để một trong 2 bên luôn trong tình trạng sẵn sàng trả lời bên kia và ngược lại. Để đảm bảo việc này, kết nối giữa Client và Server phải ở trạng thái “keep-alive” và phải luôn xảy ra quá trình đồng bộ giữa Client-Server. Socket sẽ mang lại khả năng trả lời tức thì từ một trong 2 bên khi bên kia đưa ra một sự kiện, thay vì phải thực thi lại một loạt các thủ tục kết nối phức tạp như trước, và ứng dụng của bạn sẽ trở thành ứng dụng thời gian thực ví dụ: Yahoo Messenger, Skype v.v… đều là các ứng dụng được xây dựng theo mô hình Socket.

Trong lập trình web trước đây, việc xây dựng client-server theo mô hình socket phải thông qua các phần mềm thứ . Vì mô hình socket không phù hợp với các ngôn ngữ lập trình Server như: PHP, ASP.NET, JSP v.v… Các ngôn ngữ này luôn làm việc theo cách: Die ngay connection khi Server trả lời Client xong. Tuy nhiên, mình nhấn mạnh: Chúng ta có thể làm được web-socket với bất kỳ ngôn ngữ lập trình nào. Chỉ có điều, với các ngôn ngữ cũ, việc làm này cần bạn phải am hiểu các giao thức http, tcp; hiểu thế nào là 1 request header, v.v…

Node.js là một ngôn ngữ mới, xây dựng thuần túy bằng javascript. Đây là một điểm lợi thế của Node.js để lập trình web-socket:

  • Thứ nhất: javascript là ngôn ngữ lập trình hướng sự kiện, mà trong lập trình thời gian thực, cách tiếp cận bằng lập trình sự kiện là cách tiếp cận khôn ngoan nhất.

  • Thứ hai: Node.js chạy non-blocking việc hệ thống không phải tạm ngừng để xử lý xong một request sẽ giúp cho server trả lời client gần như ngay tức thì.

  • Thứ ba: lập trình socket yêu cầu bạn phải xây dựng được mô hình lắng nghe - trả lời từ cả 2 bên. Nói khác đi, vai trò của client và server phải tương đương nhau, mà client thì chạy bằng javascript, nên nếu server cũng chạy bằng javascript nữa, thì việc lập trình sẽ dễ dàng và thân thiện hơn.

Chính vì những đặc điểm này, socket.io ra đời. Tuy nhiên, khi bạn thực sự am hiểu về Node.js, http request header, bạn hoàn toàn có thể viết một socket cho riêng mình.

III. Các cấu hình cơ bản:

Hãy tạo một thư mục để lưu dự án của bạn: websocket_test và cài đặt socket.io vào thư mục đó bằng cách gõ đoạn lệnh sau vào của sổ command promt (terminal):

cd /path/den/thu/muc/websocket_test && npm install socket.io

bạn sẽ có:

image1.png

hãy tạo một file javascript để chạy server của bạn trong thư mục websocket_test tên là socket_server.js (hoặc bất cứ cái gì bạn thích ) và chèn đoạn code sau vào:

    var http = require('http'),
    socketIO = require('socket.io'),

    port = process.env.PORT || 8080,
    ip = process.env.IP || '127.0.0.1',

    server = http.createServer().listen(port, ip, function(){
        console.log('Socket.IO server started at %s:%s!', ip, port);
    }),

    io = socketIO.listen(server);
    io.set('match origin protocol', true);
    io.set('origins', '*:*');

Nếu bạn thắc mắc đoạn:

...
    port = process.env.PORT || 8080,
    ip = process.env.IP || '127.0.0.1',
...

thì đó là cách để bạn lấy các biến môi trường để host ở cloud còn nếu bạn muốn test localhost thôi thì chỉ cần:

...
    port = 8080,
    ip = '127.0.0.1',
...
  • io.set(string option, mixed value) là phương thức cài đặt các cấu hình cho socket.io.

  • io.set('match origin protocol', true); là config để bạn có thể chạy được node.js server cùng với socket.io server. Nói nôm na là nó phân biệt được đâu là socket.io, đâu là node.js server. Bạn nên config, vì server chỉ dùng để chạy socket.io không thôi cũng phí.

  • io.set('origins', ':'); là config để socket.io server sẽ nhận những client ở domain nào port nào, ở đây là 'nhận tất'. Tuy nhiên để đảm bảo socket.io server phục vụ đúng client và bảo mật, khi triển khai thực tế, web của bạn chạy ở domain nào, port nào thì bạn nên chỉ rõ.

Và thành quả chí ít là đến lúc này:

image-2-300x206.png

IV. PHÁT VÀ NHẬN SỰ KIỆN

Để tiếp tục, bạn cần tạo 1 file javascript cho client tên là 'socket_client.js' (hoặc bất cứ cái gì bạn thích) và hiển nhiên, 1 file html tên là 'index.html' (hoặc bất cứ cái gì bạn thích ):

image-3.png

Tiếp tục quay trở lại file socket_server.js, bạn thêm đoạn lệnh sau:

    var run = function(socket){
    // Socket process here!!!
    }

    io.sockets.on('connection', run);
  • run = function(socket) là đoạn mã mà bạn định nghĩa để xử lý 1 client-socket khi socket đó kết nối đến server của bạn. Tham số socket đại diện cho chính client vừa kết nối đó.

  • io.sockets.on('connection', run) - Đây là đoạn mã triệu gọi xử lý run ở trên. Nếu bạn đã quen với lập trình hướng sự kiện thì dễ hiểu rồi, còn không thì bạn có thể tìm hiểu phần callback. Nói nôm na, là đây là đoạn xử lý chính sau khi client dùng socket kết nối thành công đến server.

Ngoài sự kiện 'connection' ở trên, còn rất nhiều sự kiện khác đã được định nghĩa trước để bạn xử lý như: mất kết nối đột ngột, xảy ra lỗi kết nối, ngắt kết nối, v.v...

Đó là những sự kiện cơ bản. Bây giờ sẽ là những sự kiện do chính bạn viết ra: sửa hàm run(socket) thành như sau:

    var run = function(socket){
    // Socket process here!!!
    socket.emit('greeting', 'Hello from Socket.IO server');
    }

Đây chính là cách bạn phát một sự kiện từ server đến client. Phương thức socket.emit(string event, object data) sẽ thực hiện việc phát 1 sự kiên về client và đồng thời gửi dữ liệu data về client. Bây giờ là phần client, bạn chèn đoạn mã sau vào file 'socket_client.js'(*):

    $(document).ready(function(){
        var socket = io.connect('http://127.0.0.1:8080');
        socket.on('greeting', function(data){
            alert(data);
        })
    })

Sau đó, chèn đoạn mã cực-kỳ-cơ-bản này vào file index.html và lưu lại(*):

    <!doctype html>
    <html>
        <head>
            <script src='http://127.0.0.1:8080/socket.io/socket.io.js'></script>
            <script src='http://code.jquery.com/jquery-1.10.1.min.js'></script>
            <script src='socket_client.js'></script>
        </head>
    </html>

Sau đó, chạy file socket_server.js:

    node /path/den/file/socket_server.js

image-4-300x115.png

và mở file index.html bằng một browser bất kỳ (tuy nhiên không khuyến khích IE cho lắm và khuyến khích GoogleChrome). Bạn sẽ có kết quả:

image-5-300x190.png

Đó là cách server phát sự kiện, client nhận sự kiện. Bây giờ là chiều ngược lại, hoàn toàn tương tự:

Trước tiên bạn sửa file index.html thành như sau:

    <!doctype html>
    <html>
        <head>
            <script src='http://127.0.0.1:8080/socket.io/socket.io.js'></script>
            <script src='http://code.jquery.com/jquery-1.10.1.min.js'></script>
            <script src='socket_client.js'></script>
        </head>
        <body>
            <div>
                <p>Hello world! You're:</p>
                <input type='text' id='name' value='Anynomous'/>
                <button id='go'>Go</button>
            </div>
        <body>
    </html>

và file 'socket_client.js' thành:

    $(document).ready(function(){
        var socket = io.connect('http://127.0.0.1:8080');
        socket.on('greeting', function(data){
            alert(data);
        })

        $('#go').click(function(){
            socket.emit('user-join',$('#name').val());
        })
    })

Vậy là client đã gửi event 'user-join' lên server, kèm theo đó là dữ liệu chứa tên của người join. Bây giờ, server sẽ lắng nghe event này:

    file 'socket_server.js':
    var http = require('http'),

    socketIO = require('socket.io'),

    port = process.env.PORT || 8080,

    ip = process.env.IP || '127.0.0.1',

    server = http.createServer().listen(port, ip, function(){

        console.log('Socket.IO server started at %s:%s!', ip, port);

    }),

    io = socketIO.listen(server);

    io.set('match origin protocol', true);

    io.set('origins', '*:*');

    io.set('log level', 1);

    var run = function(socket){
        // Socket process here!!!
        socket.emit('greeting', 'Hello from Socket.IO');
        // 'user-join' event handler here
        socket.on('user-join', function(data){
            console.log('User %s have joined', data);
        })
    }

    io.sockets.on('connection', run);

Chạy lại server và refresh browser nhập tên và 'Go': Client (gửi): image-6.png

Server (nhận):

image-7-300x86.png

Và cứ thế bạn tiếp tục phát và nhận sự kiện giữa server và client thôi. Ví dụ trong trường hợp này, mình muốn thông báo đến tất cả các client khác việc đã có một người tham gia, bạn sửa đoạn mã xử lý sự kiện 'user-join' của socket_server.js thành như sau:

    socket.on('user-join', function(data){

        console.log('User %s have joined', data);
        socket.broadcast.emit('new-user', data);

    })

ở đây, socket.bradcast.emit là phương thức thông báo cho tất cả các socket khác ngoại trừ chính socket đang làm việc ra. Và ở client, bạn sẽ lắng nghe sự kiện 'user-join' bằng cách thêm đoạn code sau :

    socket.on('new-user', function(data){
        alert(data + ' Have joined!');
    })

Restart server để đoạn code mới có thể hoạt động.

0