Ir al contenido principal
Versión: 4.x

Salas

Una sala es un canal arbitrario al que los sockets pueden unirse y abandonar. Se puede usar para transmitir eventos a un subconjunto de clientes:

Transmitiendo a todos los clientes en una salaTransmitiendo a todos los clientes en una sala
información

Por favor nota que las salas son un concepto solo del servidor (es decir, el cliente no tiene acceso a la lista de salas a las que se ha unido).

Unirse y abandonar​

Puedes llamar a join para suscribir el socket a un canal dado:

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

Y luego simplemente usa to o in (son lo mismo) al transmitir o emitir:

io.to("some room").emit("some event");

O excluye una sala:

io.except("some room").emit("some event");

También puedes emitir a varias salas al mismo tiempo:

io.to("room1").to("room2").to("room3").emit("some event");

En ese caso, se realiza una unión: cada socket que esté al menos en una de las salas recibirá el evento una vez (incluso si el socket está en dos o más salas).

También puedes transmitir a una sala desde un socket dado:

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

En ese caso, cada socket en la sala excluyendo al emisor recibirá el evento.

Transmitiendo a todos los clientes en una sala excepto el emisorTransmitiendo a todos los clientes en una sala excepto el emisor

Para abandonar un canal llamas a leave de la misma manera que join.

Casos de uso de ejemplo​

  • transmitir datos a cada dispositivo / pestaña de un usuario dado
function computeUserIdFromHeaders(headers) {
// a implementar
}

io.on("connection", async (socket) => {
const userId = await computeUserIdFromHeaders(socket.handshake.headers);

socket.join(userId);

// y luego más tarde
io.to(userId).emit("hi");
});
  • enviar notificaciones sobre una entidad dada
io.on("connection", async (socket) => {
const projects = await fetchProjects(socket);

projects.forEach(project => socket.join("project:" + project.id));

// y luego más tarde
io.to("project:4321").emit("project updated");
});

Desconexión​

Al desconectarse, los sockets abandonan todos los canales de los que formaban parte automáticamente, y no se necesita ninguna limpieza especial de tu parte.

Puedes obtener las salas en las que estaba el Socket escuchando el evento disconnecting:

io.on("connection", socket => {
socket.on("disconnecting", () => {
console.log(socket.rooms); // el Set contiene al menos el ID del socket
});

socket.on("disconnect", () => {
// socket.rooms.size === 0
});
});

Con múltiples servidores Socket.IO​

Al igual que la transmisión global, la transmisión a salas también funciona con múltiples servidores Socket.IO.

Solo necesitas reemplazar el Adaptador predeterminado por el Adaptador Redis. Más información al respecto aquí.

Transmitiendo a todos los clientes en una sala con RedisTransmitiendo a todos los clientes en una sala con Redis

Detalles de implementación​

La característica de "sala" es implementada por lo que llamamos un Adaptador. Este Adaptador es un componente del lado del servidor que es responsable de:

  • almacenar las relaciones entre las instancias de Socket y las salas
  • transmitir eventos a todos (o un subconjunto de) clientes

Puedes encontrar el código del adaptador predeterminado en memoria aquí.

Básicamente, consiste en dos Maps de ES6:

  • sids: Map<SocketId, Set<Room>>
  • rooms: Map<Room, Set<SocketId>>

Llamar a socket.join("the-room") resultará en:

  • en el Map sids, agregar "the-room" al Set identificado por el ID del socket
  • en el Map rooms, agregar el ID del socket en el Set identificado por el string "the-room"

Esos dos maps se usan luego al transmitir:

  • una transmisión a todos los sockets (io.emit()) recorre el Map sids, y envía el paquete a todos los sockets
  • una transmisión a una sala dada (io.to("room21").emit()) recorre el Set en el Map rooms, y envía el paquete a todos los sockets coincidentes

Puedes acceder a esos objetos con:

// namespace principal
const rooms = io.of("/").adapter.rooms;
const sids = io.of("/").adapter.sids;

// namespace personalizado
const rooms = io.of("/my-namespace").adapter.rooms;
const sids = io.of("/my-namespace").adapter.sids;

Notas:

  • esos objetos no están destinados a ser modificados directamente, siempre debes usar socket.join(...) y socket.leave(...) en su lugar.
  • en una configuración multi-servidor, los objetos rooms y sids no se comparten entre los servidores Socket.IO (una sala puede solo "existir" en un servidor y no en otro).

Eventos de sala​

A partir de socket.io@3.1.0, el Adaptador subyacente emitirá los siguientes eventos:

  • create-room (argumento: room)
  • delete-room (argumento: room)
  • join-room (argumento: room, id)
  • leave-room (argumento: room, id)

Ejemplo:

io.of("/").adapter.on("create-room", (room) => {
console.log(`la sala ${room} fue creada`);
});

io.of("/").adapter.on("join-room", (room, id) => {
console.log(`el socket ${id} se ha unido a la sala ${room}`);
});