Giới thiệu về WebSocket – Đưa cổng vào web

Vấn đề: Kết nối giữa máy khách và máy chủ có độ trễ thấp

Web được xây dựng chủ yếu dựa trên mô hình yêu cầu/phản hồi của HTTP. Ứng dụng khách tải một trang web và sau đó không có gì xảy ra cho đến khi người dùng nhấp vào trang tiếp theo. Khoảng năm 2005, AJAX bắt đầu làm cho web trở nên linh hoạt hơn. Tuy nhiên, tất cả hoạt động giao tiếp HTTP đều do ứng dụng điều khiển, đòi hỏi người dùng phải tương tác hoặc thăm dò định kỳ để tải dữ liệu mới từ máy chủ.

Các công nghệ cho phép máy chủ gửi dữ liệu đến ứng dụng khách ngay khi biết rằng có dữ liệu mới đã xuất hiện từ khá lâu. Các công nghệ này có tên như "Push" hoặc "Comet". Một trong những thủ thuật phổ biến nhất để tạo ra ảo tưởng về một kết nối do máy chủ khởi tạo được gọi là thăm dò ý kiến dài. Với tính năng thăm dò ý kiến dài, ứng dụng khách sẽ mở một kết nối HTTP đến máy chủ, giữ kết nối này mở cho đến khi gửi phản hồi. Bất cứ khi nào máy chủ thực sự có dữ liệu mới, máy chủ sẽ gửi phản hồi (các kỹ thuật khác bao gồm Flash, yêu cầu XHR nhiều phần và cái gọi là htmlfiles). Kỹ thuật thăm dò dài và các kỹ thuật khác hoạt động khá hiệu quả. Bạn sử dụng chúng hằng ngày trong các ứng dụng như trò chuyện trong Gmail.

Tuy nhiên, tất cả các giải pháp này đều có chung một vấn đề: Chúng mang chi phí hao tổn của HTTP, khiến chúng không phù hợp với các ứng dụng có độ trễ thấp. Hãy nghĩ đến các trò chơi bắn súng góc nhìn thứ nhất nhiều người chơi trong trình duyệt hoặc bất kỳ trò chơi trực tuyến nào khác có thành phần theo thời gian thực.

Giới thiệu về WebSocket: Đưa cổng vào web

Thông số kỹ thuật của WebSocket xác định một API thiết lập kết nối "socket" giữa trình duyệt web và máy chủ. Nói một cách đơn giản: Có một kết nối liên tục giữa ứng dụng và máy chủ, đồng thời cả hai bên đều có thể bắt đầu gửi dữ liệu bất cứ lúc nào.

Bắt đầu

Bạn mở kết nối WebSocket chỉ bằng cách gọi hàm khởi tạo WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Hãy lưu ý ws:. Đây là giản đồ URL mới cho các kết nối WebSocket. Ngoài ra, còn có wss: cho kết nối WebSocket bảo mật, tương tự như cách dùng https: cho kết nối HTTP bảo mật.

Việc đính kèm ngay một số trình xử lý sự kiện vào kết nối cho phép bạn biết thời điểm kết nối được mở, nhận tin nhắn đến hoặc có lỗi.

Đối số thứ hai chấp nhận các giao thức phụ không bắt buộc. Đây có thể là một chuỗi hoặc một mảng chuỗi. Mỗi chuỗi phải đại diện cho một tên giao thức phụ và máy chủ chỉ chấp nhận một trong các giao thức phụ được truyền trong mảng. Bạn có thể xác định giao thức phụ được chấp nhận bằng cách truy cập vào thuộc tính protocol của đối tượng WebSocket.

Tên giao thức phụ phải là một trong những tên giao thức phụ đã đăng ký trong sổ đăng ký IANA. Hiện chỉ có một tên giao thức phụ (soap) được đăng ký tính đến tháng 2 năm 2012.

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Đang kết nối với máy chủ

Ngay khi có kết nối với máy chủ (khi sự kiện open được kích hoạt), chúng ta có thể bắt đầu gửi dữ liệu đến máy chủ bằng phương thức send('your message') trên đối tượng kết nối. Trước đây, giao thức này chỉ hỗ trợ chuỗi, nhưng trong thông số kỹ thuật mới nhất, giao thức này hiện cũng có thể gửi thông báo nhị phân. Để gửi dữ liệu nhị phân, bạn có thể sử dụng đối tượng Blob hoặc ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Tương tự, máy chủ có thể gửi thông báo cho chúng ta bất cứ lúc nào. Bất cứ khi nào việc này xảy ra, lệnh gọi lại onmessage sẽ kích hoạt. Lệnh gọi lại nhận được một đối tượng sự kiện và bạn có thể truy cập vào thông báo thực tế thông qua thuộc tính data.

