Websocket là một chủ đề mà rất nhiều bạn học hay làm việc với ReactJS rất quan tâm vì đây là một chủ đề thú vị và không dễ. Với HTTP chúng ta lập trình tương đối dễ dàng vì đây là giao thức ngắt kết nối, nghĩa là chúng ta gửi đi yêu cầu và đợi kết quả nhận về thì kết nối đã bị ngắt với server rồi, còn với websocket client sẽ luôn phải kết nối đến server dẫn đến việc quản lý trạng thái phức tạp hơn. Trong bài này chúng ta sẽ cùng nhau tìm hiểu về websocket trước nhé.

Hiểu về HTTP trước

HTTP trên thực tế là một giao thức đã được thiết kế từ những năm 1991, nghĩa là đã rất lâu đời rồi, dù nó có những thiếu sót nhất định thì cũng đã được sử dụng rộng rãi đến mức không còn khả năng thay thế mà chỉ có khả năng mở rộng thêm các tính năng cho giao thức HTTP này mà thôi.

Các bước giao tiếp

Về cơ bản thì với HTTP, client và server sẽ khởi tạo kết nối, bắt tay, xử lý yêu cầu và ngắt kết nối luôn, điều này giúp cho server được giải phóng tài nguyền một cách nhanh chóng tuy nhiên cũng phải đánh đổi bằng việc lặp đi lặp lại quá trình kết nối và bắt tay rất phức tạp nên giao thức HTTP tương đối chậm.

Cấu trúc gói tin

Một gói tin HTTP bao gồm các thành phần chính sau:

  1. Request Line: Phần này có trong Request. Nó bao gồm:
  • Phương thức: GET, POST, PUT, DELETE, …
  • URL: Đường dẫn đến resource.
  • Phiên bản của giao thức HTTP (ví dụ: HTTP/1.1, HTTP/2).
    Ví dụ:
GET / HTTP/1.1
  1. Status Line: Phần này có trong response. Nó bao gồm:
  • Phiên bản HTTP.
  • Status code: Thường được dùng để đại diện cho kết quả, ví dụ 200 (thành công), 404 (không tìm thấy), 500 (lỗi máy chủ).
  • Mô tả trạng thái: Một thông báo văn bản mô tả mã trạng thái.
    Ví dụ:
HTTP/1.1 200 OK
  1. Các header: Chứa thông tin metadata liên quan đến request hoặc response. Một số header phổ biến bao gồm:
  • Host: Tên miền của server.
  • Content-Type: Loại dữ liệu (ví dụ: text/html, application/json), thường ngày nay hay dùng html cho trang web và json cho API.
  • Content-Length: Kích thước của nội dung.
  • Authorization: Thông tin xác thực (nếu có).
  • User-Agent: Thông tin về trình duyệt hoặc ứng dụng gửi yêu cầu.
    Ví dụ:
access-control-allow-credentials: true
content-encoding: gzip
content-type: text/html; charset=utf-8()
  1. Body: Chứa dữ liệu chính của gói tin, thường được dùng trong các yêu cầu POST, PUT, PATCH, hoặc trong response khi server trả lại nội dung (như HTML, JSON), đối với yêu cầu GET, phần này thường trống.
    Ví dụ:
{
 "username": "john_doe",
 "password": "123456"
}

Hiểu về websocket

Ngày nay ai trong chúng ta cũng sử dụng các ứng dụng chat realtime (thời gian thực) đúng không? Vậy nếu sử dụng HTTP có được không? Câu trả lời là không vì khi chúng ta gửi tin nhắn đi thì sau đó client sẽ ngắt kết nối đến server và khi người khác gửi tin nhắn cho chúng ta thì không được. Cũng có một cách đó là cứ vài giây lại gọi lên server một lần để kiểm tra có tin nhắn mới hay không, tuy nhiên như đã nói ở trên thì chi phí khởi tạo kết nối của HTTP là đắt đỏ sẽ làm tiêu tốn tài nguyên máy chủ cực kỳ nhiều, lại thêm mấy giây mới gọi một lần thì giữa các tin nhắn có độ trễ, vậy nên cần một giao thức mới ra đời cho phép client và server giữ kết nối liên tục với nhau để có thể gửi và nhận tin nhắn nhanh nhất có thể, và đó chính là websocket.

Các bước kết nối

Do HTTP đã quá phổ biến nên không thể tạo ra hẳn một giao thức mới được nữa nên websocket ra đời đã dựa trên HTTP, nghĩa là nó tận dụng lại các bước khởi tạo kết nối của HTTP sau đó giữ kết nối chứ không ngắt đi.

  1. Client và server vẫn kết nối như HTTP.
  2. Ở bước bắt tay thì sẽ làm thêm 1 việc đó là client và server trao đổi với nhau header Connection: upgrade để hai bên hiểu rằng sẽ nâng cấp kết nối lên thành websocket và giữ kết nối với nhau.
  3. Client và server trao đổi thông tin với nhau mà không ngắt kết nối.
  4. Kết nối chỉ bị ngắt khi một trong hai bên chủ động ngắt kết nối.
    Ví dụ thực tế:

Cấu trúc gói tin

So với HTTP thì cấu trúc gói tin của websocket phức tạp và khó hiểu hơn, trên thực tế thì bạn sẽ không cần quan tâm chi tiết quá vậy nên mình cũng sẽ giải thích đơn giản thôi:

  1. Trong một gói tin có các bit được sử dụng để làm cờ ví dụ 1 bit FIN dùng để chỉ rằng đây là gói tin cuối cùng trong một message, cá nhân mình thấy các cờ này hơi dư thừa, nhưng có lẽ cũng nên có để làm mục đích dự trữ, mở rộng về sau này nếu cần.
  2. Có các byte để lưu kích thước của gói tin để thuận tiện cho client và server khởi tạo bộ nhớ đệm và kiểm soát bộ nhớ một cách tốt nhất.
  3. Có các byte để lưu dữ liệu của gói tin.
  4. Nhiều gói tin ghép lại với nhau có thể trở thành một message hoàn chỉnh.
    Bạn có thể tìm hiểu thêm thông tin về gói tin của websocket tại đây.
    Bạn có thể hiểu đơn giản là vì client và server giữ kết nối, khi giao tiếp thì các message có thể được chia ra thành nhiều gói tin ở đầu gửi và được ghép nối lại ở đầu nhận.

Các websocket server

Trong loạt bài về ReactJS và websocket mình sẽ giới thiệu với bạn 2 websocket server đó là:

  1. ezyfox server: Một socket server hỗ trợ cả websocket, TCP lẫn UDP được phát triển và duy trì bởi tổ chức Young Monkeys Việt Nam.
  2. socket.io với NodeJS: Được phát triển và duy trì bởi Socket IO.

Tổng kết

Như vậy chúng ta đã cùng nhau tìm hiểu về hai giao thức HTTP và websocket.


Cám ơn bạn đã quan tâm đến bài viết này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a