12/08/2018, 17:10

Cơ bản về giao thức Websocket và thư viện Socket.io

Hiện nay ứng dụng web đã phát triển khác xa so với ngày đầu nó xuất hiện, kèm theo đó là vô số các kỹ thuật mới được áp dụng để phục vụ cho quá trình này nhằm đem lại trải nghiệm mới mẻ, đầy hứng thú và cũng không kém phần tiện dụng cho người dùng. Công nghệ web thời gian thực(realtime) ngày càng ...

Hiện nay ứng dụng web đã phát triển khác xa so với ngày đầu nó xuất hiện, kèm theo đó là vô số các kỹ thuật mới được áp dụng để phục vụ cho quá trình này nhằm đem lại trải nghiệm mới mẻ, đầy hứng thú và cũng không kém phần tiện dụng cho người dùng. Công nghệ web thời gian thực(realtime) ngày càng trở nên phổ biết. Có nhiều công nghệ, phương pháp giúp xây dựng ứng dụng thời gian thực

  • AJAX LONG-POLLING:
  • SERVER SENT EVENTS (SSE)
  • COMET
  • WEBSOCKET

Trong đó WEBSOCKET với sự hỗ trợ của HTML 5 đang trở lên chiếm ưu thế tuyệt đối.

WebSocket là một giao thức giúp truyền dữ liệu hai chiều giữa server-client qua một kết nối TCP duy nhất. Hơn nữa, webSocket là một giao thức được thiết kế để truyền dữ liệu bằng cách sử dụng cổng 80 và cổng 443 và nó là một phần của HTML5. Vì vậy, webSockets có thể hoạt động trên các cổng web tiêu chuẩn, nên không có rắc rối về việc mở cổng cho các ứng dụng, lo lắng về việc bị chặn bởi các tường lửa hay proxy server

Không giống với giao thức HTTP là cần client chủ động gửi yêu cầu cho server, client sẽ chời đợi để nhận được dữ liệu từ máy chủ. Hay nói cách khác với giao thức Websocket thì server có thể chủ động gửi thông tin đến client mà không cần phải có yêu cầu từ client.

Tất cả dữ liệu giao tiếp giữa client-server sẽ được gửi trực tiếp qua một kết nối cố định làm cho thông tin được gửi đi nhanh chóng và liên tục khi cần thiết. WebSocket làm giảm độ trễ bởi vì một khi kết nối WebSocket được thành lập, server không cần phải chờ đợi cho một yêu cầu từ client.

Tương tự như vậy, client có thể gửi tin nhắn đến server bất cứ lúc nào. Yêu cầu duy nhất này giúp làm giảm đáng kể độ trễ, mà sẽ gửi một yêu cầu trong khoảng thời gian, cho dù thông điệp có sẵn.

Để có thể sử dụng được Websocket thì không phải chỉ cần trình duyệt hỗ trợ mà còn phải có server Websocket, server Websocket có thể được tạo ra bằng bất kỳ ngôn ngữ server-side nào, nhưng Node.js được sử dụng rộng rãi hơn cả vì nó viết bằng Javascript nên mang nhiều ưu điểm so với các ngôn ngữ server-side truyền thống khác.

Hoạt động

Giao thức có hai phần: Bắt tay và truyền dữ liệu Ban đầu client sẽ gửi yêu cầu khởi tạo kết nối websocket đến server, server kiểm tra và gửi trả kết quả chấp nhận kết nối, sau đó kết nối được tạo và quá trình gửi dữ liệu có thể được thực hiện, dữ liệu chính là các Ws frame

Bắt tay

Đầu tiên client sẽ gửi một http request yêu cầu nâng cấp

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

server trả về

HTTP/1.1 101 Switching Protocols
 Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Để xác nhận việc kết nối, client sẽ gửi một giá trị Sec-WebSocket-Key được mã hóa bằng Based64 đến server.

Sau đó bên server sẽ thực hiện:

  • Nối thêm chuỗi cố định là “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″ vào Sec-WebSocket-Key để được chuỗi mới là “x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11″.
  • Thực hiện mã hóa SHA-1 chuỗi trên để được “1d29ab734b0c9585240069a6e4e3e91b61da1969″.
  • Mã hóa kết quả vừa nhận được bằng Base64 để được “HSmrc0sMlYUkAGmm5OPpG2HaGWk=”
  • Gửi response lại client kèm với giá trị Sec-WebSocket-Accept chính là chuỗi kết quả vừa tạo ra.

Client sẽ kiểm tra status code (phải bằng 101) và Sec-WebSocket-Accept xem có đúng với kết quả mong đợi không và thực hiện kết nối.

Trên thực tế các trường dữ liệu trao đổi có thể khác nhau. Dưới đây là hình ảnh khi client dùng thư viện socket.io mởi kết nối đến server socket

Truyền dữ liệu

