在Android切换数据连接时(例如关闭Wifi,GSM <-> Wifi切换),XMPP消息丢失。

15

我正在开发一个使用XMPP服务器的Android聊天应用。PHP服务器已迁移至ejabber。

我的通知在所有场景中都能正常工作,但有一种情况下我会丢失XMPP消息,事实上我没有收到通知。

具体来说,我关闭WiFi或数据连接后,任何发送给我的通知都无法接收。当我重新打开WiFi或数据连接时,我也无法收到之前发送给我的消息。

我已经在后台实现了服务,可以始终保持XMPP连接处于活动状态。

当我关闭WiFi连接时,我会遇到这个错误。

java.net.SocketException: sendto failed: EPIPE (Broken pipe)
java.net.SocketException: recvfrom failed: ETIMEDOUT (Connection timed out)
   at libcore.io.IoBridge.maybeThrowAfterSendto(IoBridge.java:506)
   at libcore.io.IoBridge.sendto(IoBridge.java:475)
   at java.net.PlainSocketImpl.write(PlainSocketImpl.java:508)
   at java.net.PlainSocketImpl.access$100(PlainSocketImpl.java:46)
   at java.net.PlainSocketImpl$PlainSocketOutputStream.write(PlainSocketImpl.java:270)
   at java.io.OutputStreamWriter.flushBytes(OutputStreamWriter.java:167)
   at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:158)
   at java.io.BufferedWriter.flush(BufferedWriter.java:124)
   at org.jivesoftware.smack.PacketWriter.writePackets(PacketWriter.java:286)
   at org.jivesoftware.smack.PacketWriter.access$12(PacketWriter.java:270)
   at org.jivesoftware.smack.PacketWriter$1.run(PacketWriter.java:137)
Caused by: libcore.io.ErrnoException: sendto failed: EPIPE (Broken pipe)
   at libcore.io.Posix.sendtoBytes(Native Method)
   at libcore.io.Posix.sendto(Posix.java:146)
   at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:177)
   at libcore.io.IoBridge.sendto(IoBridge.java:473)
   ... 9 more
   at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:552)
   at libcore.io.IoBridge.recvfrom(IoBridge.java:516)
   at java.net.PlainSocketImpl.read(PlainSocketImpl.java:489)
   at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
   at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:241)
   at java.io.InputStreamReader.read(InputStreamReader.java:244)
   at java.io.BufferedReader.read(BufferedReader.java:310)
   at org.jivesoftware.smack.XMPPConnection$AliveReader.read(XMPPConnection.java:1122)
   at org.kxml2.io.KXmlParser.fillBuffer(KXmlParser.java:1496)
   at org.kxml2.io.KXmlParser.peekType(KXmlParser.java:979)
   at org.kxml2.io.KXmlParser.next(KXmlParser.java:346)
   at org.kxml2.io.KXmlParser.next(KXmlParser.java:310)
   at org.jivesoftware.smack.PacketReader.parsePackets(PacketReader.java:321)
   at org.jivesoftware.smack.PacketReader.access$1(PacketReader.java:216)
   at org.jivesoftware.smack.PacketReader$1.run(PacketReader.java:70)
Caused by: libcore.io.ErrnoException: recvfrom failed: ETIMEDOUT (Connection timed out)
   at libcore.io.Posix.recvfromBytes(Native Method)
   at libcore.io.Posix.recvfrom(Posix.java:131)
   at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
   at libcore.io.IoBridge.recvfrom(IoBridge.java:513)
   ... 13 more

在Android上遇到了同样的问题,但我们有OPENFIRE XMPP服务器...您能否提供任何关于此的想法? - Mark
嗨@马克,我会给你发送一封关于OPENFIRE配置的邮件。请问你能把你的邮箱地址发给我吗? - Alpan
5个回答

4
我需要安装mod_archive,通常的方法是: 在命令行中输入以下内容(适用于CentOS):

以root用户身份登录 创建一个新目录:

mkdir ejabberd_archive_plugin
cd ejabberd_archive_plugin

现在使用以下命令获取最新的ejabberd模块。
yum https://svn.process-one.net/ejabberd-modules
cd ejabberd-modules/mod_archive/trunk
./build.sh

现在,请使用以下命令进入ebin目录,并获取ejabberd归档插件的所有*.beam文件列表。
root@ns1 [/ejabberd_archive_plugin/ejabberd-modules/mod_archive/trunk/ebin]# ls –l

使用以下命令,您可以在ejabberd安装和配置时获取所有ejabberd *.beam文件列表。
root@ns1 [/usr/lib64/ejabberd/ebin]# ls -l

现在,使用以下命令将所有*.beam(存档插件)文件复制到ejabberd的ebin目录中。
root@ns1 [/]# cp /ejabberd_archive_plugin/ejabberd-modules/mod_archive/trunk/ebin   /usr/lib64/ejabberd/ebin

现在请前往以下网址,复制所有的SQL表查询语句,并在phpmyadmin(ejabberd数据库)中运行。
https://svn.process-one.net/ejabberd-modules/mod_archive/trunk/src/mod_archive_odbc_mysql.sql

现在,请使用以下命令配置ejjaberd.cfg。
root@ns1 [/etc/ejabberd]# vi ejabberd.cfg 
Add the following line into ejabberd.cfg file in modules configuration section
{mod_archive_odbc, [{database_type, "mysql"}, {default_auto_save, true}, {enforce_default_auto_save, true}]},

