android移动端xmpp开发入门

引入###

即时通信技术现在已经广泛应用到各种app中了,那么他到底是怎么实现的呢。前段时间因为项目开发的需要,了解学习过一些这方面。这篇文章记录的是我个人了解的一些这方面的知识,如果有错误的地方还请斧正指出,非常感谢。
另外 慕课网中 郭大神的关于xmpp的这个课程也对我学习起到了很大的帮助:http://www.imooc.com/learn/223

xmpp###

  • XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。XMPP目前被IETF国际标准组织完成了标准化工作。标准化的核心结果分为两部分; 核心的XML流传输协议 基于XML流传输的即时通讯扩展应用 XMPP的核心XML流传输协议的定义使得XMPP能够在一个比以往网络通信协议更规范的平台上。借助于XML易于解析和阅读的特性,使得XMPP的协议能够非常漂亮。 XMPP的即时通讯扩展应用部分是根据IETF在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如AIM,QQ等有功能完整,完善等先进性。
  • 上面是xmpp的官方解释,其实我每次看完官方解释,总是懵懵的。好像什么都知道了,也好像什么都不知道。不过在我了解过之后简单总结看来,xmpp就像上面说的,是一套协议。里面定义了很多xml格式的文本。比如当我们要登录到服务器时,怎么告诉服务器呢。按照xmpp协议中的规范封装一段xml的字符串以流的方式传给服务器就好了。以最简单的发送一个消息举例:
   <message
       to='romeo@example.net'
       from='juliet@example.com/balcony'
       type='chat'
       xml:lang='en'>
     <body>Wherefore art thou, Romeo?</body>
   </message>
  1. suke 发送给 beta 一条消息,消息的类型是chat(聊天),消息的内容是 who are you ?。
  • 总结以上,在我理解看来,xmpp 就像是我们平时说话使用的语言,服务器和客户端都遵循这样的规范,比我message 代表一个消息。这样到客户端想给服务器发送一条消息时,就拼装一个以message开头的xml字符串流到服务器。服务器按照xmpp的协议,就知道这是一条消息。听起来感觉傻乎乎的。可是xmpp就是这样的。
  • 如果想深入了解xmpp协议,请搜索《xmpp权威指南》,英文版本和中文版本的都有。

IM即时通信软件该怎么做###

  • 目前网上比较主流的是采用开源的 openfire服务器+smack sdk 进行开发。smack是一个封装了tcp连接和xmpp协议解析的sdk。
    smack的资料可以移步github:https://github.com/igniterealtime/Smack/wiki/Smack-4.2-Readme-and-Upgrade-Guide
    里面提供了详尽的API
  • 我们都知道即时通信是需要做长连接的,而openfire和smack之间的连接采用的是tcp长连接。大家一定都知道心跳包和保持在线,断线重连之类的,我刚开始入手的时候也不知道怎么弄。看了文章开头郭大神的那个基于xmpp的推送后,对于其中的原理不能说豁然开朗吧,也算窥探到了一二。我在这里就不赘述了,那个视频已经讲的很明白了。

