专业IM即时通讯软件开发,值得信赖!

Android xmpp+openfire+smack 断线问题解决方案

openfire 云聊IM 311℃

用smack库写Android端聊天的功能的同学可能都有一个困扰,就是应用切换到后台,或者锁屏一会儿,回来以后发现消息发不出去了,此时去openfire后台查看自己这个账号,显示是下线的,但是打断点,调用connection的isConnect方法,发现返回的竟然是true,也就是说没有办法主动判断自己是否在线,长链接是否依然存在。

//使用如下代码 依然不能判定连接是有效的,依然有可能消息发不出、收不到
mConnection != null && mConnection.isConnected() && mConnection.isAuthenticated()) 

smack提供了reconnect的方法,用ReconnectedManager可以调用到,在官方文档上也可以查到。

//smack的重连机制,没有办法彻底解决掉线问题
ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(mConnection);
 reconnectionManager.setFixedDelay(3);//重联间隔3秒
 reconnectionManager.enableAutomaticReconnection();//开启重联机制

但是我调用过以后发现,还是会偶尔出现断线的情况,这样的产品没法上线,用户聊着聊着发现消息发不出去了,然后一看自己还有网,这肯定是要大发雷霆卸载app的。于是乎,自己想办法解决吧!

经过测试,发现openfire每三分钟就会ping一次客户端,客户端会pong回应服务端。但是这样ping-pong几次后,发现客户端就接受不到服务器的ping信息了,而服务器发现客户端没pong,就把客户端的会话关闭了。这是上面那个问题的原因。至于为何客户端会接受不到服务器的ping,就是下面要谈的内容了。经过查找资料发现NAT超时是影响tcp寿命的重大因素。以下是由微信开发 团队提供的资料:

上面的意思是说心跳包间隔时间要小于NAT超时时间,才能防止长连接中断。

因此,有了解决方案1::把服务器发送心跳包的间隔改为90s一次,果然问题解决了。

当然这个问题不是最优解,比较好的方法可以是客户端采取智能发送心跳包的方式来维持长连接。具体可以参考以下文章: http://www.yunliaoim.com/im/934.html

解决方法2:定时一个任务,每隔30秒进行一次登录操作。优点:几乎可以完全避免莫名其妙的断线。缺点:登录之前首先要先disconnect,不然会抛出已经连接上异常。而且还要向服务器发送present协议告诉服务器你上线了。而这些上线和下线的协议,服务器会转发给所有的好友,移动端消耗流量,服务端消耗资源,不可取。

上述方案,也可以更改为,定时一个任务,每隔30秒连接一次。如果连接成功,但没有登录,再执行一次登录。但上线、下线的消息包,依旧没法避免。

private Timer reConnectTimer;
private int delay = 10000;
//pingFailed时启动重连线程
class ReConnectTimer extends TimerTask {  
    @Override  
    public void run() {
        // 无网络连接时,直接返回
        if (getNetworkState(mService) == NETWORN_NONE) {
            Log.i(TAG, "无网络连接,"+delay/1000+"s后重新连接");
            reConnectTimer.schedule(new ReConnectTimer(), delay);
            //reConnectTimer.cancel();
            return;
        }
        // 连接服务器 
        try {
            mConnection.connect();
            if(!mConnection.isAuthenticated()){
                mConnection.login();
                reConnectTimer.cancel();
            }
            Log.i(TAG, "重连成功");
            Intent intent = new Intent(SmackConst.ACTION_RECONNECT_SUCCESS);
            mService.sendBroadcast(intent);
        } catch (Exception e) {
            Log.i(TAG, "重连失败,"+delay/1000+"s后重新连接");
            e.printStackTrace();
            reConnectTimer.schedule(new ReConnectTimer(), delay);
        } 
        
    }  
}

解决方法3:整体的流程是,定时一个任务,每隔半分钟主动向服务器发送心跳包,如果发送之后一定时间内服务器没有回执,那么就认为掉线了,进行重连工作。优点:解决方法1的缺点,避免了服务器和好友反复收到present协议的消息,节省了服务器的开销。我们可以主动的定时向服务器发送心跳包,为发送的这个心跳包协议指定一个id,当我们发送ping给服务器以后,理论上来说,立刻就可以收到服务器的反馈,即是告诉你服务器收到你的消息了,知道你还在线,不会踢掉你。其实socket底层检查链接是否存在也是通过心跳,只不过发送的是极小的心跳包。而xmpp也定义了这种心跳包的机制,发送的是协议,携带的是标签。而服务器收到这样的协议以后,也会发回来一个协议,属性中的type是result。我们利用socket底层的原理,照猫画虎写一个重连即可。

需要注意的是,iq协议info/query,顾名思义,信息查询,因此可能会有其他的某些查询操作也是发送的iq协议,因此需要在接受节点消息的监听中增加心跳包的过滤器,用消息的id判断即可。

喜欢 (1)
仿微信聊天软件开发
点击这里给我发消息