【问题标题】:xmpp messages are lost when client connection lost suddently客户端连接突然丢失时 xmpp 消息丢失
【发布时间】:2015-08-17 21:12:36
【问题描述】:

我正在使用 ejabberd 服务器和 ios xmppframework。 有两个客户端,A 和 B。

  1. 当A和B都在线时,A可以向B成功发送消息。
  2. 如果B离线,当B再次在线时,B可以收到消息。
  3. 但是当B突然/意外失去连接时,例如手动关闭wi-fi,A发送的消息丢失。 B永远不会 收到此消息。

我猜原因是B突然失去连接,服务器仍然认为B在线。因此,离线消息在这种情况下确实有效。

所以我的问题是如何确保 A 发送的消息将被 B 接收?确保没有消息丢失。

【问题讨论】:

    标签: xmpp message ejabberd xmppframework


    【解决方案1】:

    上周我一直在尝试在我的 XMPPFramework 和 eJabberd 消息传递应用程序中查找丢失的消息。以下是我为保证消息传递而执行的完整步骤以及每个步骤的效果。

    Mod_offline

    在 ejabberd.yml 配置文件中确保你在访问规则中有这个:

    max_user_offline_messages:
      admin: 5000
      all: 100
    

    这在模块部分:

    mod_offline:
      access_max_user_messages: max_user_offline_messages
    

    当服务器知道消息的接收者离线时,它们将存储它并在重新连接时传递它。

    Ping (XEP-199)

    xmppPing = XMPPPing()
    xmppPing.respondsToQueries = true
    xmppPing.activate(xmppStream)
    
    xmppAutoPing = XMPPAutoPing()
    xmppAutoPing.pingInterval = 2 * 60
    xmppAutoPing.pingTimeout = 10.0
    xmppAutoPing.activate(xmppStream)
    

    Ping 就像心跳一样,因此服务器知道用户何时离线但没有正常断开连接。最好不要通过在applicationDidEnterBackground 上断开连接来依赖它,但是当客户端失去连接或流由于未知原因断开连接时,有一段时间客户端离线但服务器还不知道,因为直到将来的某个时候才能预期 ping 。在这种情况下,邮件不会被传递,也不会被存储以供离线传递。

    流管理 (XEP-198)

    xmppStreamManagement = XMPPStreamManagement(storage: XMPPStreamManagementMemoryStorage(), dispatchQueue: dispatch_get_main_queue())
    xmppStreamManagement.autoResume = true
    xmppStreamManagement.addDelegate(self, delegateQueue: dispatch_get_main_queue())
    xmppStreamManagement.activate(xmppStream)
    

    然后在xmppStreamDidAuthenticate

    xmppStreamManagement.enableStreamManagementWithResumption(true, maxTimeout: 100)
    

    快到了。最后一步是回到ejabberd.yml,并将这一行添加到access: c2s下面的监听端口部分:

    resend_on_timeout: true
    

    流管理在每次消息传递后添加 req/akn 握手。它本身不会对服务器端产生任何影响,除非设置了 resend_on_timeout(在 eJabberd 上默认情况下不是这样)。

    当接收到的消息的确认没有到达服务器并决定保留它以供离线传递时,需要考虑最后一种边缘情况。下次客户端登录时,他们可能会收到重复的消息。为了处理这个问题,我们为 XMPPStreamManager 设置了该委托。实现xmppStreamManagement getIsHandled:,如果消息有聊天正文,则将isHandledPtr 设置为false。当您构造出站消息时,添加一个具有唯一 id 的 xmppElement:

    let xmppMessage = XMPPMessage(type: "chat", to: partnerJID)
    let xmppElement = DDXMLElement(name: "message")
    xmppElement.addAttributeWithName("id", stringValue: xmppStream.generateUUID())
    xmppElement.addAttributeWithName("type", stringValue: "chat")
    xmppElement.addAttributeWithName("to", stringValue: partnerJID.bare())
    xmppMessage.addBody(message)
    xmppMessage.addChild(xmppElement)
    xmppMessage.addReceiptRequest()
    xmppStream.sendElement(xmppMessage)
    

    然后当你收到消息时,通知流管理器该消息已经用xmppStreamManager.markHandledStanzaId(message.from().resource)处理了

    最后一步的目的是建立一个唯一标识符,您可以将其添加到 XMPPMessageArchivingCoreDataStorage 并在显示之前检查重复项。

    【讨论】:

      【解决方案2】:

      最后,我将 Ping 与 Stream Management 结合使用: http://xmpp.org/extensions/xep-0198.html 这个问题解决了。

      【讨论】:

      • 你能详细解释一下吗
      【解决方案3】:

      我猜原因是B突然失去连接和服务器 仍然认为B在线。因此,离线消息确实在此下工作 条件

      是的,你完全正确,这是众所周知的 TCP 连接限制。

      有两种方法可以解决您的问题

      1个服务器端

      我可以看到您使用 ejabbed 作为 XMPP 服务器,您可以实现 mod_ping ,启用此模块将启用服务器端 heartbeat[ping] ,如果与服务器的连接断开[ejabbed] 将 尝试发送心跳到连接并且会检测到连接丢失 服务器和客户端之间。使用这种方法有一个 缺点,模块 mod_ping 有一个名为 ping_interval 的属性 说明向连接的客户端发送心跳的频率,此处较低 限制为 32 秒,任何低于 32 的值都会被 ejabbed 忽略,意味着 您有 32 秒的黑色窗口,如果用户在该窗口中丢失消息 正在在线播种

      2 客户端

      从客户端你可以实现Message Delivery Receipts 机制。每个聊天消息都会向接收者用户发送一个收据 接收者用户收到消息后立即发回此收据 ID。通过这种方式,您可以检测到您的消息实际上已传递到 接收者。如果您在某些特定之间没有收到这样的确认 您可以在本地将用户显示为离线的时间间隔(在移动设备上 电话),将任何进一步的消息存储给该用户作为离线消息 本地[在 SQLLight 数据库中],并等待该用户的离线状态节 ,一旦您收到离线状态节,就意味着该服务器 终于检测到与该用户的连接丢失并使用户 状态为离线,现在您可以将所有消息发送给该用户, 将再次作为离线消息存储在服务器上。这是最好的 避免黑窗的方法。

      结论 您可以使用方法 2 并以这种方式设计您的客户端,也可以将方法 1 与方法 2 一起使用,以最大限度地减少服务器断开连接的时间。

      【讨论】:

        【解决方案4】:

        如果 B 突然下线,则用户 A 必须在向用户 B 发送消息时检查 B 是否在线/离线。如果用户 B 离线,则用户 A 必须使用 Web 服务将该消息上传到服务器上。并且用户 B 必须在下面的函数上调用 web 服务。

        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        

        因此,用户 B 将收到由于连接丢失而丢失的所有离线消息。

        【讨论】:

        • 这是一种解决方案。我也用 XEP-198 和 XEP-199 解决了这个问题
        猜你喜欢
        • 2019-02-20
        • 2014-02-28
        • 2016-12-02
        • 2015-07-21
        • 1970-01-01
        • 2011-05-30
        • 1970-01-01
        • 1970-01-01
        • 2015-01-17
        相关资源
        最近更新 更多