Smack怎么用###

  • 连接到服务器。

    • Constant.HOST_SERVER 为服务器IP地址,运行demo时需要修改。
                        InetAddress addr = InetAddress.getByName(Constant.HOST_SERVER);
                      HostnameVerifier verifier = new HostnameVerifier() {
                          @Override
                          public boolean verify(String hostname, SSLSession session) {
                              return false;
                          }
                      };
                      DomainBareJid serviceName = JidCreate.domainBareFrom(Constant.HOST_SERVER);
                      XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                              .setHost(Constant.HOST_SERVER) // it will be resolved by setHostAddress method
                              .setUsernameAndPassword(username, pwd)
                              .setPort(Constant.HOST_PORT)
                              .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                              .setXmppDomain(serviceName)
                              .setHostnameVerifier(verifier)
                              .setHostAddress(addr)
                              .setDebuggerEnabled(true)
                              .build();
    
                      connection = new XMPPTCPConnection(config);
                      connection.connect();
    
  • 连接之后,就是登录和获取好友列表(花名册)了。具体的实现

    • roster.addRosterLoadedListener(new RosterLoadedListener() {});这个是添加一个监听,因为再刚登录的时候,直接去获取花名册,服务器返回的经常为空集合。网上有人建议在登录成功之后让线程睡100毫秒,不过还是添加listener优雅一点。

           if (connection.isConnected()) {
                        try {
                            connection.login();
                        } catch (Exception e) {
                            android.os.Message msg = android.os.Message.obtain();
                            msg.what = LOGIN_FAILED;
                            handler.sendMessage(msg);
                        }
                        // Thread.sleep(100);
                        Roster roster = Roster.getInstanceFor(connection);
                        roster.addRosterLoadedListener(new RosterLoadedListener() {
                            @Override
                            public void onRosterLoaded(Roster roster) {
                                Logger.e(TAG,"Get roster success ... " );
                                android.os.Message msg = android.os.Message.obtain();
                                msg.what = LOGIN_SUCCESS;
                                msg.obj = roster;
                                handler.sendMessage(msg);
                            }
      
                            @Override
                            public void onRosterLoadingFailed(Exception exception) {
                                Logger.e(TAG,"Get roster failed : " + exception.getMessage());
                            }
                        });
                        android.os.Message msg = android.os.Message.obtain();
                        msg.what = LOGIN_SUCCESS;
                        msg.obj = roster;
                        handler.sendMessage(msg);
                    } else {
                        android.os.Message msg = android.os.Message.obtain();
                        msg.what = CONNECTION_FAILED;
                        handler.sendMessage(msg);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
      
  • 创建聊天室

        private void createChatRoom() {
            // 设置聊天监听器,监听聊天消息
            chatManager = ChatManager.getInstanceFor(connection);
            chatManager.addIncomingListener(mIncomingChatMessageListener);
        }
        
    IncomingChatMessageListener mIncomingChatMessageListener = new IncomingChatMessageListener() {
    //接收消息
        @Override
        public void newIncomingMessage(EntityBareJid from, Message message, org.jivesoftware.smack.chat2.Chat chat) {
            String str = chatMap.get(from.toString());
            String name = from.getLocalpart().toString();
            String msg = message.getBody();
            String type = message.getType().toString();
            if (str == null) {
                str = name + " : " + type + " : " + msg + "\n";
            } else {
                str += name + " : " + type + " : " + msg + "\n";
            }
            chatMap.put(from.toString(), str);
            mainBinding.tvChatCon.setText(str);
        }
    };
    
    //发送消息
    private void sendmsg(String msg, RosterEntry rosterEntry, Message.Type type) {
        Chat currentChat;
        try {
            String jidStr = rosterEntry.getJid().toString();
            EntityBareJid jid = JidCreate.entityBareFrom(jidStr);
            currentChat = chatManager.chatWith(jid);
            Message newMessage = new Message();
            newMessage.setBody(msg);
            newMessage.setType(type);
            currentChat.send(newMessage);
    
            String str = chatMap.get(jidStr);
            if (str == null) {
                str = username + " : " + msg + "\n";
            } else {
                str += username + " : " + msg + "\n";
            }
            chatMap.put(jidStr, str);
            mainBinding.tvChatCon.setText(str);
            mainBinding.etWillSendMsg.setText("");
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (XmppStringprepException e) {
            e.printStackTrace();
        }
    }
    

    总结###

    其实对于xmpp,我现在也只是略微了解一些,上面这些,希望对有这方面需求的同学有所帮助。郭大神的那个视频和xmpp权威指南真的很值得阅读。视频有助于理解客户端与服务器的连接实现,而xmpp权威指南中会告诉你xmpp协议都定义了那些功能(节的概念,发送消息,出席状态,状态订阅,多设备登录哪个优先,发送文件是怎么实现的,为什么只能发送小文件等等很多很多),了解了这些功能也有助于使用smack这个sdk,同时给扩展做好准备。
    好了,希望以上这些,可以对大家有些许帮助,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容