深入理解 Socket.io 中的 Room 与 Namespace

房间与命名空间的关系

在 Socket.io 里有房间与命名空间这么两个概念与结构。房间与命名空间都能实现 websocket 的多路复用,但是它们有一定的区别。

当 websocket 连接后,socket 会属于某个房间,还会属于某个命名空间。socket 与 room,namespace 的关系就像是个人,房子,地区的关系。

它们的关系如下图,每个 Namespace 里会有很多 Room,Room 里又会有很多 socket。

命名空间(Namespace)

连接的时候,使用路径名来指定命名空间。在没有指定命名空间下,默认会使用 / 作为命名空间。
如果要想指定命名空间,则需要在客户端指定:/news,这样就指明进入的是 /news 命名空间。

客户端指定命名空间:

1
2
const socket = io('/news');

在服务端里对应的处理,则需要使用 of

1
2
3
4
5
6
7
8
9
10
11
const io = require('socket.io')();

// news 命令空间
const news = io.of('/news');

news.on('connection', function(socket) {
    console.log('someone connected');
});

// 只在本命名空间发送消息
news.emit('hi', 'everyone!');

io 在创建是,它就会被指派到默认的命名空间 /,那么它的广播只限于在 / 里的 socket 才收到,其他空间里是收不到消息的。

1
2
3
4
const io = require('socket.io')();

io.send('!!!');
io.broadcast.emit('!!!');

也可以从默认空间里去到其他空间里发消息:

1
2
3
4
// 去到 /news 里发消息
io.of('/news').send('!!!');
// 去到 /news 里的 room1 房间里发消息
io.of('/news').to('room1').send('!!!');

只有命名空间才有权力去到另一个命名空间发消息:

1
2
3
4
// 下面代码是错误的
io.on('connection', function(socket) {
    socket.of('/news');
});

socket 只能换房间,不能去到另一个命名空间。

在命名空间里广播有些差异性:

1
2
io.sockets.emit('!!!');  // 默认命名空间,广播方式
news.emit('!!!');        // 非默认命名空间,广播方式

在连接时,如果指定了命名空间,也会进入到默认空间里:

1
2
3
4
5
6
7
io.on('connection', (socket) => {
    console.log(`[/ 欢迎] ${socket.id}`);
});

news.on('connection', (socket) => {
    console.log(`[/news 欢迎] ${socket.id}`);
});

上面代码,当一个客户端连接时,出现输出两次欢迎,并且拥有相同的 id。

1
2
[/ 欢迎] qTrAe97j9cNI4VYMAAAA
[/news 欢迎] /video#qTrAe97j9cNI4VYMAAAA

此外连接事件可以多次监听:

1
2
3
4
5
6
7
io.on('connection', (socket) => {
    console.log(`[/ 欢迎1] ${socket.id}`);
});

io.on('connection', (socket) => {
    console.log(`[/ 欢迎2] ${socket.id}`);
});

上面代码输出两次欢迎,并且有先后顺序(1 -> 2)

房间(Room)

对于房间的进入与离开,可以使用 join 与 leave

1
2
3
4
5
6
io.on('connection', function(socket) {
    // 把 socket 扔进 room1 房间里
    socket.join('room1');
    // 再把 socket 赶出 room1 房间
    socket.leave('room1');
});

每个房间只属于某个命名空间,因此可以收听同一个命令空间的消息。而不同的房间之间是隔离的,它们不能接收不同房间的消息。

使用 to/in (它们是一样的)来对某个房间进行广播消息。

1
2
io.to('room1').emit('some event');
socket.to('room1').to('room2').emit('hello');

当连接时,默认会指派到一个唯一的房间,也就是用 socket.id 来命名的房间。这样的做法是让每个 socket 待在自己的房间里不受到其他人影响。

这样可以轻松地向其他 socket 广播消息:

1
2
3
4
5
6
io.on('connection', function(socket) {
    // id 是某个 socket.id,相当于去到他的房间里叫他
    socket.on('say to someone', function(id, msg) {
        socket.broadcast.to(id).emit('my message', msg);
    }
});

socket 可以进入多个房间接收信息,相当于你可以在 QQ 上加入多个群一样。
可以使用 rooms 来查看,当前 socket 所在的房间。

1
2
3
4
5
6
io.on('connection', (socket) => {
    socket.join('room 237', () => {
        let rooms = Objects.keys(socket.rooms);
        console.log(rooms); // [ <socket.id>, 'room 237' ]
    });
});

如何从 url 判断房间与命名空间?

假设 url = /news/room1,其中 news 是命名空间,room1 是房间。
命名空间自动分配不用管,而房间需要我们自己去分配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const io = require('socket.io')();

// news 命令空间
const news = io.of('/news');

news.on('connection', function (socket) {
    // http://127.0.0.1:3000/news/room1
    const url = socket.request.url;
    // 获取房间 id,也就是 room1
    const room = url.match(//w+$/).toString();

    // 进入房间
    socket.join(room, () => {
        let rooms = Objects.keys(socket.rooms);
        console.log(rooms); // [ <socket.id>, 'room1' ]
    });
});

也可以使用 query,这是最简单的方式。

1
2
// http://127.0.0.1:3000/news?r=room1
const room = socket.handshake.query['r'];

轻易实现群聊与私聊

利用命名空间与房间的特性,可以轻易的实现群聊与私聊。

对于群聊,你只需要把每个 socket 扔进同一个房间即可。
对于私聊,你只需要把两个 socket 扔进同一个房间即可。

小结

socket.io 的 API 是对称的,所以适用于服务端的操作也适用于客户端。

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值