跳至主要內容
版本:4.x

房間

房間是一個任意的頻道,其中 socket 可以加入離開。它可用於向部分用戶端廣播事件

Broadcasting to all clients in a roomBroadcasting to all clients in a room
資訊

請注意,房間是一個僅限伺服器的概念(即用戶端無法存取其已加入的房間清單)。

加入和離開

您可以呼叫加入來訂閱 socket 至某個頻道

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

然後在廣播或發送時,只要使用 toin(它們是相同的)即可

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

或排除房間

io.except("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

若要離開頻道,請以與 join 相同的方式呼叫 leave

範例使用案例

  • 將資料廣播至給定使用者的每個裝置/分頁
function computeUserIdFromHeaders(headers) {
// to be implemented
}

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

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");
});

中斷連線

中斷連線後,Socket 會自動 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

實作詳細資訊

「房間」功能是由我們稱為 Adapter 的功能實作。此 Adapter 是伺服器端元件,負責

  • 儲存 Socket 實例與房間之間的關係
  • 將事件廣播至所有(或部分)客戶端

您可以在 此處 找到預設記憶體內 Adapter 的程式碼。

基本上,它包含兩個 ES6 Map

  • sidsMap<SocketId, Set<Room>>
  • roomsMap<Room, Set<SocketId>>

呼叫 socket.join("the-room") 會導致

  • sids Map 中,將「the-room」新增至由 Socket ID 識別的 Set
  • rooms Map 中,將 socket ID 加入由字串「the-room」識別的 Set

廣播時會使用這兩個 map

  • 廣播到所有 socket(io.emit())會迴圈遍歷 sids Map,並將封包傳送給所有 socket
  • 廣播到特定房間(io.to("room21").emit())會迴圈遍歷 rooms Map 中的 Set,並將封包傳送給所有符合條件的 socket

你可以使用以下方式存取這些物件

// 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 開始,基礎的 Adapter 會發出以下事件

  • 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}`);
});