一、简介
用socket.io+Node实现一个房间内广播的功能,原本以为API会有类似的功能,虽然目前来看会有比较相近的功能,但是效果依然不尽人意,故自己琢磨了一下该方面的实现过程。
当前使用的包的信息如下:
包/软件 | 版本 | 说明 |
---|---|---|
Node | v12.15.0 | |
socket.io | v3.1.0 | socket服务器 |
socket.io-client | v3.1.0 | socket客户端 |
二、一个基础的连接例子
先上一个最基础的client连接server端的例子。
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
// 监听客户端连接上的事件
io.on('connection', (socket) => {
console.log(`${socket.id} connected`);
});
- client端代码
const io = require('socket.io-client');
const socket = io('http://127.0.0.1:3001');
// client端连接上server端的事件
socket.on('connect', () => {
console.log('client connect');
});
可以看到控制台中:
- server端输出:
mY28QxO5iF557kdBAAAD connected
- client端输出:
client connect
三、普通广播
3.1 全广播
基于上面基础的例子,在server端代码中connection
事件中添加一个广播操作,并且在client端代码中添加对message
事件的监听。
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
// 监听客户端连接上的事件
io.on('connection', (socket) => {
console.log(`${socket.id} connected`);
// 广播
io.sockets.send('This is a broadcast message');
});
- client端代码
const io = require('socket.io-client');
const socket = io('http://127.0.0.1:3001');
// client端连接上server端的事件
socket.on('connect', () => {
console.log('client connect');
});
// 添加对message事件的监听
socket.on('message', (msg) => {
// 打印msg
console.log(`receive message ${msg}`)
});
当client连接上server后,server将会广播一条This is a broadcast message
消息给连接到该server的所有client。大致逻辑如下:
- A、B为客户端,C为服务器
- 当A连接上C后,C将会给A发送一条消息
- 当B连接上C后,C将会给A和B各发送一条消息
3.2 部分广播
上面的全广播代表会将消息发送给每一个连接到server端的client,包括自己。但是某些时候可能只需要发送给一部分client,比如发送除了自己之外的其他client。此时,就需要将所有连接上的client进行遍历,然后依次使用send
方法。
所有的部分广播只有server端代码会有所不同,client端代码依旧保持原样。
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
// 监听客户端连接上的事件
io.on('connection', (socket) => {
console.log(`${socket.id} connected`);
// 获取所有连接上来的client
const sockets = io.sockets.sockets;
// 遍历所有client,依次使用send方法发送,并且根据ID剔除自身
for (client of sockets) {
if (client[0] !== socket.id) {
socket[1].send('this is a broadcast message');
}
}
});
四、Namespace+广播
4.1 全广播
如果想给client划分一下,有个“分组”之类的概念,则可以使用namespace
特性。
之前没有使用namespace
特性时,我们是直接使用的io
,现在使用了namespace
特性后,需要针对特定的namespace
进行操作的话,需要使用io.of('/my_namespace')
。且类似io.sockets
这样的代码也无需加上sockets
flag了。
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
const NAME_SPACE_1 = '/my_namespace';
// 注意这里要使用of+namespace,否则无法监听到client连接到对应namespace事件
io.of(NAME_SPACE_1).on('connection', (socket) => {
console.log(`${socket.id} connected ${NAME_SPACE_1}`);
// 注意此处
io.of(NAME_SPACE_1).send('This is a broadcast message');
});
// 再添加一个namespace
const NAME_SPACE_2 = '/other';
io.of(NAME_SPACE_2).on('connection', (socket) => {
console.log(`${socket.id} connected ${NAME_SPACE_2}`);
// 注意此处
io.of(NAME_SPACE_2).send('This is a broadcast message from' + NAME_SPACE_2);
});
- client端代码
client端代码首要解决任务是连接到对应的namespace中。该namespace需要在连接的url处进行制定。
const io = require('socket.io-client');
const NAME_SPACE_1 = '/my_namespace';
const socket = io('http://127.0.0.1:3001' + NAME_SPACE_1);
// client端连接上server端的事件
socket.on('connect', () => {
console.log('client connect ' + NAME_SPACE_1);
});
// 添加对message事件的监听
socket.on('message', (msg) => {
// 打印msg
console.log(`receive message ${msg}`)
});
同样的,这里也是对连接到对应namespace
下的所有client进行广播。
逻辑如下:
- A、B为客户端,C为服务器
- A连接到C名为
/my_namespace
的namespace - C将会对名为
/my_namespace
下的所有客户端发送信息,包括A - B连接到C名为
/other
的namespace - C将会对名为
/other
下的所有客户端发送信息,包括B,不包括A
4.2 部分广播
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
const NAME_SPACE_1 = '/my_namespace';
// 这里只用一个namespace来进行展示,其余的也是相同的
io.of(NAME_SPACE_1).on('connection', (socket) => {
console.log(`${socket.id} connected ${NAME_SPACE_1}`);
// 获取全部sockets
const sockets = io.of(NAME_SPACE_1).sockets;
// 遍历sockets,
for (client of sockets) {
if (client[0] !== socket.id) {
client[1].send('this is a broadcast message');
}
}
});
五、room+广播
5.1 全广播
如果认为namespace
不是很灵活,不能随时切换分组的话,可以考虑使用room
概念。room
概念的话,允许一个client同时加入多个room
,且允许随时离开、加入,而无需重新建立连接。
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
const ROOM = 'my_room';
// 监听客户端连接上的事件
io.on('connection', (socket) => {
// 首先要让socket加入某一房间
socket.join(ROOM);
console.log(`${socket.id} connected room ${ROOM}`);
// 广播
io.in(ROOM).send('This is a room broadcast message');
});
- client端代码
const io = require('socket.io-client');
const socket = io('http://127.0.0.1:3001')
socket.on('connect', () => {
console.log('client connect')
});
socket.on('message', (data) => {
console.log('receive message ' + data);
});
5.2 部分广播
- server端代码
const { Server } = require('socket.io');
// 将socket服务器监听3001端口
const io = new Server(3001);
const ROOM = 'my_room';
// 监听客户端连接上的事件
io.on('connection', (socket) => {
// 首先要让socket加入某一房间
socket.join(ROOM);
console.log(`${socket.id} connected room ${ROOM}`);
// 获取全部sockets
const sockets = io.in(ROOM).sockets.sockets;
// 遍历
for (client of sockets) {
if (client[0] !== socket.id) {
client[1].send('This is a room broadcast message')
}
}
});
六、小结
以上就是socket.io几种广播的方式,当然也只是普通的广播,还没有做到剔除当前连接对象,只广播给其他的client,这部分内容我将考虑在之后更新上来,欢迎大家继续关注。
总结一下几种广播的区别吧
广播名称 | 特性 |
---|---|
普通广播 | 无法进行分组广播,只要连接上来的都会广播到(其实会和namespace有区分,因为普通广播的namespace为/ ),不过也仅此而已了 |
namespace广播 | 会让client根据自己所要连接的namespace进行分组广播,缺点就是不能灵活切换 |
room广播 | 允许client灵活的变更room,这是优点,也是缺点,在复杂的逻辑环境下,room过于灵活的变更反而不利于管理 |
文章若有不妥之处,欢迎大家斧正~