傳遞保證
訊息排序
Socket.IO 保證訊息排序,無論使用哪種低階傳輸(甚至在從 HTTP 長輪詢升級到 WebSocket 時)。
這是因為
- 底層 TCP 連線提供的保證
- 升級機制 的周密設計
範例
socket.emit("event1");
socket.emit("event2");
socket.emit("event3");
在上述範例中,事件將始終以相同的順序被另一方接收(假設它們確實到達,請參閱下方)。
訊息到達
至多一次
預設情況下,Socket.IO 提供最多一次的傳遞保證
- 如果在傳送事件時連線中斷,則無法保證另一端已收到事件,且重新連線時不會重試
- 已中斷連線的用戶端會緩衝事件直到重新連線(儘管前一點仍然適用)
- 伺服器上沒有此類緩衝區,這表示已中斷連線的用戶端錯過的任何事件在重新連線時都不會傳輸給該用戶端
資訊
目前,額外的傳遞保證必須在您的應用程式中實作。
至少一次
從用戶端到伺服器
從用戶端來看,您可以使用確認和逾時來達成至少一次的保證
function emit(socket, event, arg) {
socket.timeout(2000).emit(event, arg, (err) => {
if (err) {
// no ack from the server, let's retry
emit(socket, event, arg);
}
});
}
emit(socket, "foo", "bar");
在上述範例中,用戶端會在給定的延遲後重試傳送事件,因此伺服器可能會收到同一個事件多次。
注意
即使在這種情況下,如果使用者重新整理分頁,任何待處理的事件都將會遺失。
從伺服器到用戶端
對於伺服器傳送的事件,額外的傳遞保證可以透過下列方式實作
- 為每個事件指定一個唯一的 ID
- 將事件保留在資料庫中
- 儲存用戶端上最後接收到的事件的偏移量,並在重新連線時傳送
範例
用戶端
const socket = io({
auth: {
offset: undefined
}
});
socket.on("my-event", ({ id, data }) => {
// do something with the data, and then update the offset
socket.auth.offset = id;
});
伺服器
io.on("connection", async (socket) => {
const offset = socket.handshake.auth.offset;
if (offset) {
// this is a reconnection
for (const event of await fetchMissedEventsFromDatabase(offset)) {
socket.emit("my-event", event);
}
} else {
// this is a first connection
}
});
setInterval(async () => {
const event = {
id: generateUniqueId(),
data: new Date().toISOString()
}
await persistEventToDatabase(event);
io.emit("my-event", event);
}, 1000);
實作遺失的方法(fetchMissedEventsFromDatabase()
、generateUniqueId()
和 persistEventToDatabase()
)與資料庫有關,留待讀者練習。
參考
socket.auth
(用戶端)socket.handshake
(伺服器)