xmpp messages are lost when client connection lost suddently

后端 未结 4 1452
谎友^
谎友^ 2020-12-24 04:57

I am using ejabberd server and ios xmppframework. there are two clients, A and B.

  1. When A and B are online, A can send message to B successfu
相关标签:
4条回答
  • 2020-12-24 05:36

    If B goes offline suddenly then user A have to check if B is online/offline while sending message to user B. If user B is offline then user A have to upload that message on Server using Web service. And user B have to call web service on below function.

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

    So user B will get that all offline message which was lost due to connection Lost.

    0 讨论(0)
  • 2020-12-24 05:47

    I guess the reason is that B lost connection suddenly and the server still think B is online. Thus the offline message does work under this condition

    Yes you are absolutely correct,this is well known limitation of TCP connections.

    There are two approaches to your problem

    1 Server side

    As I can see you are using ejabbed as XMPP server you can implement mod_ping , Enabling this module will enables server side heartbeat[ping] ,in case of broken connection to server[ejabbed] will try to send heartbeat to connection and will detect connection is lost between server and client. Use of this approach has one drawback,module mod_ping has property called ping_interval which states how often to send heartbeat to connected clients, here lower limit is 32 seconds any value below 32 is ignored by ejabbed,means you have 32 seconds black window in which messages can be lost if user is sowing as online

    2 Client side

    From client side you can implement Message Delivery Receipts mechanism .With each Chat message send a receipt to receiver user of as soon as receiver user receives message send back this receipt id. This way you can detect that your message is actually delivered to receiver. If you don't receive such acknowledgement between certain time interval you can show user as offline locally(on mobile phone),store any further messages to this user as offline message locally[in SQLLight database ],and wait for offline presence stanza for that user ,as soon as you receive offline presence stanza it means that server has finally detected connection to that user is lost and makes user status as offline ,now you can send all messages to that user ,which will be again stored as offline messages on server.This is best approach to avoid black-window.

    Conclusion You can either use Approach 2 and design you client such way ,you can also use Approach 1 along with approach 2 to minimize server broken connection detraction time.

    0 讨论(0)
  • 2020-12-24 05:55

    At last, I use Ping together with Stream Management: http://xmpp.org/extensions/xep-0198.html This problem is solved.

    0 讨论(0)
  • 2020-12-24 05:57

    I've spent the last week trying to track down missing messages in my XMPPFramework and eJabberd messaging app. Here are the full steps I went through to guarantee message delivery and what the effects of each step are.

    Mod_offline

    In the ejabberd.yml config file ensure that you have this in the access rules:

    max_user_offline_messages:
      admin: 5000
      all: 100
    

    and this in the modules section:

    mod_offline:
      access_max_user_messages: max_user_offline_messages
    

    When the server knows the recipient of a message is offline they will store it and deliver it when they re-connect.

    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 acts like a heartbeat so the server knows when the user is offline but didn't disconnect normally. It's a good idea to not rely on this by disconnecting on applicationDidEnterBackground but when the client looses connectivity or the stream disconnects for unknown reasons there is a window of time where a client is offline but the server doesn't know it yet because the ping wasn't expected until sometime in the future. In this scenario the message isn't delivered and isn't stored for offline delivery.

    Stream Management (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)
    

    and then in xmppStreamDidAuthenticate

    xmppStreamManagement.enableStreamManagementWithResumption(true, maxTimeout: 100)
    

    Nearly there. The final step is to go back to the ejabberd.yml and add this line to the listening ports section underneath access: c2s:

    resend_on_timeout: true
    

    Stream Management adds req/akn handshakes after each message delivery. On it's own it won't have any effect on the server side unless that resend_on_timeout is set (which it isn't by default on eJabberd).

    There is a final edge case which needs to be considered when the acknowledgement of a received message doesn't get to the server and it decides to hold it for offline delivery. The next time the client logs in they are likely to get a duplicate message. To handle this we set that delegate for the XMPPStreamManager. Implement the xmppStreamManagement getIsHandled: and if the message has a chat body set the isHandledPtr to false. When you construct an outbound message add an xmppElement with a unique id:

    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)
    

    Then when you receive a message, inform the stream manager that the message has been handled with xmppStreamManager.markHandledStanzaId(message.from().resource)

    The purpose of this final step is to establish a unique identifier that you can add to the XMPPMessageArchivingCoreDataStorage and check for duplicates before displaying.

    0 讨论(0)
提交回复
热议问题