Vấn đề: Kết nối máy khách-máy chủ và máy chủ-máy khá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 tải một trang web lên 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 giúp web trở nên linh động hơn. Tuy nhiên, tất cả hoạt động giao tiếp HTTP đều do ứng dụng điều hướng, đòi hỏi người dùng tương tác hoặc thăm dò ý kiến đị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 máy khách ngay khi máy chủ biết rằng dữ liệu mới có sẵn đã được sử dụng được khá lâu. Các phiên bản 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 ảo giác về kết nối do máy chủ khởi tạo được gọi là tính năng thăm dò ý kiến dài hạn. Với tính năng thăm dò ý kiến dài hạn, ứng dụng sẽ mở một kết nối HTTP đến máy chủ và giữ kết nối đó 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 liên quan đến Flash, yêu cầu XHR nhiều phần và cái gọi là htmlfiles). Tính năng thăm dò ý kiến dài hạn và các kỹ thuật khác hoạt động khá hiệu quả. Bạn sử dụng các biểu tượng này hằng ngày trong các ứng dụng như tính năng trò chuyện của Gmail.
Tuy nhiên, tất cả các giải pháp này đều có chung một vấn đề: Chúng gây ra hao tổn cho HTTP, khiến các giải pháp này 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 WebSocket: Đưa ổ cắm vào web
Thông số kỹ thuật WebSocket xác định một API thiết lập kết nối "ổ cắm" 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ủ và 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 có thể 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:
để kết nối WebSocket bảo mật giống như cách https:
được dùng cho các kết nối HTTP bảo mật.
Việc đính kèm một số trình xử lý sự kiện ngay lập tức 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 được thông báo đến hoặc xảy ra lỗi.
Đối số thứ hai chấp nhận các giao thức phụ không bắt buộc. Đó 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 con và máy chủ chỉ chấp nhận một trong các giao thức con đã 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 các tên giao thức phụ đã đăng ký trong Cơ sở dữ liệu đăng ký của IANA. Hiện tại, chỉ có một tên giao thức phụ (soap) được đăng ký kể từ 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);
};
Giao tiếp 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 điệp 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ự như vậy, máy chủ có thể gửi thông báo cho chúng tôi bất kỳ lúc nào. Bất cứ khi nào điều này xảy ra, lệnh gọi lại onmessage
sẽ kích hoạt. Lệnh gọi lại sẽ 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. Khung nhị phân có thể được nhậ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 phải căn 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 khác của WebSocket là tiện ích. Khi sử dụng tiện ích, bạn sẽ có thể gửi khung nén, đa thành phần, 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ở. Kể từ tháng 2 năm 2012, chưa có thông số kỹ thuật chính thức nào về tiện ích.
// Determining accepted extensions
console.log(connection.extensions);
Giao tiếp nhiều nguồn gốc
Là một giao thức hiện đại, tính năng giao tiếp trên nhiều nguồn gốc được tích hợp ngay vào WebSocket. Mặc dù bạn vẫn phải đả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 khách hay chỉ cung cấp cho những ứng dụng khách nằm trong 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 có 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 dàn xếp các kết nối HTTP trong hầu hết các mạng của 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 lên kết nối WebSocket. Một số máy chủ proxy không thích tính năng này và sẽ làm mất kết nối. Do đó, ngay cả khi một ứng dụng nhất định sử dụng giao thức WebSocket, ứng dụng đó vẫn có thể không thiết lập được kết nối. Điều này làm cho phần tiếp theo thậm chí cò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 đủ trong tất cả cá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 các phương thức dự phòng nêu trên bất cứ khi nào không có WebSocket. Một thư viện đã trở nên rất phổ biến trong lĩnh vực này là socket.io, đi kèm với một ứng dụng và một máy chủ triển khai giao thức, đồng thời bao gồm cả các phương án dự phòng (socket.io chưa hỗ trợ 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. Bạn có thể dễ dàng tích hợp các giải pháp này 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 ứng dụng khách. Do yêu cầu HTTP bổ sung, 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ẫu 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ế xung 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 kết nối WebSocket đang mở. Để duy trì một 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 có khả năng đồng thời cao với chi phí hiệu suất thấp. Các kiến trúc như vậy thường được thiết kế xung quanh luồng hoặc được gọi là IO không chặn.
Triển khai phía máy chủ
- Node.js
- Java
- Ruby
- Python
- Erlang
- C++
- .NET
Phiên bản giao thức
Giao thức dây (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ả thông báo 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ũ nhưng không nên dùng vì chúng đã được xác định là dễ bị tấn công. Nếu đã triển khai máy chủ cho phiên bản cũ hơn của giao thức WebSocket, bạn nên nâng cấp giao thức này 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 độ trễ thực sự thấp, gần như theo thời gian thực giữa ứng dụng và máy chủ. Xin lưu ý rằng việc 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ệ mới như hàng đợi sự kiện. Sau đây là một số ví dụ về trường hợp sử dụng:
- Trò chơi trực tuyến nhiều người chơi
- Ứng dụng nhắn tin
- Kênh thể thao trực tiếp
- Cập nhật luồng nội dung xã hội theo thời gian thực
Bản thu thử
- Plink
- Paint With Me
- Pixelatr
- Đã xác thực
- Trò chơi ô chữ trực tuyến nhiều người chơi
- Máy chủ ping (được dùng trong các ví dụ ở trên)
- Mẫu HTML5demos