You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Socket.io实现注册用户一对一聊天离线场景最优解决方案咨询

Socket.io离线场景一对一聊天解决方案

你当前的实现已经完成了核心的消息持久化存储,仅需要补全两处逻辑即可解决接收方离线的问题:

1. 修复服务端离线场景报错

当前服务端代码在接收方离线时,getUser(receiverId)会返回空值,直接读取user.socketId会触发异常,修改sendMessage事件逻辑如下:

// 服务端socket.on("sendMessage") 部分修改
socket.on("sendMessage", ({ senderId, receiverId, text }) => {
  console.log("receiverid=====>>",receiverId)
  console.log("users ",users)
  const user = getUser(receiverId);
  console.log("userSocket Id",user)
  // 仅当接收方在线时实时推送消息
  if (user) {
    io.to(user.socketId).emit("getMessage", {
      senderId,
      text,
    });
  }
  // 离线场景无需额外操作,消息已经通过客户端的post接口存入数据库
});

2. 补充用户上线时的消息同步逻辑

你现有代码已经实现了进入单个会话时拉取历史消息的逻辑,可额外补充两个逻辑优化体验:

  • 给消息表新增isRead布尔字段,默认值为false,用于标记消息是否已读
  • 用户建立Socket连接后,拉取所有属于自己的未读消息,更新会话列表的未读计数;用户打开对应会话时,标记该会话下的所有消息为已读
// 客户端Socket连接初始化部分补充逻辑
useEffect(() => {
    socket.current = io("ws://localhost:8900");
    socket.current.on("getMessage", (data) => {
      setArrivalMessage({
        sender: data.senderId,
        text: data.text,
        createdAt: Date.now(),
      });
      // 收到的消息属于当前打开的会话时,标记为已读
      if (currentChat?.members.includes(data.sender)) {
        axios.post("http://localhost:8080/api/messages/markRead", {
          conversationId: currentChat._id,
          senderId: data.sender
        })
      }
    });
    // 连接建立后拉取当前用户的未读消息总数,更新会话列表的未读提示
    axios.get("http://localhost:8080/api/messages/unreadCount", {
      params: { userId: clientId }
    }).then(res => {
      // 自行处理返回结果,更新会话列表的未读标识
    })
}, []);

可选扩展能力

如果需要支持多端登录同步,可修改服务端addUser逻辑,允许同一个userId绑定多个socketId,发消息时遍历所有绑定的socketId推送即可;如果需要离线通知能力,可对接浏览器Notification API或者移动端推送SDK,用户离线时触发推送。

内容的提问来源于stack exchange,提问作者Shehryar Tanoli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:18:03