WebSocket là gì?

Hiện nay, WebSocket được biết đến rộng rãi như một giao thức phổ biến trong các ứng dụng thời gian thực, nhằm trao đổi thông tin trực tuyến hai chiều và liên tục giữa client và server, client và client.
Với Socket.IO là công cụ nổi tiếng và hỗ trợ mạnh mẽ giao thức WebSocket với các tính năng nổi bật. Trong bài viết hôm nay chúng ta sẽ cùng tìm hiểu giao thức WebSocket cũng như những kiến thức cơ bản về thư viện Socket.IO.

1. Giao tiếp thời gian thực

1.1 Half duplex

Với giao thức truyền thống HTTP/1.0 và HTTP/1.1 trong mô hình client - server, client gửi một yêu cầu HTTP đến server, server xử lý và gửi kết quả trả về cho client, bao gồm trang HTML cũng như các thông tin liên quan. HTTP/1.0 đã đủ để thực hiện một yêu cầu lấy tài liệu từ một server.
Với HTTP/1.1 thêm vào các kết nối tái sử dụng, trình duyệt có thể khởi tạo một kết nối đến một server web để lấy các trang HTML, sau đó sử dụng cùng một kết nối để lấy nguồn tài nguyên như hình ảnh, chữ viết, và như vậy. HTTP / 1.1 giảm độ trễ giữa yêu cầu bằng cách giảm số lượng các kết nối đã được thực hiện từ các client đến các server.
Về bản chất, HTTP cũng là half-duplex, có nghĩa là lưu lượng truyền tin theo một hướng duy nhất tại một thời điểm, điều này gây lãng phí và kém hiệu quả.
A
half-duplex

1.2 Full duplex

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 có thể kể đến như
  • AJAX LONG-POLLING
  • SERVER SENT EVENTS (SSE)
  • COMET
  • WEBSOCKET
Và các công nghệ yêu cầu "thời gian thực" hoặc gần như "thời gian thực" hiện nay thì half-duplex không còn thể đáp ứng. Cho đến hiện tại, WEBSOCKET dạng full-duplex đươc tích hợp trong HTML 5 đang trở lên chiếm ưu thế tuyệt đối.
A
full-duplex

2. WebSocket

2.1 WebSocket là gì?

notion image
Giao thức WebSocket là một tiêu chuẩn mở được hỗ trợ rộng rãi để phát triển các ứng dụng thời gian thực. Các phương pháp trước đây để mô phỏng kết nối song công hoàn toàn dựa trên Polling, một phương pháp đồng bộ trong đó client đưa ra yêu cầu tới server để xem liệu có bất kỳ thông tin nào có sẵn hay không.
Tính năng Polling hoạt động tốt trong trường hợp biết chính xác khoảng thời gian có sẵn của tin nhắn. Tuy nhiên, trong hầu hết các ứng dụng thời gian thực, tần suất tin nhắn thường không thể đoán trước được. Ngoài ra, polling yêu cầu client mở và đóng nhiều kết nối không cần thiết.
Long Polling (còn được gọi là Comet) là một phương thức liên lạc phổ biến khác trong đó client mở kết nối với server trong một khoảng thời gian nhất định. Nếu server không có bất kỳ thông tin nào, nó sẽ giữ yêu cầu mở cho đến khi có thông tin hoặc hết thời hạn được chỉ định (hết thời gian chờ).
Về cơ bản, Comet trì hoãn việc hoàn thành phản hồi HTTP cho đến khi server có thứ gì đó cần gửi cho client, một kỹ thuật thường được gọi là hanging-GET hoặc pending-POST.
Việc client phải liên tục kết nối lại với server để có thông tin mới khiến Long Polling trở thành một lựa chọn khá tệ đối với nhiều ứng dụng thời gian thực.
notion image
WebSocket là một giao thức truyền thông máy tính (computer communication protocol), cung cấp các kênh liên lạc dạng full-duplex (song công) qua một kết nối TCP. Giao thức WebSocket   hiện nay được bao hàm ngay ở mục Connectivity trong đặc tả của HTML5.
WebSocketis a computer communications protocol, providing full-duplex communication channels over a single TCP connection.Nguồn: WebSocket - Wikipedia
Sử dụng WebSocket, bạn có thể tạo ra những ứng dụng realtime thật sự như chat, chỉnh sửa tài liệu online (ví dụ Google docs), giao dịch hoặc game online nhiều người chơi...

