即時通信系統Openfire分析之一:Openfire與XMPP協議


   引言

  目前互聯網產品使用的即時通信協議有這幾種:即時信息和空間協議(IMPP)、空間和即時信息協議(PRIM)、針對即時通訊和空間平衡擴充的進程開始協議SIP(SIMPLE)以及XMPP。PRIM與XMPP、SIMPLE類似,但已經不再使用了。

  本次要講的是XMPP,由Openfire實現.

  1、Openfire與XMPP

  Openfire是開源的實時協作服務器(RTC),它是基於公開協議XMPP(RFC-3920),並在此基礎上實現了XMPP-IM(RFC-3921),擴展了IM功能,對實施協作的各種場景做較全面的考慮,如用戶在線狀態切換、消息訂閱、通知等等,因此可以用來搭建即時通信服務器,其搭建的方法也很簡易。

  RFC-3920與RFC-3921的說明:

  (1)RFC-3920:XMPP的核心。定義了XMPP協議框架下應用的網絡架構,引入了XMLStream(XML流)與XMLStanza(XML節),並規定XMPP協議在通信過程中使用的XML標簽。使用XML標簽從根本上說是協議開放性與擴展性的需要。此外,在通信的安全方面,把TLS 安全傳輸機制與SASL 認證機制引入到內核,與XMPP進行無縫的連接,為協議的安全性、可靠性奠定了基礎。Core文檔還規定了錯誤的定義及處理、XML 的使用規范、JID(Jabber Identifier,Jabber 標識符)的定義、命名規范等等。所以這是所有基於XMPP協議的應用都必需支持的文檔。

  (2)RFC-3921:用戶成功登陸到服務器之后,發布更新自己的在線好友管理、發送即時聊天消息等業務。所有的這些業務都是通過三種基本的XML 節來完成的:IQ Stanza(IQ 節), Presence Stanza(Presence 節), Message Stanza(Message 節)。RFC3921 還對阻塞策略進行了定義,定義是多種阻塞方式。可以說,RFC3921 是RFC3920的充分補充。兩個文檔結合起來,就形成了一個基本的即時通信協議平台,在這個平台上可以開發出各種各樣的應用。

  2、Openfire的特點:

 (1)超強的擴展能力

  XMPP協議,繼承了在XML靈活的擴展性,通過擴展發送擴展信息、或者在原有的信息中增加擴展節點來處理用戶需求。另外,Openfire本身也支持插件開發,開發者可以根據需求,以插件的形式添加所需要的功能,例如好友列表、群成員列表等,而不需要去修改核心的源代碼。

(2)並發能力

  Openfire的通信處理基於Apache MINA框架實現,單機可支持上萬的並發,同時也支持集群。

(3)安全性:

  XMPP在C2S通信,和S2S通信中都使用TLS協議作為通信通道的加密方法,保證通信的安全

(3)對Web的支持:

  Openfire采用內置的jetty作web服務器,可以方便的在上面增加WEB功能。jetty服務器是隨AdminConsolePlugin插件時啟動,通過調用startup()方法。9090為其明文端口,9091為其加密端口。

  3、通信機制

  1、帳號體系

  (1)XMPP服務器的帳號基礎,是域(Domain),例如:org.example.com,它在服務器配置時的時候設置,也是服務器能被訪問到的域名或IP地址。客戶端連接的時候,用這個域去尋找服務器。

  (2)JID:XMPP中,任何一個可能進行通信的實體,包括一個用戶、或者一個聊天室,都需要一個JID。

  用戶的JID格式為:usre@domain,如:abc@org.example.com

  聊天室的JID為:room@conference.domain, 如ABC@conference.org.example.com

  一般情況,JID后面還會附帶一個資源名(resource),指代這個客戶端的來源,比如:abc@org.example.com/pc-abc,表示這個JID在一台名為pc-abc的設備上登錄。這是同一個帳號能夠在多個設備中登錄的基礎。

  2、通信端口

  C-S連接的端口是5222 ,S-S連接的端口為5269,這些端口已經在IANA注冊。

  3、通信機制

  當客戶端連接上XMPP服務器創建會話時,首先是建立一個TCP長連接,並在這個連接上收發XML流進行協商,協商通過后,服務端與客戶端,可以通過Message、Presence、IQ這三種格式進行數據交換。

  這三種數據交換格式,下面逐個做介紹:

  (1)Message:基本的消息發送,不要求得到響應,用於即時通信、群組、通知等

  結構如下:

