Từ bài SocketIO đầu tiên helloworld tới nay cũng đã đi hơi xa xa rồi, giờ đã tới bài thứ 3, nay ta tìm hiểu về broadcasting, namespace, room xem nó là cái gì nhé.

Broadcasting

Broadcasting nghĩa là gửi message tới tất cả các client connect. Việc này có thể hoàn thành ở nhiều cấp độ. Chúng ta có thể gửi message tới toàn bộ các client kết nối, các client trên namespace, các client ở trong room cụ thể. Để làm được điều này thì ta phải dùng method là io.sockets.emit.

Lưu ý: Broadcasting đúng với cái tên, nó sẽ gửi event tới TẤT CẢ các client kết nối

Ví dụ, chúng ta sẽ broadcast số lượng client  kết nối tới toàn bộ người dùng với việc sửa file app.js như sau.

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res) {
   res.sendFile(__dirname + '/index.html');
});

var clients = 0;
io.on('connection', function(socket) {
   clients++;
   io.sockets.emit('broadcast',{ description: clients + ' clients connected!'});
   socket.on('disconnect', function () {
      clients--;
      io.sockets.emit('broadcast',{ description: clients + ' clients connected!'});
   });
});

http.listen(3000, function() {
   console.log('listening on localhost:3000');
});

Ở phía client thì ta cần handle broadcast event, sửa file index.html

<!DOCTYPE html>
<html>
   <head>
      <title>Hello world</title>
   </head>
   <script src = "/socket.io/socket.io.js"></script>
   <script>
      var socket = io();
      socket.on('broadcast',function(data) {
         document.body.innerHTML = '';
         document.write(data.description);
      });
   </script>
   <body>Hello world</body>
</html>

Nếu bạn mở 4 tab truy cập vào đường dẫn localhost:3000 thì sẽ có kết quả như sau

Trường hợp trên sẽ gửi event tới mọi người. Nghĩa là cứ mở 1 tab mới lên thì mỗi tab con sẽ hiển thị thông tin giống nhau. Giờ ta điều chỉnh để user mới có thể thấy dòng message chào mừng và update những client khác rằng "tôi đã join vào hội rồi đó ae ơi". Điều chỉnh code với bí kíp socket.broadcast.emit, vẫn sửa file app.js nha anh em

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res) {
   res.sendFile(__dirname + '/index.html');
});

var clients = 0;
io.on('connection', function(socket) {
   clients++;
   socket.emit('newclientconnect',{ description: 'Hey, welcome!'});
   socket.broadcast.emit('newclientconnect',{ description: clients + ' clients connected!'})
   socket.on('disconnect', function () {
      clients--;
      socket.broadcast.emit('newclientconnect',{ description: clients + ' clients connected!'})
   });
});

http.listen(3000, function() {
   console.log('listening on localhost:3000');
});

Kèm theo sửa luôn code html, file index.html

<!DOCTYPE html>
<html>
   <head>
      <title>Hello world</title>
   </head>
   <script src = "/socket.io/socket.io.js"></script>
   <script>
      var socket = io();
      socket.on('newclientconnect',function(data) {
         document.body.innerHTML = '';
         document.write(data.description);
      });
   </script>
   <body>Hello world</body>
</html>

Giờ thì client mới sẽ thấy message chào mừng và những client cũ sẽ thấy có bao nhiêu anh em trong hội kết nối tới server nha.

Hey, welcome!
4 clients connected!

Namespace

Socket.IO cho phép dùng “namespace” vào socket của bạn, nghĩa là có thể gán vào các endpoint hoặc path khác. Việc này rất có ích để tối thiểu số resourse (TCP connections) và cùng một lúc và đồng thời tách biệt quan hệ trong ứng dụng với truyền thông. Nhiều namespaces chia sẻ chung WebSockets connection có thể giúp chúng ta tiết kiệm các port socket trên server.

Namespace mặc định