2.2 WebSocket API

2.2.1 Handshake

Khi tạo kết nối WebSocket, bước đầu tiên clientserver cần làm là bắt tay (handshake) qua TCP, qua đó clientserver đồng ý sử dụng giao thức WebSocket.
Handshake từ phía client có dạng như sau:
Handshake từ phía server có dạng như sau:
Để 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 một chuỗi cố định quy định bởi giao thức WebSocket258EAFA5-E914-47DA-95CA-C5AB0DC85B11 vào Sec-WebSocket-Key để được chuỗi mới là dGhlIHNhbXBsZSBub25jZQ==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 s3pPLMBiTxaQ9kYGzzhZRbK+xOo= .
  • 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.
Sau khi handshake được thiết lập, API WebSocket cho phép ứng dụng của bạn kiểm soát giao thức WebSocket và phản hồi các sự kiện do server kích hoạt. Vì API hoàn toàn được điều khiển theo sự kiện nên khi kết nối song công hoàn toàn được thiết lập.
Khi server có dữ liệu cần gửi cho client hoặc nếu tài nguyên mà ứng dụng đang giám sát thay đổi trạng thái, nó sẽ tự động gửi dữ liệu hoặc thông báo. Với API hướng sự kiện, không cần phải thăm dò server để biết trạng thái cập nhật nhất của tài nguyên được nhắm mục tiêu.
notion image

2.2.2 Tạo một kết nối WebSocket

Để kết nối với server từ xa, hãy tạo một phiên bản đối tượng WebSocket mới và cung cấp cho đối tượng mới URL của điểm cuối đích (endpoint).
Kết nối WebSocket được thiết lập bằng cách nâng cấp từ giao thức HTTP lên Giao thức WebSocket trong quá trình bắt tay ban đầu giữa clientserver, qua cùng một kết nối TCP cơ bản.
Tiêu đề nâng cấp Upgrade được bao gồm trong yêu cầu này để thông báo cho server rằng client muốn thiết lập kết nối WebSocket. Sau khi thiết lập kết nối thành công, các tin nhắn WebSocket sẽ được gửi và nhận bằng phương pháp được định nghĩa bởi giao diện WebSocket.
Để tạo kết nối, hãy gọi hàm tạo WebSocket của Javascript, hàm này trả về đối tượng phiên bản kết nối. Sau đó bạn có thể lắng nghe các sự kiện trên đối tượng đó. Những sự kiện này được kích hoạt khi kết nối mở hoặc đóng, có tin nhắn đến hoặc xảy ra lỗi.
Hàm tạo WebSocket lấy một đối số bắt buộc URL. Ngoài ra có thêm một số đối số tùy chọn chỉ định một giao thức:
  • url - bắt buộc: đường dẫn URL mà bạn muốn thiết lập kết nối
  • protocols tuỳ chọn: một string hoặc mảng string protocol. Những string này để chỉ định sub-protocol, từ đó một server có thể áp dụng nhiều sub-protocol WebSocket (ví dụ một server có thể xử lý những loại tương tác khác nhau tuỳ thuộc vào protocol được chỉ định)

2.2.3 WebSocket Events

Bản chất không đồng bộ của WebSocket có nghĩa là miễn là kết nối WebSocket được mở, ứng dụng có thể lắng nghe các sự kiện.
Để bắt đầu lắng nghe các sự kiện, hãy thêm các hàm gọi lại vào đối tượng WebSocket hoặc sử dụng phương thức DOM addEventListener() để thêm trình xử lý sự kiện vào đối tượng WebSocket.
Một đối tượng WebSocket có thể gửi 4 sự kiện sau đây:
  • Open: Server phản hối yêu cầu kết nối WebSocket. Nó cho biết handshake đã được thực hiện và kết nối đã thiết lập. Callback cho sự kiện này là onopen.
  • Message: Xảy ra khi nhận data thông qua WebSocket. Callback để gọi sự kiện này là onmessage.
  • Error: Xảy ra khi kết nối với WebSocket đóng bởi vì một lỗi nào đó (ví dụ data không thể gửi). Callback tương ứng là onerror.
