Giới thiệu
Trong một ứng dụng thời gian thực, bạn hiếm khi làm việc với WebSockets ở chế độ độc lập. Các client cần các tin nhắn nhắm mục tiêu (gửi tới một socket cụ thể) cũng như các sự kiện phát (broadcast) tới mọi client được kết nối. Trên hết, hầu hết các sản phẩm thực tế vẫn expose một REST API — ví dụ, khi người dùng gửi một biểu mẫu qua HTTP, bạn muốn rằng sự thay đổi đó xuất hiện ngay lập tức trong mọi phiên WebSocket đang mở. Bài học này đi đúng vào vấn đề đó: cách gửi và phát các sự kiện từ máy chủ Node.js, và cách kết nối một REST endpoint với lớp WebSocket của bạn để cả hai bề mặt đồng bộ với nhau.
Gửi tin nhắn tới một client so với phát broadcast
Mỗi socket được kết nối có một id duy nhất được gán bởi máy chủ. Bạn có thể dùng id đó để gửi tin nhắn riêng tư:
Nếu bạn muốn mọi client được kết nối nhận cùng một payload, bạn phát broadcast trên namespace gốc — không cần đối số target:
Còn có `socket.broadcast.emit`, thứ gửi tới mọi người trừ chính socket kích hoạt sự kiện. Điều này hữu ích trong các phòng trò chuyện nơi bạn không muốn phát echo tin nhắn trở lại cho người gửi.
Kết nối REST endpoint với các sự kiện WebSocket
Mẫu chung là: trình xử lý REST thực hiện công việc của nó (ghi vào cơ sở dữ liệu, xác thực đầu vào, v.v.), sau đó yêu cầu lớp WebSocket đẩy một cập nhật. Dưới đây là một ví dụ tối giản dùng Express cùng với Socket.IO:
Máy khách HTTP nhận đơn hàng được tạo trong REST response của nó, và mọi tab trình duyệt kết nối qua WebSocket nhận sự kiện `order:new` cùng lúc. Một lần ghi, hai bề mặt.
Gắn mục tiêu tới các phòng (rooms) cụ thể
Khi bạn cần kiểm soát tinh vi hơn "toàn bộ" hay "một socket" thì hãy dùng rooms. Một room là một kênh có tên mà các socket tham gia hoặc rời bỏ:
`socket.to("orders")` tới mọi socket trong room đó ngoại trừ người gửi. `io.to("orders").emit(...)` tới mọi người trong room kể cả người gửi. Rooms là cách chuẩn để triển khai các feed theo người dùng, theo đội, hoặc theo chủ đề mà không viết logic định tuyến tùy chỉnh.
Những sai lầm phổ biến
Gọi `io.emit` bên trong handler REST là được, nhưng đừng quên rằng phản hồi REST và phát WebSocket là độc lập — nếu lớp WebSocket gặp sự cố, yêu cầu HTTP vẫn trả về thành công. Đối với các luồng quan trọng, bọc broadcast trong try/catch và log lỗi. Một lỗi khác thường gặp là trộn `socket.emit` (gửi chỉ tới socket hiện tại) với `io.emit` (gửi toàn cầu) và giả sử chúng cùng hoạt động như nhau. Chọn cái phù hợp với ý định của bạn. Cuối cùng, tuyệt đối không tin vào `socket.id` được gửi từ client — luôn tra cứu tại máy chủ từ phiên xác thực trước khi gửi dữ liệu riêng tư.
Tóm tắt
Bạn có ba chế độ phân phối: `socket.emit` cho client hiện tại, `io.emit` cho phát sóng toàn cầu, và rooms cho nhóm mục tiêu. Ghép một handler REST với `io.emit` (hoặc `io.to(room).emit`) là mẫu cầu nối chuẩn để giữ các bề mặt HTTP và WebSocket nhất quán. Giữ broadcast bên trong handler REST sau logics kinh doanh chính, xử lý lỗi WebSocket một cách linh hoạt, và luôn xác thực danh tính ở phía máy chủ trước khi gửi payload riêng tư.
Bài kiểm tra cuối bài
Ôn lại kiến thức, thử sức và nhận góp ý cho bài làm.