要想保证信息的传输,目前在smack/asmack + openfire架构上,我个人想到有两种实现方式:
1.端到端确保发送(类似短信)。
其实这个就是xmpp协议的XEP-0184: Message Delivery Receipts.
里边为了确保消息的到达,需要接收方返回回执,这样发送方就知道对方是否确切收到消息。
当然咯,接收回执过程中也可能出现断线,导致发送方收不到回执,而认为接收方没收到,再重新发的问题,这个需要接收方过滤掉重复的信息来解决。
其实这个协议,asmack在0.8.3版本就已经支持,具体什么版本开始,就懒得去研究了。
需要注意的是:这个是两个客户端之间的事情,即openfire什么也不用干,只要客户端都支持xep-0184就可以了。
下面简单说下xep-0184协议的交互内容:
发送方发送一个需要回执的消息:
<message from='[email protected]/westminster' id='richard2-4.1.247' to='[email protected]/throne'> <body>My lord, dispatch; read o'er these articles.</body> <request xmlns='urn:xmpp:receipts'/> </message>
接收方收到消息后,返回的消息:
<message from='[email protected]/throne' id='bi29sg183b4v' to='[email protected]/westminster'> <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/> </message>
注意回执的id要与接收的packetID对应。
好了,了解原理了。大家来看看asmack是怎么实现的。
http://bamboo.igniterealtime.org/browse/SMACK-TRUNK-59/commit 上说的好简单,实际上你不会成功的,因为asmack有bug,目前最新的0.8.5上也没解决。
下面跟大家介绍怎么使用消息回执及解决这个bug。
发送需要回执的消息前,调用
DeliveryReceiptManager.addDeliveryReceiptRequest(packet); myConnection.sendPacket(packet);
来为你的packet添加<request xmlns='urn:xmpp:receipts'/>节点。
在初始化xmppconnection后,调用
DeliveryReceiptManager.getInstanceFor(myConnection) .enableAutoReceipts();
来设置自动进行回执,设置后,回执的事情就不用我们自己操心啦。
好了,要做的事情就这么点。额,,,本来应该就这么点。但是。。asmack有bug啊,他把request跟received都用同一个ExtensionProvider啦!!
证据在org.jivesoftware.smackx.ConfigureProviderManager类里边这两句:
// XEP-184 Message Delivery Receipts pm.addExtensionProvider("received", "urn:xmpp:receipts", new DeliveryReceipt.Provider()); pm.addExtensionProvider("request", "urn:xmpp:receipts", new DeliveryReceipt.Provider());
都用了同一个Provider,当然出问题了。就是接受者无法找到<request xmlns='urn:xmpp:receipts'/>节点,因为DeliveryReceipt.Provider()生成的是received节点。
于是乎,自动回执没有效果。
“改吧!。。”
“不改jar包行不行啊?”
“行啊!”
如下:
sAndroid = SmackAndroid.init(cxtContext); ProviderManager pm = ProviderManager.getInstance(); // add delivery receipts pm.addExtensionProvider(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceipt.Provider()); pm.addExtensionProvider(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceiptRequest.Provider());
将jar包中错误的设置,重新设置一下。这样,消息回执功能就大功告成啦。。
xep-0184里边还讲了如何判断客户端是否支持消息回执的问题,如果你有这样的需求,就自己去了解吧。
2.openfire服务端确保消息发送到达(待续)
我不知是否是由于MINA太旧的原因,导致openfire在nio发送的过程中,无法捕获发送异常,导致无法识别异常断线的客户端,也就不能准确的保存离线消息。
openfire目前用的MINA是1.1.7版本,可能是这个版本有问题。apache在这版本上进行了重大的调整和改造,版本号直接改成2.0.0。因此直接升级openfire的MINA包难度很大,连openfire开发者们都拖了这么些年不更新MINA版本。。
这个原因纯属个人猜测,于是这个方法走不通。有兴趣的同学可以试试,熟悉MINA的人应该能看出来。
既然底层机制不可变,那就只能通过自己的手法来处理了。
这个我目前在实践中,或许下一篇就会讲到。