现在重新启动ejabberd服务。
root@ns1 [/etc/ejabberd]# sudo service ejabberd restart

我在Android上遇到了同样的问题,但我们有OPENFIRE XMPP服务器...您能否提供一些相关的想法吗? - Mark

3
您需要启用 “Stream Management” XEP-198 才能实现此功能。关于这个主题,Georg 写了一篇很好的博客文章:http://op-co.de/blog/posts/XEP-0198/。基本上它允许 报文确认流恢复,这正是您在例如 Wifi <-> GSM 切换的情况下想要做的。
从堆栈跟踪中可以看出您正在使用 aSmack(即 Smack 移植到 Android)。有一个开放的功能请求,要在 Smack 中实现 Stream Management:SMACK-333。实际上,我最近开始着手实施此功能,但无法确定时间表。

对我来说问题在于,我将离线消息策略设置为“始终存储”,因此XEP-0184并不能真正帮助确定消息是否未能传递给其接收者。提供以下场景:-我有两个用户正在聊天,称之为A和B-A向B发送一条消息,而B的连接刚刚断开-消息丢失了,A没有收到通知-在这种情况下,A不知道消息已经丢失,它只会认为消息已经传递到服务器,服务器最终会将其传递给B-B永远失去了该消息。 - Alpan
我正在使用 stanza 进行连接,像这样:<enabled xmlns='urn:xmpp:sm:3' resume='true'/> - Alpan
<resume xmlns='urn:xmpp:sm:3' h='125' previd='43NN1-98'/>这段代码不正常工作。发送请求是正常的,但我没有收到丢失通知,请给我一个正确的解决方案。 - Alpan
1
同样的事情发生在一个场景中。它有效。当我关闭手机时,我会从另一台手机发送消息。然后重新启动设备,所有消息都能成功接收... - Alpan
我在安卓上也遇到了同样的问题,但我们有OPENFIRE XMPP服务器...您能否提供任何相关的想法? - Mark

2

您需要创建单独的网络服务。这样我们可以处理发送消息的历史记录,也可以管理离线消息。

XEP-198是唯一的解决方案。为您的服务器制作插件,并使其易于管理未发送的消息。可能会有其他解决方案,但据我所知,这将是解决方案。


@Rumit Alpan,你能否使用最新的XMPP管理所有服务器代码? - Zala Janaksinh
@Rumit的意思是你所有的Web服务都与XMPP有关?使用openfire smack库吗?你的应用程序现在完成了吗? - Zala Janaksinh
@Rumit 意味着你的网络服务部分仍然是 PHP!我想你正在使用 Pusup 通知吗? - Zala Janaksinh
@ZalaJanaksinh,我们的WS是用PHP编写的。我们还计划从PHP迁移到ejabbered。 - Rumit Patel
让我们在聊天中继续这个讨论 - Zala Janaksinh
显示剩余6条评论

1
你可以修改归档插件以在其中保存消息ID。
当用户上线时,调用一个带有收到的最后一条消息ID的Web服务。服务器可以获取所有后续保存在服务器上的消息。我以同样的方式创建了它,并且完美地工作着。

1
我理解你对我的解释。但是根据你的解释,我仍然没有得到正确的解决方案。你能否深入解释一下?我在连接时使用以下 stanza:<enabled xmlns='urn:xmpp:sm:3' resume='true'/>,并且在恢复时使用<resume xmlns='urn:xmpp:sm:3' h='125' previd='43NN1-98'/>,但是它们不能正常工作。发送请求可以正常工作,但我没有收到丢失通知。请给我一个正确的解决方案。非常感谢您的帮助。 - Alpan
1
你需要创建一个网络服务来获取离线消息。你可以创建一个插件,或者修改存档插件。 - Jaspreet Chhabra
1
XEP-198仍未为Smack准备好,因此这是唯一的解决方案。 - Jaspreet Chhabra
1
如果您使用的是Openfire,我可以提供完整的代码,但我没有使用过ejabber服务器。您需要按照以下步骤操作:
  1. 当连接到互联网时,通过HTTP GET调用将服务器上收到的最后一条消息ID发送到存档插件(存档插件保存所有消息)。
  2. 服务器将回复在该消息ID之后接收到的所有消息,并回复。
- Jaspreet Chhabra
您好,@JaspreetSingh先生,经过一些研究和开发,我对套接字连接感到困惑。因为ejabber和openfire都可以正常工作。在ejabber中是否有任何插件可用于存档插件? - Alpan
显示剩余2条评论

-1
每次重新连接到互联网后,您都应该重新登录。但请记住,如果您使用相同的资源和相同的用户重新登录,即使在线,服务器也会抛出流错误(由新连接替换),这将使您处于离线状态,您将无法接收任何消息。为此,请先正确断开连接,然后再重新登录。尝试在mod Amp模块上搜索。

@JaspreetChhabra 我正在使用它,但是没有重新连接,出现了java.lang.NullPointerException: Attempt to get length of null array W/AbstractXMPPConnection: at org.jivesoftware.smack.util.stringencoder.Base64.encode(Base64.java:61) W/AbstractXMPPConnection: at org.jivesoftware.smack.util.stringencoder.Base64.encodeToString(Base64.java:43) W/AbstractXMPPConnection: at org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism.evaluateChallenge(SCRAMSHA1Mechanis - Rahul Matte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接