簡體   English   中英

當該套接字重新連接時,如何讓 socket.io 服務器向特定套接字重新發送消息?

[英]How do I let socket.io server re-emit message to a particular socket when that socket re-connect?

我的服務器代碼使用io.to(socketId).emit('xxx'); 要向特定套接字發送消息,請參閱https://socket.io/docs/emit-cheatsheet/

但是當連接壞了,客戶端斷開連接再次連接時,socketId發生變化, io.to(socketId).emit('xxx'); 然后失敗。 那么如何實現重新發送方法來處理重新連接?

我的問題與這個問題相反, socket.io 在 x 秒/第一次嘗試獲得響應失敗后停止重新發出事件,從中我了解到 socket.io客戶端可以在重新連接時重新發出。 但是服務器代碼似乎缺乏這種支持。

- - 更新 - -

正如評論所說,我在這里顯示“您保存過時的 socketId 值的代碼上下文”。

首先,每個用戶在我們的系統中都有一個唯一的 id,當 socket 連接時,客戶端會發送一個登錄消息,所以我保存了 userId 和 socketId 對。 當我收到斷開連接事件時,我會清除該信息。

其次,用戶將通過 redis pubsub(來自其他服務)獲取消息,我將通過我記錄的 socketid 將該消息發送給客戶端。 因此,當套接字連接時,我將訂閱由 userid 標識的頻道並在斷開連接事件中取消訂閱。

第三,當我收到 redis pub 事件而 socketId 值變得陳舊時,就會發生我的問題。 實際上socketid的值是最新的,但是socket可能發送消息失敗。

io.on('connection', async function (socket) {
  socket.on('login', async function (user_id, fn) {
    ...
    await redis
      .multi()
      .hset('websocket:socket_user', socket.id, user_id)
      .hset(
        `websocket:${user_id}`,
        'socketid',
        socket.id
      )
      .exec()
    await sub.subscribe(user_id)
    ...
  }
                
  socket.on('disconnect', async function (reason) {     
    let user_id = await redis.hget('websocket:socket_user', socket.id)
    await redis.del(`websocket:${user_id}`)
    sub.unsubscribe(user_id)
    ...
  }
  
  //Here is what problem happens when socket is trying to reconnect 
  //while I got redis pub message

  sub.on('message', async function (channel, message) {
  let socketid = await redis.hget(`websocket:${channel}`, 'socketid')
  if (!socketid) {
    return
  }
  //so now I assume the socketid is valid, 
  //but it turns out clients still complain they didn't receive message
  io.to(socketid).emit('msg', message) //When it fails how do I re-emit?
})

我找不到任何現有的解決方案,所以這就是我想出的:

  1. 記錄未能發送到套接字的消息。
  2. 我想不出一種方法來處理這些失敗的消息,例如,如果它們失敗了,我不知道重新發送是否會產生任何影響。
  3. 當我收到套接字連接事件時,我檢查這是否來自同一個客戶端,如果是,則檢查我是否有舊套接字的失敗消息,如果是,則將這些消息發送到新套接字。

如何記錄失敗的消息是我發現 socket.io 不支持的另一項任務,這就是我所做的,

  1. 記錄我要發送的信息
  2. 使用 ack 回調刪除消息,因此沒有刪除的消息是失敗的消息。
  3. 使用 ack 我不能io.to(socketId).emit('xxx'); 因為“從命名空間發出時不支持確認。” 所以我需要首先使用io.sockets.connected[socketid]從 id 獲取套接字 object
  4. 我可以啟動一個計時器來檢查消息是否仍然存儲在 redis 中,如果是,我重新發送它。 但我不知道重新發送是否會有所作為。 所以我還沒有這樣做。

記錄失敗的代碼有點像這樣

let socket = io.sockets.connected[socketid]
if (!socket) return
let list_key = `websocket:${socketid}`
await redis.sadd(list_key, message)
socket.emit('msg', message, async (ack) => {
  await redis.srem(list_key, message)
}) 

如果有人能提出更好的解決方案,我會全力以赴。 謝謝。

您可以通過覆蓋默認方法來為您的套接字客戶端生成自定義 socket.id 以生成套接字 id。 在這種情況下,socket.id 將為您所知,並且可以在相同的 id 上線時重新發出。

//使用節點請求 object (http.IncomingMessage) 作為第一個參數調用 function。 根據 socket.io 文檔

io.engine.generateId = (req) => {
  return "custom:id:" + user_unique_id; // custom id must be unique
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM