当前位置: 首页 > 知识库问答 >
问题:

Websocket传输可靠性(重新连接期间socket.io数据丢失)

杜良骏
2023-03-14

NodeJS,socket.io

    null
    null

附言。我称之为“消息”的东西不仅仅是可以存储在数据库中的文本消息,而是有价值的系统消息,必须保证交付,否则UI就会出错。

谢了!

我已经有了一个用户帐户系统。而且,我的应用程序已经很复杂了。添加脱机/联机状态没有帮助,因为我已经有了这类东西。问题不一样。

查看步骤2。在这一步上,我们技术上不能说如果U1离线,他只是失去连接,比如说2秒钟,可能是因为糟糕的互联网。所以U2给他发了一条消息,但U1没有收到,因为他的互联网仍然关闭(步骤3)。需要第4步来检测脱机用户,假设超时为60秒。最终,再过10秒钟,U1的互联网连接就接通了,他重新连接到socket.io。但是来自U2的消息在空间中丢失,因为在服务器U1上由于超时而断开连接。

这就是问题所在,我不想100%交货。

  1. 在{}用户中收集一个发射(发射名称和数据),由随机发射ID标识。发送emit
  2. 在客户端确认发出(用emitID将发出发送回服务器)
  3. 如果确认-从emitid标识的{}中删除对象
  4. 如果用户重新连接-检查{}此用户并循环遍历{}
  5. 中的每个对象执行步骤1
  6. 断开连接或/和连接时,如果需要,为用户刷新{}
// Server
const pendingEmits = {};

socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });

// Client
socket.on('something', () => {
    socket.emit('confirm', emitID);
});

2020年2月1日添加。

虽然这并不是Websockets的真正解决方案,但有些人可能仍然会发现它很方便。我们从Websockets迁移到SSE+Ajax。SSE允许您从客户端进行连接,以保持持久的TCP连接,并从服务器实时接收消息。要将消息从客户机发送到服务器,只需使用Ajax即可。虽然存在延迟和开销等缺点,但SSE可以保证可靠性,因为它是TCP连接。

由于我们使用Express,所以我们将这个库用于SSE https://github.com/dpskvn/express-SSE,但是您可以选择适合您的库。

IE和大多数Edge版本不支持SSE,因此需要一个polyfill:https://github.com/yaffle/eventsource。

共有1个答案

殷承恩
2023-03-14

其他人在其他回答和评论中暗示了这一点,但根本问题是socket.io只是一种交付机制,您不能单独依赖它来实现可靠的交付。唯一确定消息已成功传递到客户机的人是客户机本身。对于这种系统,我建议作出以下断言:

  1. 消息不直接发送到客户端;相反,它们被发送到服务器并存储在某种数据存储中。
  2. 客户端负责在重新连接时询问“我错过了什么”,并将查询数据存储中存储的消息以更新它们的状态。
  3. 如果在收件人客户端连接时向服务器发送消息,则该消息将实时发送到客户端。

当然,根据应用程序的需要,您可以对其中的部分进行调优--例如,您可以对消息使用Redis列表或排序集,如果您知道客户机是最新的,您可以清除这些消息。

下面是几个例子:

快乐小路:

  • U1和U2都连接到系统。
  • U2向U1应该接收的服务器发送消息。
  • 服务器将消息存储在某种持久存储中,并用某种时间戳或顺序ID将其标记为U1。
  • 服务器通过socket.io.
  • 向U1发送消息
  • U1的客户端确认(可能通过socket.io回调)它收到了消息。
  • 服务器从数据存储区中删除持久消息。

脱机路径:

  • U1失去internet连接。
  • U2向U1应该接收的服务器发送消息。
  • 服务器将消息存储在某种持久存储中,并用某种时间戳或顺序ID将其标记为U1。
  • 服务器通过socket.io.
  • 向U1发送消息
  • U1的客户端未确认收到,因为它们处于脱机状态。
  • 也许U2向U1发送了更多的消息;它们都以相同的方式存储在数据存储区中。
  • 当U1重新连接时,它会询问服务器“我看到的最后一条消息是X/I有状态X,我错过了什么。”
  • 服务器将根据U1的请求数据存储区中遗漏的所有消息发送给U1
  • U1的客户端确认收到,然后服务器从数据存储区中删除这些消息。