Root namespace '/' là namespace mặc định, có thể được join bởi client nếu một namespace không được chỉ định bởi client khi kết nối tới server. Tất cả các kết nối tới server sử dụng socket-object client được tạo namespace mặc định. Ví dụ

var socket = io();

Lệnh trên sẽ kết nối client tới namespace mặc định, tất cả event trên namespace này sẽ kết nối và được handle bởi  io object trên server. Tất cả các ví dụ từ trước tới nay ta đều dùng namespace mặc định nha.

Custom namespace

Chúng ta có thể tạo namespace custom của riêng mình, để thiết lập thì ta gọi hàm of của server, thay đoạn code app.js như sau

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res) {
   res.sendFile(__dirname + '/index.html');
});

var nsp = io.of('/my-namespace');
nsp.on('connection', function(socket) {
   console.log('someone connected');
   nsp.emit('hi', 'Hello everyone!');
});

http.listen(3000, function() {
   console.log('listening on localhost:3000');
});

Giờ để connect client tới namespace này thì bạn cần cung cấp cho nó một argument tới io constructor call để tạo ra kết nối và socket object ở client.

Chỉnh sửa file index.html để kết nối tới các namespace trên như sau

<!DOCTYPE html>
<html>
   <head>
      <title>Hello world</title>
   </head>
   <script src = "/socket.io/socket.io.js"></script>
   
   <script>
      var socket = io('/my-namespace');
      socket.on('hi',function(data) {
         document.body.innerHTML = '';
         document.write(data);
      });
   </script>
   <body></body>
</html>

Mỗi khi ai đó kết nối vào namespace này thì sẽ nhận được event là 'hi'

#Client
Hello everyone!
#Server
someone connected
someone connected
someone connected
someone connected

Room

Join room

Bạn có thể gọi method join trên socket để subscribe socket vào channel/room. Ví dụ: ta sẽ tạo room có tên là 'room-<room-number>' và join một số client. Nếu room đầy thì tạo một room khác để cho các client join vào tiếp.

Lưu ý: Chúng ta làm việc này trên cùng namespace mặc định '/'. Bạn có thể implement nó trên custom namespace với cách tương tự

Để join vào một room thì cần cung cấp tên room và argument trong hàm join như sau

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res) {
   res.sendFile(__dirname + '/index.html');
});

var roomno = 1;
io.on('connection', function(socket) {
   
   //Increase roomno 2 clients are present in a room.
   if(io.nsps['/'].adapter.rooms["room-"+roomno] && io.nsps['/'].adapter.rooms["room-"+roomno].length > 1) roomno++;
   socket.join("room-"+roomno);

   //Send this event to everyone in the room.
   io.sockets.in("room-"+roomno).emit('connectToRoom', "You are in room no. "+roomno);
})

http.listen(3000, function() {
   console.log('listening on localhost:3000');
});

Điều chỉnh file html để handle event connectToRoom trên client.

<!DOCTYPE html>
<html>
   <head>
      <title>Hello world</title>
   </head>
   <script src = "/socket.io/socket.io.js"></script>
   
   <script>
      var socket = io();
      socket.on('connectToRoom',function(data) {
         document.body.innerHTML = '';
         document.write(data);
      });
   </script>
   <body></body>
</html>

Giờ nếu bạn kết nối với 3 client, 2 cái đầu tiên sẽ có message như sau

You are in room no. 1
You are in room no. 2

Leave room

Để rời khỏi room thì bạn cần gọi hàm leave khi bạn gọi hàm join vào socket. Ví dụ ta leave room  'room-1' thì sẽ dùng lệnh

socket.leave("room-"+roomno);

Chương trình mẫu cho anh em đây

hocarm/nodejs-socketio-tutorial
Contribute to hocarm/nodejs-socketio-tutorial development by creating an account on GitHub.

Kết

Vậy là ta đã tìm hiểu được các vận dụng broadcasting, viết được namespace custome và join/leave room, còn bài nữa là tạo app chat, anh em xem post tiếp theo nha.