当客户端连接突然丢失时,xmpp消息将丢失

时间:2015-06-03 15:01:53

标签: xmpp message ejabberd xmppframework

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

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

我猜原因是B突然失去连接,服务器仍然认为B在线。因此,离线消息在这种情况下可以正常工作。

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

4 个答案:

答案 0 :(得分:11)

我上周试图在我的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而不依赖于此是一个好主意,但是当客户端失去连接或流因未知原因断开连接时,会有一个时间窗口,客户端处于脱机状态,但服务器没有&#39我知道它,因为直到将来的某个时候才能预期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并在显示之前检查重复项。

答案 1 :(得分:8)

  

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

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

您的问题有两种解决方法

  

1服务器端

     

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

     

2客户端

     

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

<强>结论 您可以使用方法2并以这种方式设计客户端,也可以使用方法1和方法2来最小化服务器断开的连接缩减时间。

答案 2 :(得分:3)

如果B突然脱机,则用户A必须在向用户B发送消息时检查B是否在线/离线。如果用户B处于离线状态,则用户A必须使用Web服务在服务器上上传该消息。用户B必须在以下功能上调用Web服务。

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

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

答案 3 :(得分:0)

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