Dữ lệu sẽ được truyền thông qua một kết nối duy nhất được tạo ra sau quá trình bắt tay. Dữ liệu được truyền bằng các Frame, ta có thể thấy nó khi bật trình debug của trình duyệt lên

Socket.IO là một bộ thư viện dành cho các ứng dụng web, mobile đê phát triển các ứng dụng realtime. Với đặc trưng mạnh mẽ và dễ sử dụng, Socket.IO đang ngày càng được sử dụng rộng rãi từ những trang mạng xã hội cần sự tương tác cao, đến các blog hay các trang web thương mại điện tử. Với bộ thư viện này, làm việc với WebSockets trở nên đơn giản hơn rất nhiều. Thư viện gồm 2 phần

  • Phía client: gồm bộ thư viện viết cho web(JavaScript), iOS, Android
  • Phía server: viết bằng JavaScript và dùng cho các máy chủ node.JS

Socket.IO hỗ trợ sử dụng rất nhiều các công nghệ realtime

  • WebSocket
  • Flash Socket
  • AJAX long-polling
  • AJAX multipart streaming
  • IFrame
  • JSONP polling

Nó sẽ tự động chuyển sang Websocket nếu có thể, hầu hết các trình duyệt hiện nay đã hỗ trợ websocket nên việc sử dụng socket.io trên trình duyệt cũng là đang sử dụng websocket Việc sử dụng socket.io rất đơn giản và giống nhau ở cả client lẫn server nó bao gồm 3 phần chính:

  • Khởi tạo kết nối
  • Lắng nghe event
  • Gửi event

Riêng ở server thì sẽ không có phần khởi tạo kết nối vì chỉ có clent mới cần khởi tạo kết nối đến server Việc dùng socket.io đồng bộ ở cả client lẫn server, cú pháp cũng khá đơn giản. Chúng ta sẽ làm một ví dụ nhỏ để làm quen với một số cách gửi và nhận dữ liệu với socket.io

Server API

Ta sẽ khởi tạo một server socket bằng thư viện socket.io Đầu tiên cần tạo ứng dụng nodejs tạo file package.json với nội dung sau

{
  "name": "Websocket",
  "version": "1.0.0",
  "description": "begin",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "thangnt",
  "license": "ISC",
  "dependencies": {
    "express": "^4.15.4",
    "request": "^2.81.0",
    "socket.io": "^2.0.4"
  }
}

file index.js

var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

var port = (process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 6969);
server.listen(port, () => console.log('Server running in port ' + port));



io.on('connection', function (socket) { //Bắt sự kiện một client kết nối đến server
 
  socket.on('all client', function (data) { //lắng nghe event 'all client'
    io.sockets.emit('news', socket.id + ' send all client: ' + data); // gửi cho tất cả client
  });
  
  socket.on('broadcast', function (data) { //lắng nghe event 'broadcast'
    socket.broadcast.emit('news',  socket.id + ' send broadcast: ' + data); // gửi event cho tất cả các client từ client hiện tại
  });

  socket.on('private', function (data) { //lắng nghe event 'private'
    socket.emit('news', ' You send private message: ' + data); // chỉ gửi event cho client hiện tại
  });

});

app.get('/', (req, res) => {
  res.sendFile('test-socket-client.html', { "root": __dirname });
})

Client API

Để sử dụng được socket.io ở phía trình duyệt, ta cần import thư viện socket.io tạo file test-socket-client.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Title of the document</title>
</head>

<body>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script>
    var socket = io.connect('http://localhost:6969');// clent khởi tạo kết nối socket đến server
    socket.on('news', function (data) { // lắng nghe event 'news' được server gửi đến
            console.log(data); // log data để kiểm tra
        });
    $(document).ready(function () {
        $('#send_private').click(function(){
            socket.emit('private',  $('#data').val()); // gửi event 'private'
             $('#data').val(');
        });
        $('#send_broadcast').click(function(){
            socket.emit('broadcast',  $('#data').val()); // gửi event 'broadcast'
            $('#data').val(');
        });
        $('#send_all_client').click(function(){
            socket.emit('all client',  $('#data').val()); // gửi event 'all client'
            $('#data').val(');
        })
    });
</script>
<h1>Test socket.io</h1>
<input type="text" id="data"><br>
<button id="send_private">Send Private message</button><br>
<button id="send_broadcast">Send Broadcast</button><br>
<button id="send_all_client">Send All Client</button>
</html>

chạy npm install để cài đặt các dependency tiếp tục chạy

node index.js

terminal hiển thị như sau

ngo.trung.thang@DESKTOP-CIR0DAC MINGW64 /e/Lab/Javascript/socket
$ node index.js
Server running in port 6969

Mở trình duyệt và gõ http://localhost:6969/, các bạn có thể mở 2 tab song song để kiểm tra xem socket đã chạy chưa nhé

0