Комнаты
Комната — это произвольный канал, к которому сокеты могут join и leave. Его можно использовать для трансляции событий подмножеству клиентов:
Обратите внимание, что комнаты — это понятие только сервера (т.е. клиент не имеет доступа к списку комнат, к которым он присоединился).
Присоединение и выход
Вы можете вызвать join для подписки сокета на заданный канал:
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");
В этом случае выполняется операция объединения: каждый сокет, который находится хотя бы в одной из комнат, получит событие один раз (даже если сокет находится в двух или более комнатах).
Вы также можете транслировать событие в комнату из данного сокета:
io.on("connection", (socket) => {
socket.to("some room").emit("some event");
});
В этом случае каждое событие в комнате кроме отправителя получит событие.
Чтобы выйти из канала, вызывается leave аналогично join.
Комната по умолчанию
Каждый Socket в Socket.IO идентифицируется случайным, непредсказуемым и уникальным идентификатором 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:
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. Дополнительная информация об этом здесь.
Подробности реализации
Функция "комнаты" реализована с помощью компонента, который мы называем адаптером. Этот адаптер — это компонент на стороне сервера, который отвечает за:
- хранение отношений между экземплярами сокетов и комнатами
- трансляцию событий всем (или подмножеству) клиентам
Вы можете найти код адаптера по умолчанию в памяти здесь.
В основном, он состоит из двух ES6 карт:
-
sids:Map<SocketId, Set<Room>> -
rooms:Map<Room, Set<SocketId>>
Вызов socket.join("the-room") приведет к:
- в карте ̀
sidsдобавлению "the-room" в множество, идентифицированное идентификатором сокета - в карте
roomsдобавлению идентификатора сокета в множество, идентифицированное строкой "the-room"
Затем эти две карты используются при трансляции:
- трансляция всем сокетам (
io.emit()) проходит по картеsids, и пакет отправляется всем сокетам - трансляция в заданную комнату (
io.to("room21").emit()) проходит по множеству в картеrooms, и пакет отправляется всем соответствующим сокетам
Доступ к этим объектам можно получить следующим образом:
// 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(аргумент: комната)delete-room(аргумент: комната)join-room(аргумент: комната, id)leave-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}`);
});
© 2014–2021 Automattic
Licensed under the MIT License.
https://socket.io/docs/v4/rooms