Chú ý: khi xảy ra error cũng có thể đóng kết nối WebSocket.
  • Close: Xảy ra khi kết nối WebSocket đóng. Callback tương ứng là onclose.

2.2.4 WebSocket Methods

WebSocket cung cấp hai phương thức là:
  • send(): phương thức socket.send(data) vận chuyển data thông qua kết nối WebSocket. Nếu vì lý do nào đó mà kết nối không khả thi hoặc kết nối đóng, thì sẽ trả về exception về trạng thái kết nối không hợp lệ.
  • close(): công cụ socket.close() được dùng để ngắt kết nối đang tồn tại. Hoặc khi kết nối đã đóng rồi thì sẽ không làm gì cả. Sử dụng với 2 tham số: code (mã trạng thái), reason (chuỗi văn bản giải thích).

2.2.5 Thuộc tính của đối tượng WebSocket

Một đối tượng kết nối WebSocket cũng có những thuộc tính sau:
  • url (read-only): Trả về URL được thiết lập khi khởi tạo.
  • readyState (read-only): Thể hiện trạng thái của kết nối, có thể có các giá trị sau đây:
- 0: kết nối đang tiến triển và vẫn chưa được thiếp lập.
- 1: kết nối được thiết và có thể gửi tin nhắn giữa clientserver.
- 2: kết nối đang được xử lý closing handshake.
- 3: kết nối đang đóng hoặc không thể mở.
  • bufferedAmount (read-only): Thể hiện số byte của văn bản UTF-8 đang ở hàng đợi của phương thức send(). Ví dụ dưới đây áp dụng thuộc tính này để đảm bảo tin nhắn được gửi khi buffer chưa đầy:
  • protocol (read-only): Trả về sub-protocol được chọn bởi server, xuất hiện từ những protocol được liệt kê trong tham số protocols khi khởi tạo đối tượng WebSocket. Trả về chuỗi rỗng nếu kết nối không được thiết lập.
  • binaryType: Kiểm soát kiểu dữ liệu nhị phân nhận được từ kết nối WebSocket. Có 2 giá trị là blob (giá trị mặc định) và arraybuffer.

3. Socket.IO

3.1 Socket.IO là gì?

notion image
Socket.IO là một thư viện cung cấp kết nối giữa clientserver với độ trễ thấp, hai chiều và hoạt động trên hướng sự kiện.
Socket.IO is a library that enables
low-latency
bidirectional
event-based
client
server
Kết nối Socket.IO có thể được thiết lập với các phương thức vận chuyển cấp thấp khác nhau:
  • HTTP long-polling
  • WebSocket
  • WebTransport
Socket.IO chọn loại vận chuyển có sẵn hợp lý nhất dựa trên:
  • Mạng (một số mạng chặn kết nối WebSocket hoặc WebTransport)

3.2 Những sai lầm về Socket.IO

Socket.IO không phải là một áp dụng của WebSocket
Mặc dù thực tế Socket.IO sẽ sử dụng WebSocket để vận chuyển khi có thể, nhưng có thêm metadata vào mỗi gói tin. Vì lẽ đó không thể kết nối Socket.IO client đến WebSocket server và ngược lại là WebSocket client đến Socket.IO server.
Lưu ý
  • Socket.IO không được dùng cho dịch vụ nền của ứng dụng điện thoại.
  • Thư viện Socket.IO luôn giữ cho kết nối TCP đến server mở, điều này có thể tiêu thụ nhiều pin cho người dùng.
  • Có thể sử dụng nền tảng nhắn tin chuyên dụng như FCM cho trường hợp sử dụng này.

3.3 Tính năng của Socket.IO

Dưới đây là các tính năng được cung cấp bởi Socket.IO trên WebSocket đơn giản:

3.3.1 Dự phòng HTTP long-polling

