最近工作中用到openfire,做聊天应用服务器,环境塔建没有问题,但当手机客户端网络异常时,却仍显示用户在线,导致有一部分消息对方会收不到。在网上找了半天,也没找到好的解决方案。参考N多文章及询问群友,终决定加个消息回执插件,来保证 消息是否收到 的准确性。
具体如下:
插件基本上是在别人基础上改的
基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件
http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html
1、核心插件类ChatLogPlugin(偷懒了,直接用别人的)
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
throws PacketRejectedException {
//System.out.println("chat...........session."+session);
if (session != null) {
debug(packet, incoming, processed, session);
}
JID recipient = packet.getTo();
if (recipient != null) {
String username = recipient.getNode();
// 广播消息或是不存在/没注册的用户.
if (username == null || !UserManager.getInstance().isRegisteredUser(recipient)) {
return;
} else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
// 非当前openfire服务器信息
return;
} else if ("".equals(recipient.getResource())) {
}
}
this.doAction(packet, incoming, processed, session);
}
private void doAction(Packet packet, boolean incoming, boolean processed, Session session) {
Packet copyPacket = packet.createCopy();
System.out.println("=================1===================="+(packet instanceof IQ));
System.out.println("================2====================="+(packet instanceof Message));
if (packet instanceof Message) {
Message message = (Message) copyPacket;
// 一对一聊天,单人模式
if (message.getType() == Message.Type.chat) {
log.info("单人聊天信息:{}", message.toXML());
debug("单人聊天信息:" + message.toXML());
debug("单人聊天信息:" + message.getID()+":"+message.getTo());
// 程序执行中;是否为结束或返回状态(是否是当前session用户发送消息)
if (processed || !incoming) {
return;
}
if(IsUserOnLine(message.getTo().toString())==1){
//原代码
//logsManager.add(this.get(packet, incoming, session));
logsManager.insertOffline(message);
}
// 群聊天,多人模式
} else if (message.getType() == Message.Type.groupchat) {
List<?> els = message.getElement().elements("x");
if (els != null && !els.isEmpty()) {
log.info("群聊天信息:{}", message.toXML());
debug("群聊天信息:" + message.toXML());
} else {
log.info("群系统信息:{}", message.toXML());
debug("群系统信息:" + message.toXML());
}
// 其他信息
} else {
log.info("其他信息:{}", message.toXML());
debug("其他信息:" + message.toXML());
}
} else if (packet instanceof IQ) {
IQ iq = (IQ) copyPacket;
debug("IQ:" + iq);
debug("IQ0:" + iq.toXML());
debug("IQ1:" + iq.getType());
if (iq.getType() == IQ.Type.set && iq.getChildElement() != null && "session".equals(iq.getChildElement().getName())) {
log.info("用户登录成功:{}", iq.toXML());
debug("用户登录成功:" + iq.toXML());
}
else if (iq.getType() == IQ.Type.set && iq.getElement() != null ) {//判断回执
Element el= iq.getElement().element("message");
String type1=el.attributeValue("type");
Element el2=el.element("item");
String type2=el2.attributeValue("id");
debug("IQ2:" + type1);
debug("IQ3:" + type2);
if("receipt".equals(type1)&&type2!=null){//回执
logsManager.delById(type2);
}
}
} else if (packet instanceof Presence) {
Presence presence = (Presence) copyPacket;
if (presence.getType() == Presence.Type.unavailable) {
log.info("用户退出服务器成功:{}", presence.toXML());
debug("用户退出服务器成功:" + presence.toXML());
}
}
}
方法说明:做消息拦截,执行doAction方法,首先判断哪种聊天方式,这里我只实现了单对单的。然后判断用户是否在线,如果在线,用户发的消息存到ofoffline表中,不在线就不管了,由openfire本身的离线机制去处理,它也是存到ofoffline表中。现在存到表中的数据其实不是真实的离线数据,这时候就要去判断客户端的回执了,客户端收到消息后需要封装个IQ回来,然后得到收到消息的id.因为ofoffline表中没存messageId ,原来的自增长的,需要在表中新加字段receipt_id,varchar型,即保存消息时,将messageId 存到这个字段中。得到ID后,就可以调数据处理类的删除方法,将这条信息从表中删除了。这样就简单的实现了异常掉线的消息处理。</p>
另:判断用户在线需安装presence插件。
ps:本人刚接触openfire,欢迎修改、指证
附件中包括核心三个类