Xmpp學習之Android-smack入門指導

Xmpp學習之Android-smack入門指導

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。java

轉載請代表出處:http://www.cnblogs.com/cavalier-/p/6940406.htmlandroid

快速導航git

尷尬TOC目錄模板無論用了,那就簡單來個目錄圖片。github

圖片


在此爲後面的smack學習作筆記,以做備忘。
如下是本次採用的Demo環境:apache

  • 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配置的端口。session

進階配置

可能看到這裏,你會以爲很奇怪,爲何還有個進階配置,難道Openfire難道登陸就是一個普通的Socket登陸嗎?這樣很容易被黑吧,沒錯,實際上還有進階配置知足加密鏈接和自定義的要求,如TLS登陸或SSL登陸或者壓縮通信,以下例子:less

  • 如自定義的TLS登陸
    TLS的登陸方式,具體筆者也沒實踐過,歡迎拍磚。
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(ConnectionConfiguration.SecurityMode.disabled);
  • 其餘的兼容方式
    實際上就是沿用上面的那個函數builder.setSecurityMode中填入的另外兩種方式,如下是這三種方式的註釋和描述:
/**
 * 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/

相關文章
相關標籤/搜索