WebSocket cũng có thể nhận thông báo nhị phân trong thông số kỹ thuật mới nhất. Bạn có thể nhận khung nhị phân ở định dạng Blob hoặc ArrayBuffer. Để chỉ định định dạng của tệp nhị phân đã nhận, hãy đặt thuộc tính binaryType của đối tượng WebSocket thành "blob" hoặc "arraybuffer". Định dạng mặc định là "blob". (Bạn không cần phải điều chỉnh tham số binaryType khi gửi.)

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Một tính năng mới được thêm vào WebSocket là tiện ích. Khi sử dụng tiện ích, bạn có thể gửi khung đã nén, đã ghép kênh, v.v. Bạn có thể tìm thấy các tiện ích được máy chủ chấp nhận bằng cách kiểm tra thuộc tính tiện ích của đối tượng WebSocket sau sự kiện mở. Tính đến tháng 2 năm 2012, vẫn chưa có thông số kỹ thuật chính thức nào được công bố cho tiện ích.

// Determining accepted extensions
console.log(connection.extensions);

Giao tiếp khác nguồn gốc

Là một giao thức hiện đại, giao tiếp nhiều nguồn gốc được tích hợp ngay vào WebSocket. Mặc dù bạn vẫn nên đảm bảo chỉ giao tiếp với các ứng dụng và máy chủ mà bạn tin tưởng, nhưng WebSocket cho phép giao tiếp giữa các bên trên bất kỳ miền nào. Máy chủ quyết định xem có cung cấp dịch vụ cho tất cả ứng dụng hay chỉ cung cấp cho những ứng dụng nằm trên một nhóm miền được xác định rõ ràng.

Máy chủ proxy

Mỗi công nghệ mới đều đi kèm với một loạt vấn đề mới. Trong trường hợp của WebSocket, đó là khả năng tương thích với các máy chủ proxy làm trung gian cho các kết nối HTTP trong hầu hết các mạng công ty. Giao thức WebSocket sử dụng hệ thống nâng cấp HTTP (thường dùng cho HTTP/SSL) để "nâng cấp" kết nối HTTP thành kết nối WebSocket. Một số máy chủ proxy không thích điều này và sẽ ngắt kết nối. Do đó, ngay cả khi một ứng dụng nhất định sử dụng giao thức WebSocket, thì bạn vẫn có thể không thiết lập được kết nối. Điều này khiến phần tiếp theo trở nên quan trọng hơn :)

Sử dụng WebSocket ngay hôm nay

WebSocket vẫn là một công nghệ mới và chưa được triển khai đầy đủ trên tất cả trình duyệt. Tuy nhiên, bạn có thể sử dụng WebSocket ngay hôm nay với các thư viện sử dụng một trong những phương án dự phòng nêu trên bất cứ khi nào WebSocket không hoạt động. Một thư viện đã trở nên rất phổ biến trong miền này là socket.io, đi kèm với việc triển khai giao thức của máy khách và máy chủ, đồng thời bao gồm các phương án dự phòng (socket.io chưa hỗ trợ tính năng nhắn tin nhị phân kể từ tháng 2 năm 2012). Ngoài ra còn có các giải pháp thương mại như PusherApp có thể dễ dàng tích hợp vào bất kỳ môi trường web nào bằng cách cung cấp API HTTP để gửi thông báo WebSocket đến máy khách. Do yêu cầu HTTP bổ sung, nên sẽ luôn có thêm hao tổn so với WebSocket thuần tuý.

Phía máy chủ

Việc sử dụng WebSocket tạo ra một mô hình sử dụng hoàn toàn mới cho các ứng dụng phía máy chủ. Mặc dù các ngăn xếp máy chủ truyền thống như LAMP được thiết kế xoay quanh chu kỳ yêu cầu/phản hồi HTTP, nhưng chúng thường không xử lý tốt một số lượng lớn các kết nối WebSocket đang mở. Để duy trì một số lượng lớn kết nối mở cùng một lúc, bạn cần có một cấu trúc nhận được tính đồng thời cao với chi phí hiệu suất thấp. Những cấu trúc như vậy thường được thiết kế dựa trên luồng hoặc IO không chặn.

Triển khai phía máy chủ

Phiên bản giao thức

Giao thức truyền dữ liệu (giao thức bắt tay và chuyển dữ liệu giữa ứng dụng và máy chủ) cho WebSocket hiện là RFC6455. Chrome và Chrome dành cho Android mới nhất hoàn toàn tương thích với RFC6455, bao gồm cả tính năng nhắn tin nhị phân. Ngoài ra, Firefox sẽ tương thích với phiên bản 11, Internet Explorer với phiên bản 10. Bạn vẫn có thể sử dụng các phiên bản giao thức cũ hơn, nhưng không nên vì chúng được biết là dễ bị tấn công. Nếu bạn có các phương thức triển khai máy chủ cho các phiên bản cũ của giao thức WebSocket, bạn nên nâng cấp lên phiên bản mới nhất.

Trường hợp sử dụng

Sử dụng WebSocket bất cứ khi nào bạn cần kết nối có độ trễ thực sự thấp, gần như theo thời gian thực giữa ứng dụng và máy chủ. Hãy nhớ rằng điều này có thể liên quan đến việc xem xét lại cách bạn xây dựng các ứng dụng phía máy chủ, tập trung vào các công nghệ như hàng đợi sự kiện. Sau đây là một số trường hợp sử dụng mẫu:

  • Trò chơi trực tuyến nhiều người chơi
  • Ứng dụng Chat
  • Bảng thông tin thể thao trực tiếp
  • Cập nhật luồng mạng xã hội theo thời gian thực

Bản minh hoạ

Tài liệu tham khảo