Комнаты
Комната — это произвольный канал, к которому могут 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', function(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');
}); |
Использование с асинхронным кодом
Пожалуйста, используйте io.to(...).emit(...) (или socket.to(...).emit(...)) синхронным образом.
Пример с обратной функцией:
// BAD
const room = socket.to('room1');
saveProduct(() => {
room.emit('product-updated');
});
// GOOD
saveProduct(() => {
socket.to('room1').emit('product-updated');
}); |
Пример с async/await:
// BAD
io.to('room2').emit('details', await fetchDetails());
// GOOD
const details = await fetchDetails();
io.to('room2').emit('details', details); |
Пояснение: метод to() не возвращает новый объект, а изменяет атрибут объекта io (соответственно, объекта socket).
Это также относится к другим модификаторам трансляции:
Отключение
При отключении сокеты 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.
Вам просто нужно заменить стандартный адаптер на Redis адаптер. Более подробная информация об этом здесь.
Подробности реализации
Функция «комната» реализована с помощью того, что мы называем адаптером. Этот адаптер — это компонент на стороне сервера, который отвечает за:
- хранение отношений между экземплярами сокетов и комнатами
- трансляцию событий всем (или подмножеству) клиентам
Вы можете найти код стандартного адаптера в памяти здесь.
В основном, он состоит из двух ES6 Map:
-
sids:Map<SocketId, Set<Room>> -
rooms:Map<Room, Set<SocketId>>
Вызов socket.join("the-room") приведет к:
- в карте
sids, добавлению «the-room» в Set, идентифицируемый ID сокета - в карте
rooms, добавлению ID сокета в Set, идентифицируемый строкой «the-room»
Затем эти две карты используются при трансляции:
- трансляция во все сокеты (
io.emit()) перебирает картуsids, и отправляет пакет всем сокетам - трансляция в определенную комнату (
io.to("room21").emit()) перебирает Set в карте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/v3/rooms