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

Socket 實例(伺服器端)

Socket 是用於與用戶端互動的基本類別。它繼承了 Node.js EventEmitter 的所有方法,例如 emitononceremoveListener

Bidirectional communication between server and clientBidirectional communication between server and client

此外

Socket 實例有幾個屬性,可能在您的應用程式中派得上用場

Socket#id

每個新連線會分配一個隨機的 20 個字元識別碼。

此識別碼與用戶端上的值同步。

// server-side
io.on("connection", (socket) => {
console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
});

// client-side
socket.on("connect", () => {
console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
});
注意

請注意,除非啟用連線狀態復原,否則id 屬性是一個臨時 ID,不應在您的應用程式中使用(或僅用於除錯目的),因為

  • 每次重新連線後,這個 ID 都會重新產生(例如 WebSocket 連線中斷,或使用者重新整理頁面時)
  • 兩個不同的瀏覽器分頁會有兩個不同的 ID
  • 伺服器上不會儲存特定 ID 的訊息佇列(也就是說,如果客戶端斷線,伺服器傳送給這個 ID 的訊息就會遺失)

請改用一般會話 ID(透過 Cookie 傳送,或儲存在 localStorage 中並在 auth 酬載中傳送)。

另請參閱

注意:您無法覆寫這個識別碼,因為它會用在 Socket.IO 程式碼庫的幾個部分。

Socket#handshake

這個物件包含 Socket.IO 會話一開始時握手的一些詳細資料。

{
headers: /* the headers of the initial request */
query: /* the query params of the initial request */
auth: /* the authentication payload */
time: /* the date of creation (as string) */
issued: /* the date of creation (unix timestamp) */
url: /* the request URL string */
address: /* the ip of the client */
xdomain: /* whether the connection is cross-domain */
secure: /* whether the connection is secure */
}

範例

{
"headers": {
"user-agent": "xxxx",
"accept": "*/*",
"host": "example.com",
"connection": "close"
},
"query": {
"EIO": "4",
"transport": "polling",
"t": "NNjNltH"
},
"auth": {
"token": "123"
},
"time": "Sun Nov 22 2020 01:33:46 GMT+0100 (Central European Standard Time)",
"issued": 1606005226969,
"url": "/socket.io/?EIO=4&transport=polling&t=NNjNltH",
"address": "::ffff:1.2.3.4",
"xdomain": false,
"secure": true
}

Socket#rooms

這是 Socket 目前所在的 房間 的參考。

io.on("connection", (socket) => {
console.log(socket.rooms); // Set { <socket.id> }
socket.join("room1");
console.log(socket.rooms); // Set { <socket.id>, "room1" }
});

Socket#data

一個可以與 fetchSockets() 實用程式方法一起使用的任意物件

// server A
io.on("connection", (socket) => {
socket.data.username = "alice";
});

// server B
const sockets = await io.fetchSockets();
console.log(sockets[0].data.username); // "alice"

更多資訊 在這裡

Socket#conn

基礎 Engine.IO socket 的參考(請參閱 這裡)。

io.on("connection", (socket) => {
console.log("initial transport", socket.conn.transport.name); // prints "polling"

socket.conn.once("upgrade", () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log("upgraded transport", socket.conn.transport.name); // prints "websocket"
});

socket.conn.on("packet", ({ type, data }) => {
// called for each packet received
});

socket.conn.on("packetCreate", ({ type, data }) => {
// called for each packet sent
});

socket.conn.on("drain", () => {
// called when the write buffer is drained
});

socket.conn.on("close", (reason) => {
// called when the underlying connection is closed
});
});

其他屬性

只要您不覆寫任何現有屬性,就可以附加任何屬性到 Socket 實例,並在稍後使用

// in a middleware
io.use(async (socket, next) => {
try {
const user = await fetchUser(socket);
socket.user = user;
} catch (e) {
next(new Error("unknown user"));
}
});

io.on("connection", (socket) => {
console.log(socket.user);

// in a listener
socket.on("set username", (username) => {
socket.username = username;
});
});

Socket 中介軟體

這些中介軟體看起來很像一般的 中介軟體,只不過它們會在每個傳入封包時被呼叫

socket.use(([event, ...args], next) => {
// do something with the packet (logging, authorization, rate limiting...)
// do not forget to call next() at the end
next();
});

next 方法也可以使用錯誤物件呼叫。在這種情況下,事件不會傳送到已註冊的事件處理常式,而會發出 error 事件

io.on("connection", (socket) => {
socket.use(([event, ...args], next) => {
if (isUnauthorized(event)) {
return next(new Error("unauthorized event"));
}
next();
});

socket.on("error", (err) => {
if (err && err.message === "unauthorized event") {
socket.disconnect();
}
});
});

注意:此功能僅存在於伺服器端。對於客戶端,您可能對全域監聽器感興趣。

事件

在伺服器端,Socket 實例會發出兩個特殊事件

disconnect

此事件是由 Socket 實例在斷線時觸發。

io.on("connection", (socket) => {
socket.on("disconnect", (reason) => {
// ...
});
});

以下是可能原因的清單

原因說明
伺服器命名空間斷線Socket 已使用 socket.disconnect() 強制斷線。
客戶端命名空間斷線客戶端已使用 socket.disconnect() 手動斷開 Socket。
伺服器正在關閉伺服器正在關閉。
ping 超時客戶端未在 pingTimeout 延遲中傳送 PONG 封包。
傳輸關閉連線已關閉(例如:使用者已失去連線,或網路已從 WiFi 變更為 4G)。
傳輸錯誤連線已發生錯誤。
解析錯誤伺服器已從客戶端收到無效封包。
強制關閉伺服器已從客戶端收到無效封包。
強制伺服器關閉客戶端未及時加入命名空間(請參閱 connectTimeout 選項),並已強制關閉。

disconnecting

此事件類似於 disconnect,但會稍早觸發,當 Socket#rooms 集合尚未清空時

io.on("connection", (socket) => {
socket.on("disconnecting", (reason) => {
for (const room of socket.rooms) {
if (room !== socket.id) {
socket.to(room).emit("user has left", socket.id);
}
}
});
});

注意:這些事件與 connectconnect_errornewListenerremoveListener,是特殊事件,不應在您的應用程式中使用

// BAD, will throw an error
socket.emit("disconnect");

完整 API

Socket 實例公開的完整 API 可在 此處 找到。