<message from='1002@zy-fordestiny' to='1001@zy-fordestiny' xml:lang='en'>
    <body>Are you OK?</body>
</message>

  其中:

  To : 消息接收方JID。

  from : 消息發送方JID

  body: 所要發送的消息。

  (2)Presence:用來表明用戶的狀態,當用戶離線或改變自己的狀態時,就會在stream的上下文中插入一個Presence元素,來表明自身的狀態.

  結構如下:

<presence from='1001@zy-fordestiny' to='1002@zy-fordestiny'>
    <status> Online </status>
</presence>

  (3)IQ:一種請求/響應機制,類似於Http的get請求。

  結構如下:(以客戶端請求服務器綁定資源為例)

<iq type='set' id='bind_1'>
     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</iq>

  iq消息中,type是主要屬性,值包括:

Get :獲取當前域值。
Set :設置或替換get查詢的值。
Result :說明成功的響應了先前的查詢。
Error: 查詢和響應中出現的錯誤。

 

  4、通信示例

  下面以一個客戶端登錄IM服務器的例子,說明客戶端與服務的交互過程。其中C-S表示客戶端往服務端發消息,S-C表示服務端往客戶端發消息。

  1、初始stream

  C-S:

<stream:stream xmlns='jabber:client' to='zy-fordestiny' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0'
        from='10023@zy-fordestiny' 
        xml:lang='en'>

  S-C:

<?xml version='1.0' encoding='UTF-8'?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" 
        xmlns="jabber:client" 
        from="zy-fordestiny" 
        id="34hpvqh6zz" 
        xml:lang="en" 
        version="1.0">

  S-C:通知客戶端STL認證

<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>SCRAM-SHA-1</mechanism>
        <mechanism>CRAM-MD5</mechanism>
        <mechanism>DIGEST-MD5</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>

  2、TLS協商

  C-S:

<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>

  S-C:通知客戶端可以進行STL協商

<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

  3、客戶端和服務器嘗試通過已有的TCP連接完成 TLS 握手

  4、如果 TLS 握手成功,客戶端初始化一個新的流給服務器

  C-S:

<stream:stream xmlns='jabber:client' to='zy-fordestiny' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0' 
        from='10023@zy-fordestiny' 
        xml:lang='en'>

  S-C:

<?xml version='1.0' encoding='UTF-8'?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" 
            xmlns="jabber:client" from="zy-fordestiny" id="34hpvqh6zz" 
            xml:lang="en" version="1.0">
        <stream:features>
    <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        <mechanism>PLAIN</mechanism>
        <mechanism>ANONYMOUS</mechanism>
        <mechanism>SCRAM-SHA-1</mechanism>
        <mechanism>CRAM-MD5</mechanism>
        <mechanism>DIGEST-MD5</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>

  4 開始SASL握手

  C-S:客戶端選擇一種驗證機制

<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' 
    mechanism='SCRAM-SHA-1'>biwsbj0xMDAyMyxyPUpkYjNr
    Vn5mLzYwYH05N0tdfmk2M3Q8Wj14cmdnIy1S</auth>

  S-C:

<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
    cj1KZGIza1Z+Zi82MGB9OTdLXX5pNjN0PFo9eHJnZyMtUmQ0ODQ3MGFjLWU4YjAtNG
    Q3NS05ZDAzLWI4NjE3MTI0ODlhYSxzPU5aQjJ2bzljcjlqNjFINEE5L1dLMWtOMVFjVnVJVFlkLGk9NDA5Ng==
</challenge>

  C-S:

<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
    Yz1iaXdzLHI9SmRiM2tWfmYvNjBgfTk3S11+aTYzdDxaPXhyZ2cjLVJkNDg0NzBh
    Yy1lOGIwLTRkNzUtOWQwMy1iODYxNzEyNDg5YWEscD11QStXMFBWQnFKWnNWQjVZd2pNRlVESGFGQjQ9
