转到主要内容
版本:4.x

房间

sockets可以joinleave房间。它可用于向一部分客户端广播事件:

Broadcasting to all clients in a roomBroadcasting to all clients in a room
信息

请注意,房间是一个仅限服务器的概念(即客户端无权访问它已加入的房间列表)。

加入和离开

您可以调用join以将socket订阅到给定的频道:

io.on("connection", (socket) => {
socket.join("some room");
});

然后在广播或发射时简单地使用toin(它们是相同的):

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都会收到该事件。

Broadcasting to all clients in a room excepting the senderBroadcasting to all clients in a room excepting the sender

要离开频道,您调用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 computeUserIdFromHeaders(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。更多关于它的信息在这里

Broadcasting to all clients in a room with RedisBroadcasting to all clients in a room with Redis

实施细节

“房间”功能由我们称为适配器的东西实现。该适配器是一个服务器端组件,负责:

  • 存储 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())循环通过sidsMap,并将数据包发送到所有sockets
  • 对给定房间的广播 ( io.to("room21").emit())循环通过roomsMap 中的 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(...)来代替。
  • 多服务器设置中,roomssids对象不会在 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}`);
});