美文网首页程序员Android知识
Xmpp学习之Android-smack入门指导

Xmpp学习之Android-smack入门指导

作者: Cavalier123 | 来源:发表于2017-06-04 15:13 被阅读0次

    版权声明:本文为博主原创文章,未经博主允许不得转载。

    转载请表明出处:http://www.jianshu.com/p/049ef7d049eb

    [TOC]
    WTF简书不支持 toc 目录模式,简单截图一张。


    img-w140img-w140

    在此为后面的smack学习做笔记,以作备忘。
    以下是本次采用的Demo环境:

    • Openfire 4.3.8.2
    • smack4.2.1

    smack之登录

    xmpp首次登录,可以通过自定义的Socket去进行连接服务器,如果你服务器不是配置了非常特殊TLS连接,一般可以是用Smack中的XMPPConnection类建立连接(我推荐通过API中的XMPPConnection去连接,能省去很多复杂的过程),下面我讲一下XMPPConnection的连接配置。

    基础配置

    XMPPConnection的连接需要通过XMPPTCPConnectionConfiguration.builder()配置你在Openfire设置的配置,代码如下:

    XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
    builder.setXmppDomain("server domain");
    builder.setHostAddress(InetAddress.getByName("you host address"));
    //default port 5222
    builder.setPort(you port);
    builder.setDebuggerEnabled(true);
    builder.setCompressionEnabled(true);
    builder.setSendPresence(false);
    builder.setUsernameAndPassword("username", "password");

    像如上代码中的,XmppDomain是必须配置(这里的```XmppDomain```会拼接在首次建立```Socket```的IQ中的to和from中),HostAddress是你运行服务器的域名或IP地址,Port是你登录的端口,在Openfire中默认是5222端口,但如果你的服务器使用的是Nginx做前置机,那么这里你需要填入你的Nginx配置的端口。  
    
    ### 进阶配置
    可能看到这里,你会觉得很奇怪,为什么还有个进阶配置,难道Openfire难道登录就是一个普通的Socket登录吗?这样很容易被黑吧,没错,实际上还有进阶配置满足加密连接和自定义的要求,如TLS登录或SSL登录或者压缩通讯,如下例子: 
    
    * 如自定义的TLS登录  
    TLS的登录方式,具体笔者也没实践过,欢迎拍砖。
    
    ```Java
    SSLContext sslContext = SSLContext.getInstance("TLS");
    
    MemorizingTrustManager mtm = new MemorizingTrustManager(getApplicationContext());
    
    MemorizingKeyManager memorizingKeyManager = new MemorizingKeyManager(getApplicationContext(), "123456");
    
    sslContext.init(new X509KeyManager[]{(X509KeyManager) memorizingKeyManager}
       , new X509TrustManager[]{mtm}, new java.security.SecureRandom());
       
    builder.setCustomSSLContext(sslContext);
    
    builder.setHostnameVerifier(mtm.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier())); 
    

    以上的代码需要注意一点是内中的MemorizingTrustManager,他来自于MemorizingTrustManager,而另外一个MemorizingKeyManager是自定义的类,内中的代码其实就是自定义了一个继承自X509TrustManager的类,这里不做深究,如有需要可以联系我。

    • 使用旧的TLS连接
      实际上这里的意思是关闭安全模式,可以通过如下代码关闭安全模式
    * 其他的兼容方式  
        实际上就是沿用上面的那个函数```builder.setSecurityMode```中填入的另外两种方式,以下是这三种方式的注释和描述:
        
    ```JavaDoc
    /**
     * Security via TLS encryption is required in order to connect. If the server
     * does not offer TLS or if the TLS negotiation fails, the connection to the server
     * will fail.
     */
    required,
    
    /**
     * Security via TLS encryption is used whenever it's available. This is the
     * default setting.
     * <p>
     * <b>Do not use this setting</b> unless you can't use {@link #required}. An attacker could easily perform a
     * Man-in-the-middle attack and prevent TLS from being used, leaving you with an unencrypted (and
     * unauthenticated) connection.
     * </p>
     */
    ifpossible,
    
    /**
     * Security via TLS encryption is disabled and only un-encrypted connections will
     * be used. If only TLS encryption is available from the server, the connection
     * will fail.
     */
    disabled
    
    • 开启通讯压缩
      这里的通讯压缩开启后,传输的流量将节省90%
    builder.setCompressionEnabled(true);
    
    • SASL认证
      Openfire服务器默认支持 PLAIN 、ANONYMOUS 、JIVE-SHAREDSECRET,这里我的服务器开启的是 PLAIN ,所以我使用 PLAIN
    SASLAuthentication.blacklistSASLMechanism(SASLPlainMechanism.NAME);
    

    登录服务器

    通过了上面的配置后,咱们可以登录Openfire系统了,相当简单:

    AbstractXMPPConnection xmpptcpConnection = new XMPPTCPConnection(builder.build());
    if (!xmpptcpConnection.isConnected()) {
        xmpptcpConnection.connect();
    }else{
        System.out.println("Already connected");
    }
    

    如果以上的配置和服务器的自定义不匹配,在建立连接的这一步会抛出异常,具体的异常可以通过对应方案处理,这里不详谈。

    登录底层报文通讯简要解析

    作为程序猿,难道会使用API够了吗?不,我们继续折腾,下面一起来看一下底层的通讯报文流程。

    • 在建立了Socket后,client会向服务器发出一条xml
    SENT
    <stream:stream 
        xmlns='jabber:client'   
        to='server domain' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0' 
        from='username@server domain' 
        xml:lang='en'>
    
    • 服务器解析到上面的指令后,会返回用于告诉client可选的SASL方式
    RECV
    <?xml version='1.0' encoding='UTF-8'?>
        <stream:stream xmlns:stream="http://etherx.jabber.org/streams"  
        xmlns="jabber:client" 
        from="im" 
        id="c997c3a8" 
        xml:lang="en" 
        version="1.0">
            <stream:features>
            <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">
            </starttls>
            <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
                <mechanism>PLAIN</mechanism>
                <mechanism>ANONYMOUS</mechanism>
                <mechanism>JIVE-SHAREDSECRET</mechanism>
            </mechanisms>
            <compression xmlns="http://jabber.org/features/compress">
                <method>zlib</method>
            </compression>
            <auth xmlns="http://jabber.org/features/iq-auth"/>
            <register xmlns="http://jabber.org/features/iq-register"/>
        </stream:features>
    
    • 客户端选择ANONYMOUS认证方式
    SENT
    <auth 
        xmlns='urn:ietf:params:xml:ns:xmpp-sasl'    
        mechanism='ANONYMOUS'>=</auth>
    
    • 服务器通过计算加密后的密码后,服务器将返回
    RECV
    <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
    
    • 当客户端收到以上命令后,将首次发起连接的id发送到服务器
    SENT
    <stream:stream 
        xmlns='jabber:client' 
        to='server domain' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0'
        from='username@server domain' 
        id='c997c3a8' 
        xml:lang='en'>
    
    • 这时服务器会返回如下内容说明此时的已经成功绑定了当前的Socket,
    RECV
    <?xml version='1.0' encoding='UTF-8'?>
        <stream:stream 
            xmlns:stream="http://etherx.jabber.org/streams" 
            xmlns="jabber:client" 
            from="im" 
            id="c997c3a8" 
            xml:lang="en"
            version="1.0">
                <stream:features>
                    <compression 
                        xmlns="http://jabber.org/features/compress">
                        <method>zlib</method>
                    </compression>
                    <bind 
                        xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
                        <session 
                            xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
        </stream:features>
    
    • 客户端在接收到如上的内容后会告诉服务器开启压缩
    SENT
    <compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>
    
    • 服务器返回
    RECV
    <compressed xmlns='http://jabber.org/protocol/compress'/>
    
    • 客户端收到服务器的响应命令后,重新建立一个Socket,发送指令
    SENT
    <stream:stream 
        xmlns='jabber:client'       
        to='server domain' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0' 
        from='username@server domain'  
        id='c997c3a8' 
        xml:lang='en'>
    
    • 服务器将返回,不知道你有没有发现,这里的id="c997c3a8"还是那个id
    RECV
    <?xml version='1.0' encoding='UTF-8'?>
        <stream:stream 
            xmlns:stream="http://etherx.jabber.org/streams" 
            xmlns="jabber:client" 
            from="im" 
            id="c997c3a8" 
            xml:lang="en" 
            version="1.0">
            <stream:features>
                <mechanisms 
                xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
                    <mechanism>PLAIN</mechanism>
                    <mechanism>ANONYMOUS</mechanism>
                    <mechanism>JIVE-SHAREDSECRET</mechanism>
                </mechanisms>
                <bind 
                    xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
                    <session 
                        xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
        </stream:features>
    
    • 实际上到这里客户端的登录已经完成了,但是还没算成功,接下来可以开始做绑定Socket的操作了
    SENT
    <iq 
        id='b86j8-4' 
        type='set'>
            <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
            </bind>
    </iq>
    
    • 服务器返回绑定了JID = c997c3a8的客户端
    RECV
    <iq 
        type="result" 
        id="b86j8-4" 
        to="im/c997c3a8">
        <bind 
                xmlns="urn:ietf:params:xml:ns:xmpp-bind">
            <jid>c997c3a8@im/c997c3a8</jid>
        </bind>
    </iq>
    
    • 客户端继续请求,开启一个session
    SENT
    <iq id='b86j8-6' type='set'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>
    
    • 这时服务器返回
    RECV
    <iq 
        type="result" 
        id="b86j8-6" 
        to="c997c3a8@im/c997c3a8"/>
    
    • 到此,整个登录流程已经成功了,接下来可以做一些用户信息的获取等操作。

    结尾

    本篇带领大家了解如何利用smack登录Openfire和XMPP的报文流程,下一个章节将会带大家进入xmpp的里面,看看获取用户信息等具体的操作。

    引用

    https://download.igniterealtime.org/smack/docs/latest/documentation/gettingstarted.html/

    相关文章

      网友评论

        本文标题:Xmpp学习之Android-smack入门指导

        本文链接:https://www.haomeiwen.com/subject/eaivfxtx.html