</response>

  S-C:通知客戶端驗證成功

<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
    dj02a1l6bkFDVHRLaGNEdVlVR1BlY1FWZ283RzQ9
</success>

  5、stream

  C-S:客戶端發起一個新的流

<stream:stream xmlns='jabber:client' to='zy-fordestiny' 
        xmlns:stream='http://etherx.jabber.org/streams' 
        version='1.0' from='10023@zy-fordestiny' 
        id='34hpvqh6zz' 
        xml:lang='en'>

  S-C:服務器發送一個流頭信息回應客戶端,並附上任何可用的特性,注意,多了session

<?xml version='1.0' encoding='UTF-8'?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" 
        xmlns="jabber:client" from="zy-fordestiny" id="34hpvqh6zz" 
        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">
        <optional/>
    </session>
    <sm xmlns='urn:xmpp:sm:2'/><sm xmlns='urn:xmpp:sm:3'/>
</stream:features>

  6. 資源綁定,重要!!

  C-S:

<iq id='h1q24-3' type='set'>
    <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
        <resource>ZY-fordestiny</resource>
    </bind>
</iq>

  S-C:

<iq type="result" id="h1q24-3" to="zy-fordestiny/34hpvqh6zz">
    <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
        <jid>10023@zy-fordestiny/ZY-fordestiny</jid>
    </bind>
</iq> 

  7、獲取花名冊

  C-S:

<iq id='h1q24-5' type='get'>
    <query xmlns='jabber:iq:roster'></query>
</iq>

  S-C:

<iq type="result" id="h1q24-5" to="10023@zy-fordestiny/ZY-fordestiny">
    <query xmlns="jabber:iq:roster"/>
</iq>

  8、發送disco#items,查詢server所支持的components

  C-S:

<iq to='zy-fordestiny' id='h1q24-6' type='get'>
    <query xmlns='http://jabber.org/protocol/disco#items'></query>
</iq>

  S-C:

<iq type="result" id="h1q24-6" from="zy-fordestiny" to="10023@zy-fordestiny/ZY-fordestiny">
    <query xmlns="http://jabber.org/protocol/disco#items">
        <item jid="pubsub.zy-fordestiny" name="Publish-Subscribe service"/>
        <item jid="conference.zy-fordestiny" name="公共房間"/>
        <item jid="broadcast.zy-fordestiny" name="Broadcast service"/>
        <item jid="proxy.zy-fordestiny" name="Socks 5 Bytestreams Proxy"/>
    </query>
</iq>  

  9、獲取名片,群組

  C-S:

<iq id='h1q24-23' type='get'>
    <vCard xmlns='vcard-temp'/>
</iq>

  S-C:

<iq type="result" id="h1q24-23" to="10023@zy-fordestiny/ZY-fordestiny">
    <vCard xmlns="vcard-temp"/>
</iq>

  C-S:

<iq id='h1q24-24' type='get'>
    <sharedgroup xmlns='http://www.jivesoftware.org/protocol/sharedgroup'>
    </sharedgroup>
</iq>

  S-C:

<iq type="result" id="h1q24-24" to="10023@zy-fordestiny/ZY-fordestiny">
    <sharedgroup xmlns="http://www.jivesoftware.org/protocol/sharedgroup"/>
</iq>

  10、提交在線狀態

  C-S:

<presence id='h1q24-10'>
    <status>在線</status>
    <priority>1</priority>
    <c xmlns='http://jabber.org/protocol/caps' 
        hash='sha-1' 
        node='http://www.igniterealtime.org/projects/smack' 
        ver='njE08O0d+gOu+3R5iJiJSheFRMw='/>
</presence>

 

 


  

  可以看到,完整的登錄過程,需要經過多次的信息交換。

  關於XMPP的其他信息,可以網上查閱資源了解,這里不再贅述。

  

  后面的章節,將一步步的從Openfire的源代碼入手,分析整套系統的運行機制,望對讀者有幫助。


  over!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM