房间
sockets可以join
和 leave
房间。它可用于向一部分客户端广播事件:
信息
请注意,房间是一个仅限服务器的概念(即客户端无权访问它已加入的房间列表)。
加入和离开
您可以调用join
以将socket订阅到给定的频道:
io.on("connection", (socket) => {
socket.join("some room");
});
然后在广播或发射时简单地使用to
或 in
(它们是相同的):
io.to("some room").emit("some event");
您可以同时发射到多个房间:
io.to("room1").to("room2").to("room3").emit("some event");
在这种情况下,将执行联合:至少在其中一个房间中的每个socket都将获得一次事件(即使socket在两个或更多房间中)。
您还可以从给定的socket广播到房间:
io.on("connection", (socket) => {
socket.to("some room").emit("some event");
});
在这种情况下,房间中除发送者之外的每个socket都会收到该事件。
要离开频道,您调用leave
的方式与join
相同。
默认房间
Socket.IO 中的每一个socket
都由一个随机的、不可猜测的、唯一的标识符Socket#id。为了您的方便,每个socket都会自动加入一个由其自己的 id 标识的房间。
这使得实现私人消息变得容易:
io.on("connection", (socket) => {
socket.on("private message", (anotherSocketId, msg) => {
socket.to(anotherSocketId).emit("private message", socket.id, msg);
});
});
示例用例
- 向给定用户的每个设备/选项卡广播数据
io.on("connection", async (socket) => {
const userId = await fetchUserId(socket);
socket.join(userId);
// and then later
io.to(userId).emit("hi");
});
- 发送有关给定实体的通知
io.on("connection", async (socket) => {
const projects = await fetchProjects(socket);
projects.forEach(project => socket.join("project:" + project.id));
// and then later
io.to("project:4321").emit("project updated");
});
断开
断开连接后,leave
会自动将它们所属的所有通道连接起来,您不需要进行特殊的拆卸。
您可以通过监听disconnecting
事件来获取 Socket 所在的房间:
io.on("connection", socket => {
socket.on("disconnecting", () => {
console.log(socket.rooms); // the Set contains at least the socket ID
});
socket.on("disconnect", () => {
// socket.rooms.size === 0
});
});
使用多个 Socket.IO 服务器
与全局广播一样,向房间广播也适用于多个 Socket.IO 服务器。
您只需要将默认的Adapter替换为 Redis Adapter。更多关于它的信息在这里。
实施细节
“房间”功能由我们称为适配器的东西实现。该适配器是一个服务器端组件,负责:
- 存储 Socket 实例和房间之间的关系
- 向所有(或部分)客户端广播事件
您可以在此处找到默认内存适配器的代码。
基本上,它包含两个ES6 Maps:
sids
:Map<SocketId, Set<Room>>
rooms
:Map<Room, Set<SocketId>>
调用socket.join("the-room")
将导致:
- 在
sids
Map中,将“the-room”添加到由Socket ID 标识的 Set - 在
rooms
Map 中,将Socket ID 添加到由字符串“the-room”标识的 Set 中
然后在广播时使用这两个地图:
- 对所有套接字的广播(
io.emit()
)循环通过sids
Map,并将数据包发送到所有sockets - 对给定房间的广播 (
io.to("room21").emit()
)循环通过rooms
Map 中的 Set,并将数据包发送到所有匹配的sockets
您可以通过以下方式访问这些对象:
// main namespace
const rooms = io.of("/").adapter.rooms;
const sids = io.of("/").adapter.sids;
// custom namespace
const rooms = io.of("/my-namespace").adapter.rooms;
const sids = io.of("/my-namespace").adapter.sids;
笔记:
- 这些对象并不意味着直接修改,您应该始终使用
socket.join(...)
和socket.leave(...)
来代替。 - 在多服务器设置中,
rooms
和sids
对象不会在 Socket.IO 服务器之间共享(房间可能只“存在”在一个服务器上而不是另一个服务器上)。
房间事件
从socket.io@3.1.0
开始,底层适配器将发出以下事件:
create-room
(argument: room)delete-room
(argument: room)join-room
(argument: room, id)leave-room
(argument: room, id)
例子:
io.of("/").adapter.on("create-room", (room) => {
console.log(`room ${room} was created`);
});
io.of("/").adapter.on("join-room", (room, id) => {
console.log(`socket ${id} has joined room ${room}`);
});