сообщения xmpp теряются, когда клиентское соединение потеряно suddently

Я использую сервер ejabberd и ios xmppframework. есть два клиента, A и B.

  1. когда A и B находятся в сети, A может успешно отправить сообщение B.
  2. если B находится в автономном режиме, B может получить сообщение, когда B снова в сети.
  3. но когда B внезапно / неожиданно потерял соединение, например, вручную закрыть wi-fi, сообщение, отправленное A теряется. B никогда не будет получить это сообщение.

Я думаю, причина в том, что B внезапно потерял соединение, и сервер все еще думает, что B находится в сети. Таким образом, автономное сообщение работает при этом условии.

Итак, мой вопрос заключается в том, как гарантировать, что сообщение, отправленное A, будет получено B? Чтобы убедиться, что сообщения не потеряны.

4 ответов


я провел последнюю неделю, пытаясь отследить недостающие сообщения в моем приложении xmppframework и ejabberd messaging. Вот полные шаги, которые я прошел, чтобы гарантировать доставку сообщений и какие последствия каждого шага.

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 но когда клиент теряет подключение или поток отключается по неизвестным причинам, существует окно времени, когда клиент находится в автономном режиме, но сервер пока не знает, потому что пинг не ожидался до какого-то времени в будущем. В этом случае сообщение не доставляется и не хранится для автономной доставки.

управление потоком (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. При создании исходящего сообщения добавьте 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 и проверьте наличие дубликатов перед отображением.


Я думаю, причина в том, что B потерял соединение внезапно и сервер все еще думаю, что B в сети. Таким образом, автономное сообщение работает под этим условие

Да, вы абсолютно правы,это хорошо известное ограничение TCP-подключений.

есть два подхода к вашей проблеме

1 стороне сервера

Как я вижу, вы используете ejabbed как сервер XMPP можно реализовать mod_ping включение этого модуля позволит серверной стороне сердцебиение[ping], в случае разрыва соединения с сервером[ejabbed] будет попробуйте отправить сердцебиение на соединение и обнаружите, что соединение потеряно между сервером и клиентом. Использование этого подхода имеет один недостаток,модули mod_ping имеет свойство ping_interval, которое указывает, как часто отправлять heartbeat подключенным клиентам, здесь ниже предел 32 секунды любое значение ниже 32 игнорируется ejabbed, средство у вас 32 секунды черное окно, в котором сообщения могут быть потеряны, если пользователь сеет как онлайн

2 сторона клиента

со стороны клиента вы можете реализовать Квитанции О Доставке Сообщений механизм.С каждым сообщением чата отправить квитанцию получателю пользователю как только пользователь receiver получит сообщение, отправьте обратно эту квитанцию идентификатор. Таким образом, вы можете обнаружить, что ваше сообщение действительно доставлено приемник. Если нет ... получение такого подтверждения между определенными интервал времени вы можете показать пользователя в автономном режиме локально(на мобильном телефоне телефон), хранить любые дополнительные сообщения для этого пользователя как автономное сообщение локально[в базе данных SQLLight] и дождитесь строки автономного присутствия для этого пользователя ,как только вы получаете offline присутствие stanza это означает, что сервер наконец, обнаружено соединение с этим пользователем потеряно и делает пользователя статус как offline, теперь вы можете отправлять все сообщения этому пользователю, который будет снова сохранен как автономные сообщения на сервере.Это лучший подход, чтобы избежать черного окна.

вывод Вы можете либо использовать подход 2 и спроектировать клиента таким образом, вы также можете использовать подход 1 вместе с подходом 2, чтобы минимизировать время разрыва соединения сервера.


Если B внезапно отключается, пользователь A должен проверить, находится ли B в сети/в автономном режиме при отправке сообщения пользователю B. Если пользователь B отключен, то пользователь A должен загрузить это сообщение на сервер с помощью веб-службы. И пользователь B должен вызвать веб-службу ниже функции.

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

таким образом, пользователь B получит все автономное сообщение, которое было потеряно из-за потери соединения.


наконец, я использую Ping вместе с управлением потоком: http://xmpp.org/extensions/xep-0198.html Эта проблема решена.