默认
发表评论 32
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
[已回复] MobileIMSDK服务端的多设备登陆互踢问题求助
阅读(96785) | 评论(32 收藏 淘帖1
同一个ID用户登录到两台设备时,我在onVerifyUserCallBack 返回前,
        public int onVerifyUserCallBack(String lpUserName, String lpPassword, String extra)
        {
                logger.debug("正在调用回调方法:OnVerifyUserCallBack...(extra="+extra+")");
                logger.debug("正在调用回调方法:OnVerifyUserCallBack...(lpUserName="+lpUserName+")");
                logger.debug("正在调用回调方法:OnVerifyUserCallBack...(lpPassword="+lpPassword+")");                
                HashMap<Integer, String> map= UserProcessor.getInstance().getUserNames();
                Iterator iter = map.entrySet().iterator();
                while (iter.hasNext()) {
                        Map.Entry entry = (Map.Entry) iter.next();
                        Integer key = (Integer)entry.getKey();
                        String val = (String)entry.getValue();
                        if(val.equals(lpUserName)){
                                try {
                                        ServerLauncher.sendData(0, key , "logout");
                                } catch (Exception e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                }
                                break;
                        }
                }
                return 0;
        }

遍历了UserProcessor.getInstance().getUserNames()和lpUserName,存在key就给对应的发送ServerLauncher.sendData(0, key , "logout");下线指令。
现在遇到的问题:2个设备快速切换登陆的时候,会出现互踢失败的情况,然后两台设备会不停的断线登录。

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

标签:MobileIMSDK
上一篇:[已解决] MobileIMSDK 安卓客户端退出登入后重新登入问题下一篇:[已回复] 求助MobileIMSDK v2版中掉线后自动重登录的问题

本帖已收录至以下技术专辑

推荐方案
评论 32
请问下,最新版本的sdk,加入了互踢的功能了吗
签名: 哈哈哈
引用:dahai2070 发表于 2019-06-17 10:52
不知道群主现在最新的4.*版本,同一个id 重复登录,踢掉前一台设备,这个逻辑还是在onLineProcessor 里面执 ...

客户端收到被踢指令后,只需要管好自已的事,比如release,服务端怎么断开那是服务端的逻辑,不需要客户端来决定,不然就涉及安全问题了。

另外,我在这个帖子里回复了有关互踢的思路,可以参考一下《[已回复] 求教同一账号、不同设备、同时登陆强制下线的优化方法

不知道群主现在最新的4.*版本,同一个id 重复登录,踢掉前一台设备,这个逻辑还是在onLineProcessor 里面执行吗?
我跟着看了此贴的回复,貌似他们在这儿T之前设备(对应之前的session)的时候,之前的客户端收到被踢,调用退出逻辑,这个退出逻辑,这个是退出逻辑是直接 让客户端直接rlease (调用ClientCoreSDK.getInstance().release();)呢,还是发送一条 退出命令给服务器呢?仅仅是 rlease 就行了吧?
public void putUser(String user_id, IoSession session)
        {
                if(onlineSessions.containsKey(user_id))
                {
                        logger.debug("[IMCORE]【注意】用户id="+user_id+"已经在在线列表中了,session也是同一个吗?"
                                        +(onlineSessions.get(user_id).hashCode() == session.hashCode()));
                        // TODO 同一账号的重复登陆情况可在此展开处理逻辑
                        IoSession oldSession=onlineSessions.get(user_id);
                        if(oldSession!=session) {
                                //当同一账号 在多个设备上登录时,会执行这里的代码,发消息给该账号之前登录的设备,通知它“你的账号在其他设备上登录了,你被踢出去了”,在客户端收到这样类型的消息后,需要调用ClientCoreSDK.release()方法。
                                ServerToUserMessage serverToUserMessage=new ServerToUserMessage();
                                serverToUserMessage.setType(5);
                                try {
                                        LocalSendHelper.sendData("0", user_id, new Gson().toJson(serverToUserMessage),false,0);
                                        LocalSendHelper.sendData("0", user_id, new Gson().toJson(serverToUserMessage),false,0);
                                        LocalSendHelper.sendData("0", user_id, new Gson().toJson(serverToUserMessage),false,0);
                                        //Thread.sleep(5000);
                                        oldSession.setAttribute(OnlineProcessor.USER_ID_IN_SESSION_ATTRIBUTE, null);
                                        oldSession.close(true);
                                       
                                } catch (Exception e) {
                                        e.printStackTrace();
                                }
                        }
                }
                onlineSessions.put(user_id, session);
                __printOnline();// just for debug
        }
       
下面是我根据 jack的回复 ,写的代码 ,大家 看下 有没有什么问题?
public void putUser(String user_id, IoSession session)
        {
                if(onlineSessions.containsKey(user_id))
                {
                        logger.debug("[IMCORE]【注意】用户id="+user_id+"已经在在线列表中了,session也是同一个吗?"
                                        +(onlineSessions.get(user_id).hashCode() == session.hashCode()));
                        // TODO 同一账号的重复登陆情况可在此展开处理逻辑
                        IoSession oldSession=onlineSessions.get(user_id);
                        if(oldSession!=session) {
                                //当同一账号 在多个设备上登录时,会执行这里的代码,发消息给该账号之前登录的设备,通知它“你的账号在其他设备上登录了,你被踢出去了”,在客户端收到这样类型的消息后,需要调用ClientCoreSDK.release()方法。
                                ServerToUserMessage serverToUserMessage=new ServerToUserMessage();
                                serverToUserMessage.setType(5);
                                try {
                                        LocalSendHelper.sendData("0", user_id, new Gson().toJson(serverToUserMessage),false,0);
                                        Thread.sleep(10000);
                                } catch (Exception e) {
                                        e.printStackTrace();
                                }
                        }
                }
                onlineSessions.put(user_id, session);
                __printOnline();// just for debug
        }
引用:吴佳同 发表于 2017-11-16 13:43
在网络出现断线后 框架会自动重连
当重连的时候,有可能再次调用 onVerifyUserCallBack方法,
再次调用 ...

它自动重连的时候,因为之前已经踢掉了其它设备上登陆的端,所以重连时只有这一个客户端存在,没有理由不能自动重连啊。你逻辑不要乱。
引用:JackJiang 发表于 2017-11-16 11:31
你按照你的逻辑实现就可以了

如果我不做任何 处理,
当一个账号在多个设备上登录时,会出现什么情况?
框架 会出现什么问题?
引用:JackJiang 发表于 2017-11-16 11:31
你按照你的逻辑实现就可以了

在网络出现断线后 框架会自动重连
当重连的时候,有可能再次调用 onVerifyUserCallBack方法,
再次调用 这个方法时,有可能ioSession 还不是null,
按照我的写法 虽然可以达到同一账号只能登录在一个设备上,但有可能造成账号断线后无法自动重连登录。

引用:吴佳同 发表于 2017-11-16 11:19
我这样的处理方式 可以 吗?
@Override
        public int onVerifyUserCallBack(String userId, String toke ...

你按照你的逻辑实现就可以了
引用:JackJiang 发表于 2017-07-18 15:52
解决了吗?

我这样的处理方式 可以 吗?
@Override
        public int onVerifyUserCallBack(String userId, String token, String extra, IoSession session) {
                //返回1026代表用户名不存在,返回1027代表密码不正确,返回1028代表此用户已经登录了不允许再登录了。
                User user=userService.findByName(userId);
                if(user==null) {
                        return 1026;
                }
                user=userService.findByNameAndPwd(userId, token);
                if(user==null) {
                        return 1027;
                }
                IoSession ioSession=OnlineProcessor.getInstance().getOnlineSessions().get(userId);
                if(ioSession!=null) {
                        return 1028;
                }
                return 0;
        }
引用:tiandao 发表于 2017-07-18 15:48
public void putUser(String user_id, IoSession session)
        {
                if(onlineSessio ...

互踢解决:  第一步:        public void putUser(String user_id, IoSession session)
    {
            if(onlineSessions.containsKey(user_id))
            {
                    logger.debug("[IMCORE]【注意】用户id="+user_id+"已经在在线列表中了,session也是同一个吗?"
                                    +(onlineSessions.get(user_id).hashCode() == session.hashCode()));
                    try {
                        IoSession oldsession = onlineSessions.get(user_id);
                        logger.debug("-->发送结果:"+LocalSendHelper.sendData(oldsession,ProtocalFactory.createCommonData(
                                        "logout", "0", user_id, false, null, -1)));
                        oldsession.close(true);
                    } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                    }
                    // TODO 同一账号的重复登陆情况可在此展开处理逻辑
            }

            onlineSessions.put(user_id, session);

            __printOnline();// just for debug
    }  第二步:public void sessionClosed(IoSession session) throws Exception
    {
            String user_id = OnlineProcessor.getUserIdFromSession(session);
            logger.info("[IMCORE]与"+ServerToolKits.clientInfoToString(session)+"的会话关闭(user_id="+user_id+")了...");
            if(user_id != null)
            {
                    logger.info("[是否相同]"+OnlineProcessor.getInstance().getOnlineSession(user_id)+session);
                    if(OnlineProcessor.getInstance().getOnlineSession(user_id).hashCode() == session.hashCode())
                            OnlineProcessor.getInstance().removeUser(user_id);
                    
                    if(serverEventListener != null)
                            serverEventListener.onUserLogoutAction_CallBack(user_id, null, session);
                    else
                            logger.debug("[IMCORE]>> 客户端"+ServerToolKits.clientInfoToString(session)+"的会话被系统close了,但回调对象是null,没有进行回调.");
            }
            else
            {
                    logger.warn("[IMCORE]【注意】客户端"+ServerToolKits.clientInfoToString(session)+"的会话被系统close了,但它里面没有存放user_id,这个会话是何时建立的?");
            }
    }
修改ServerCoreHandler.java文件上面这个函数
OnlineProcessor.getInstance().removeUser(user_id);
改为
                    if(OnlineProcessor.getInstance().getOnlineSession(user_id).hashCode() == session.hashCode())
                            OnlineProcessor.getInstance().removeUser(user_id);

签名: 该会员没有填写今日想说内容.
引用:JackJiang 发表于 2017-07-18 15:52
解决了吗?

[INFO] - [15:51:43.512][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:46.511][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:49.511][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:52.515][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:55.514][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)在另一个设备重登陆的时候,为什么要调用完这5次,才会再次调用public void putUser(String user_id, IoSession session) ,在上面全部调用完前登陆的话,会把自己给T下来,但是等上面5次重发送结束之后,互踢就一切正常了
签名: 该会员没有填写今日想说内容.
引用:kelefun 发表于 2017-07-17 09:39
你好,你实现功能了吗?能否贴个示例代码?

没实现呢,发生了奇怪的事情
签名: 该会员没有填写今日想说内容.
引用:tiandao 发表于 2017-07-18 15:48
try {
                                IoSession oldsession = onlineSessions ...

解决了吗?
引用:JackJiang 发表于 2017-07-17 11:37
按我16搂的思路理论上是可以实现的。你的问题3里的想法是一个思路,但是会把问题复杂化,没有必要。

...

public void putUser(String user_id, IoSession session)
        {
                if(onlineSessions.containsKey(user_id))
                {
                        logger.debug("[IMCORE]【注意1】用户id="+user_id+"已经在在线列表中了,session也是同一个吗?"
                                        +(onlineSessions.get(user_id).hashCode() == session.hashCode()));
                        logger.debug("[IMCORE]【注意2】用户id="+user_id+onlineSessions.get(user_id)+session);
                        try {
                                IoSession oldsession = onlineSessions.get(user_id);
                                logger.debug("-->发送结果:"+LocalSendHelper.sendData(oldsession,ProtocalFactory.createCommonData(
                            "logout", "0", user_id, false, null, 5)));
                                oldsession.close(true);
                                onlineSessions.remove(user_id);
                        } catch (Exception e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                        // TODO 同一账号的重复登陆情况可在此展开处理逻辑
                }
               
                onlineSessions.put(user_id, session);
               
                __printOnline();// just for debug
        }
发生了奇怪的事情,有些时候能够成功互踢,有些时候会把自己给T下来(自己登陆成功后几秒钟自己收到了logout下线指令,但这个指令是发给另一个设备的),另一个账号T不下来。还有为什么互踢的时候每次都会出现下面这个情况
[INFO] - [15:51:43.512][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:46.511][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:49.511][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:52.515][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
[INFO] - [15:51:55.514][IMCORE]toSession==null >> id=0的用户尝试发给客户端yangqj的消息:str={}因接收方的id已不在线,此次实时发送没有继续(此消息应考虑作离线处理哦). | (LocalSendHelper^sendData:86)
而且2个设备在很短时间内相继重登陆,会出现不走这个函数的情况。太诡异了


签名: 该会员没有填写今日想说内容.
引用:kelefun 发表于 2017-07-17 10:56
非常希望您能提供一个实现互踢逻辑的demo.

我之前有试过,没有达到效果,(刚接触这个sdk没多久,理解的不 ...

按我16搂的思路理论上是可以实现的。你的问题3里的想法是一个思路,但是会把问题复杂化,没有必要。

其实MINA内部本来就是一条连接一个session(就是socket句柄了),OnlineProcessor只是MobileIMSDK加的在线列表逻辑而已,也就是说你只要针对某个sesion进行操作,它的消息一定可以发过去,不会影响别的连接。
另外,旧的session你可以关掉它,MINA有api,Mobileimsdk里也有关闭的代码,你看看就明白了。这些并没有你想象的复杂。

最佳实践一定是优雅而简单的,如果你觉得复杂,那一定是还没有理解透,那应该换个思路。
引用:JackJiang 发表于 2017-07-17 10:20
UserProcessor只在MobileIMSDK v2版里有,在v3里对应的是OnlineProcessor,我说错了。

我有空了在下一 ...

非常希望您能提供一个实现互踢逻辑的demo.

我之前有试过,没有达到效果,(刚接触这个sdk没多久,理解的不是很透)

遇到的问题 1:  服务器主动断开之前登录的socket,如何能保证旧设备收到"被踢"指令(以便Release---释放客户端监听)?
问题2:如果想要不影响新登录的账号,应该在建立新的session之前,处理掉之前旧设备的session,不知道如何做到
问题3:有想过把之前设备登录的userId重命名,然后让它自己慢慢处理退出指令,不知道能不能这样做?

总之 还是希望您能提供一个demo
引用:kelefun 发表于 2017-07-17 09:38
谢谢回复,我想要的逻辑是,同一账号只能在一个设备登录,后登录的把之前登录的踢掉 (新登录的设备无感知).
...

UserProcessor只在MobileIMSDK v2版里有,在v3里对应的是OnlineProcessor,我说错了。

我有空了在下一个版本实现一个互踢的参考逻辑吧,不过现在你可以这样去改试试:

1)当第2个客户端(同一个账号)登陆时,服务端在OnlineProcessor里立即像这个socket推出一条“被踢”指令,同时立即断开此socket(你可以测试一下,如果立即断开socket会导致指令还没能发到客户就断开的话,就稍等个几毫秒再断开此socket)并在OnlineProssor里清掉这个已被断掉的socket;
2)客户端在收到“被踢”指令后,不发sendLoginout指令,但ClientCoreSDK.release()方法是一定要调用的:目的是关掉客户端正在运行的im后台线程和网络监听。

你按我的思路试试看。
引用:tiandao 发表于 2017-07-14 16:27
搜嘎,我去研究up的最新版

你好,你实现功能了吗?能否贴个示例代码?
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部