Kết nối sẽ tự động trở về HTTP long-polling trong trường hợp không thể thiết lập kết nối WebSocket.
Đây là tính năng hàng đầu để người dùng sử dụng Socket.IO suốt 10 năm qua khi mà trước đây chưa có nhiều trình duyệt hỗ trợ cho WebSocket.
Ngay cả khi hầu hết các trình duyệt hiện nay đều hỗ trợ WebSocket (hơn 97%), đây vẫn là một tính năng tuyệt vời vì chúng tôi vẫn nhận được báo cáo từ người dùng rằng không thể thiết lập kết nối WebSocket vì họ sử dụng một số proxy bị định cấu hình sai.

3.3.2 Tự động kết nối lại

Trong một số điều kiện cụ thể, kết nối WebSocket giữa serverclient có thể bị gián đoạn mà cả hai bên đều không biết về trạng thái liên kết bị hỏng.
Đó là lý do tại sao Socket.IO bao gồm cơ chế heartbeat, kiểm tra định kỳ trạng thái kết nối.
Và khi client cuối cùng bị ngắt kết nối, nó sẽ tự động kết nối lại với độ trễ chờ tăng dần theo theo cấp số nhân, để không làm server bị quá tải.

3.3.3 Packet buffering

Các gói được tự động lưu vào bộ đệm khi client bị ngắt kết nối và sẽ được gửi khi kết nối lại.
Mặc dù hữu ích trong hầu hết các trường hợp (khi độ trễ kết nối lại ngắn), nhưng nó có thể dẫn đến một lượng lớn các sự kiện khi kết nối được khôi phục.
Có một số giải pháp để ngăn chặn hành vi này, tùy thuộc vào trường hợp sử dụng của bạn:
  • sử dụng thuộc tính connected của phiên bản Socket
  • sử dụng volatile events

3.3.4 Acknowledgements

Socket.IO cung cấp một cách thuận tiện để gửi sự kiện và nhận phản hồi:
Sender
Receiver
Có hỗ trợ thêm Timeout

3.3.5 Broadcasting

Từ phía server, bạn có thể gửi tin nhắn / sự kiện đến tất cả các client hoặc một nhóm client được chỉ định. Ví dụ:

3.3.6 Multiplexing

Những namespaces cho phép bạn phân chia logic của ứng dụng chỉ trong 1 kết nối. Điều này hữu ích cho trường hợp bạn phân quyền admin cho những người dùng được uỷ quyền

4. Tổng kết

Chúng ta đã tìm hiểu về cách WebSocket cung cấp một cách tiếp cận hai chiều và liên tục giữa máy khách và máy chủ, là một thay thế hữu ích cho long-polling trong HTML5. WebSocket mở ra nhiều khả năng mới cho việc phát triển những ứng dụng web hiện đại hai chiều, nhiều người dùng trong thời gian thực.
Tuy nhiên, việc sử dụng WebSocket cơ bản có thể phức tạp và đầy thách thức. Đó là lý do tại sao thư viện Socket.IO được tạo ra - để giúp đơn giản hóa việc triển khai và quản lý kết nối WebSocket với nhiều tính năng nổi bật được hỗ trợ tự động.
Socket.IO không chỉ hỗ trợ WebSocket mà còn giả lập các sự kiện và nhiều tính năng hữu ích khác như dự phòng kết nối HTTP long polling khi có sự cố với WebSocket và hỗ trợ kết nối lại cách tự động. Vì thế ngày nay Socket.IO là một lựa chọn tối ưu trong việc phát triển ứng dụng với WebSocket trở nên dễ dàng hơn.

5. Tài liệu tham khảo

Loading Comments...

Follow me @kevinbkdev

Donate to me
Bank QR

Bank QR Code

Buy me a coffee
Buy Me A Coffee

@source-blog by @thanhledev

Thứ 7 (24-12) lúc 9 giờ sáng mình có buổi workshop nhỏ chia sẻ cách viết Smart Contract dùng Solidity, target là chỉ cần biết code là làm được.
Nếu bạn hứng thú hãy tham gia nhé!