如果你绝对想要有保证的交付,那么重要的是设计你的系统,这样连接实际上并不重要,实时交付只是一个额外的好处;这几乎总是涉及某种数据存储。正如user568109在评论中提到的,有一些消息传递系统抽象了所述消息的存储和传递,可能值得研究这样一个预先构建的解决方案。(您可能仍然需要自己编写socket.io集成。)

如果您对将消息存储在数据库中不感兴趣,您可以将它们存储在本地数组中;服务器尝试向U1发送消息,并将其存储在一个“挂起消息”列表中,直到U1的客户机确认收到该消息。如果客户端离线,那么当它回来时,它可以告诉服务器“嘿,我断开了连接,请发送我错过的任何信息”,服务器可以迭代这些消息。

幸运的是,socket.io提供了一种机制,允许客户机“响应”看起来像原生JS回调的消息。下面是一些伪代码:

// server
pendingMessagesForSocket = [];

function sendMessage(message) {
  pendingMessagesForSocket.push(message);
  socket.emit('message', message, function() {
    pendingMessagesForSocket.remove(message);
  }
};

socket.on('reconnection', function(lastKnownMessage) {
  // you may want to make sure you resend them in order, or one at a time, etc.
  for (message in pendingMessagesForSocket since lastKnownMessage) {
    socket.emit('message', message, function() {
      pendingMessagesForSocket.remove(message);
    }
  }
});

// client
socket.on('connection', function() {
  if (previouslyConnected) {
    socket.emit('reconnection', lastKnownMessage);
  } else {
    // first connection; any further connections means we disconnected
    previouslyConnected = true;
  }
});

socket.on('message', function(data, callback) {
  // Do something with `data`
  lastKnownMessage = data;
  callback(); // confirm we received the message
});

这与上一个建议非常相似,只是没有持久化数据存储。

您也可能对事件源的概念感兴趣。

 类似资料:
  • 问题内容: 用过的 NodeJS,Socket.io 问题 想象有两个用户 U1 和 U2 通过Socket.io连接到应用程序。该算法如下: U1 完全失去Internet连接(例如,关闭Internet) U2 向 U1 发送消息。 U1 尚未收到消息,因为Internet断开 服务器 通过心跳超时检测到 U1 断开连接 U1 重新连接到socket.io U1 从不接收来自 U2 的消息-我

  • 问题内容: 在生产中,我有一个使用连接局部变量保存游戏状态的游戏。但是,我注意到,如果我在连接上闲置了一段时间,它将断开连接并重新连接,这将丢失当前状态。在本地主机上进行测试时,我从未注意到此行为。这是套接字连接的规范行为,还是导致连接断开的其他原因。 如果是正常行为,通常如何处理?是否应该将连接值全局存储,以便用户断开/重新连接时可以恢复连接值? 问题答案: 您的问题与套接字超时有关。如果某个套

  • 问题内容: 更新: 我在驱动程序上使用2.1版本,而不是3.2 我有一个使用MongoDB的节点应用程序。我的问题是,如果MongoDB服务器由于任何原因而关闭,则应用程序不会重新连接。为了实现这一点,我基于此官方教程中的代码进行了测试。 想法是运行此脚本,然后停止mongod,然后重新启动它。所以,我们开始: 将MongoDb停止10秒钟可以达到预期效果:它将停止运行查询10秒钟,然后在服务器返

  • 我编写了一些逻辑,同时表示与exchange的近200个websocet连接。我使用第三方api,它基于org.eclipse.jetty.webSocket.api。我有一个我必须重写的方法。 我在stackoverflow上找到了这个问题,但我看不到清晰的答案。请帮忙,提前谢谢。

  • 问题内容: 我尝试使用nginx设置nodejs。但是当客户端尝试连接时,它将失败并显示… 那么如何启用websocket通信呢? 我目前的Nginx配置 问题答案: 首先,将您的nginx服务器升级到1.3或更高版本。 其次,我的nginx conf有效。您可以关注我的conf。

  • 我正在使用套接字。io,应用程序由AWS提供。我收到一个浏览器控制台错误: websocket.js:54 WebSocket 连接到 'wss://www.tidee.com/socket.io/?EIO=4 客户代码: 服务器代码: 在 AWS 中,安全组接受端口 443,负载均衡器侦听端口 443。 应用程序在本地运行时工作(状态 101),并使用 协议。