xmpp协议

date: 2015.10.19; modification:2015.10.19

目录:

1 XMPP简介

XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议, 它用于即时消息(IM)以及在线现场探测. 它在促进服务器之间的准即时操作. 这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息, 即使其操作系统和浏览器不同.

XMPP的前身是Jabber, 一个开源形式组织产生的网络即时通信协议. XMPP目前被IETF国际标准组织完成了标准化工作.

在IETF 中, 把IM协议划分为四种协议, 即:

XMPP 和SIMPLE 两种协议是架构, 有助于实现IMPP协议所描述的规范. PRIM 最初是基于即时通信的协议, 与XMPP 和SIMPLE 类似, 但是己经不再使用

2 XMPP特性

3 XMPP层次

+----------+
|   XMPP   |
+----------+
|   SASL   |
+----------+
|   TLS    |
+----------+
|   TCP    |
+----------+
|   IP     |
+----------+

4 XMPP协议的组成

主要的XMPP 协议范本及当今应用很广的XMPP 扩展:

5 XMPP协议网络架构

XMPP是一个典型的C/S架构, 而不是像大多数即时通讯软件一样, 使用P2P客户端到客户端的架构, 也就是说在大多数情况下, 当两个客户端进行通讯时, 他们的消息都是通过服务器传递的(也有例外, 例如在两个客户端传输文件时). 采用这种架构, 主要是为了简化客户端, 将大多数工作放在服务器端进行, 这样, 客户端的工作就比较简单, 而且, 当增加功能时, 多数是在服务器端进行.

XMPP中定义了三个角色:

通信能够在这三者的任意两个之间双向发生. 服务器同时承担了客户端信息记录, 连接管理和信息的路由功能. 网关承担着与异构即时通信系统的互联互通, 异构系统可以包括SMS(短信), MSN, ICQ等. 基本的网络形式是单客户端通过TCP/IP连接到单服务器, 然后在之上传输XML, 工作原理是:

  1. 节点连接到服务器;
  2. 服务器利用本地目录系统中的证书对其认证;
  3. 节点指定目标地址, 让服务器告知目标状态;
  4. 服务器查找, 连接并进行相互认证;
  5. 节点之间进行交互.

6 XMPP各角色简介

6.1 XMPP客户端

XMPP 系统的一个设计标准是必须支持简单的客户端. 事实上, XMPP 系统架构对客户端只有很少的几个限制. 一个XMPP 客户端必须支持的功能有:

  1. 通过 TCP 套接字与XMPP 服务器进行通信;
  2. 解析组织好的 XML 信息包;
  3. 理解消息数据类型.

XMPP 将复杂性从客户端转移到服务器端. 这使得客户端编写变得非常容易, 更新系统功能也同样变得容易. XMPP 客户端与服务端通过XML 在TCP 套接字的5222 端口进行通信, 而不需要客户端之间直接进行通信.

基本的XMPP 客户端必须实现以下标准协议(XEP-0211):

6.2 XMPP服务器

XMPP 服务器遵循两个主要法则:

XMPP开源服务器一般被设计成模块化, 由各个不同的代码包构成, 这些代码包分别处理

另外, 服务器可以通过附加服务来进行扩展, 如完整的安全策略, 允许服务器组件的连接或客户端选择, 通向其他消息系统的网关.

基本的XMPP服务器必须实现以下标准协议.

6.3 XMPP网关

XMPP 突出的特点是可以和其他即时通信系统交换信息和用户在线状况. 由于协议不同, XMPP 和其他系统交换信息必须通过协议的转换来实现, 目前几种主流即时通信协议都没有公开, 所以XMPP 服务器本身并没有实现和其他协议的转换, 但它的架构允许转换的实现. 实现这个特殊功能的服务端在XMPP 架构里叫做网关(gateway). 目前, XMPP 实现了和AIM, ICQ, IRC, MSN Massager, RSS0.9 和Yahoo Massager 的协议转换. 由于网关的存在, XMPP 架构事实上兼容所有其他即时通信网络, 这无疑大大提高了XMPP 的灵活性和可扩展性.

7 XMPP协议分析

7.1 XMPP地址格式

一个实体在XMPP网络结构中被称为一个接点, 它有唯一的标示符jabber identifier(JID), 即实体地址, 用来表示一个Jabber用户, 但是也可以表示其他内容, 例如一个聊天室.

一个有效的JID包括一系列元素:

  1. 域名(domain identifier);
  2. 节点(node identifier);
  3. 源(resource identifier).

它的格式是node@domain/resource, node@domain, 类似电子邮件的地址格式. resource用来表示接点不同的设备或位置, 这个是可选的, 例如a在Server1上注册了一个用户, 用户名为doom, 那么a的JID就是doom@serverl, 在发送消息时, 指明doom@serverl就可以了, resource可以不用指定, 但a在登录到这个Server时, JID可能是doom@serverl/exodus(如果a用Exodus软件登录), 也可能是doom@serverl/psi(如果a用psi软件登录). 资源只用来识别属于用户的位置或设备等, 一个用户可以同时以多种资源与同一个XMPP服务器连接.

7.2 XMPP通讯格式

XMPP中定义了 3个顶层XML元素: Message, Presence, IQ, 下面针对这三种元素进行介绍.

7.2.1 Message

用于在两个jabber用户之间发送信息. Jsm(jabber会话管理器)负责满足所有的消息, 不管目标用户的状态如何. 如果用户在线jsm立即提交;否则jsm就存储.

Message结构如下所示:

<message to= ‘lily@jabber.org/contact’ type =’chat’>
    <body> 你好, 在忙吗</body>
</message>

7.2.2 Presence

用来表明用户的状态, 如: online, away, dnd(请勿打扰)等. 当用户离线或改变自己的状态时, 就会在stream的上下文中插入一个Presence元素, 来表明自身的状态. 结构如下所示:

<presence>
    From =‘lily @ jabber.com/contact’
    To = ‘yaoman @ jabber.com/contact'
    <status> Online </status>
</presence>

presence元素可以取下面几种值: * probe: 用于向接受消息方法发送特殊的请求 * subscribe: 当接受方状态改变时, 自动向发送方发送presence信息.

7.2.3 IQ

一种请求/响应机制, 从一个实体从发送请求, 另外一个实体接受请求, 并进行响应. 例如, client在stream的上下文中插入一个元素, 向Server请求得到自己的好友列表, Server返回一个, 里面是请求的结果.

iq 主要的属性是type. 包括:

结构如下所示:

<iq from =‘lily @ jabber.com/contact’id=’1364564666’ Type=’result’>

7.3 XMPP通讯流程分析

7.3.1 第一步: 打开 stream

Client: 客户端发送打开 stream 的片段到服务器, 请求一个新的 session.

<stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>  

这里 "example.com" 是客户端试图连接的服务器的域名.

Server: Server 返回 XML stream, 以 <stream:freatures> 开头, 包含要求 TLS 或者 SASL 协商谈判之一, 或者2个都要求.

<stream:features>  
    <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>  
        <required/>  
    </starttls>  
    <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>  
        <mechanism>DIGEST-MD5</mechanism>  
        <mechanism>PLAIN</mechanism>  
        <mechanism>EXTERNAL</mechanism>  
    </mechanisms>  
</stream:features>  

7.3.2 第二步: 加密和认证.

7.3.2.1 如果服务器需要 TLS 交涉.

Client: 客户端发送 STARTTLS 到服务器.

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

Server: 服务器返回消息显示 TLS 已被允许:

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

或者 TLS失败了:

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

在失败的情况下, 服务器会关闭 TCP 连接.

Client: 如果 TLS 已被服务器正确处理, 客户端发送请求一个新的 session:

<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>  

Server: 服务器响应一个 XML stream, 指示是否需要 SASL 交涉.

<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='c2s_234' version='1.0'>  
<stream:features>  
    <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>  
        <mechanism>DIGEST-MD5</mechanism>  
        <mechanism>PLAIN</mechanism>  
        <mechanism>EXTERNAL</mechanism>  
    </mechanisms>  
</stream:features>  

7.3.2.2 SASL 交涉

Client 客户端需要选择一个服务器上有效的认证方式来携带SASL交涉数据, 上面的情况, "DIGEST-MD5", "PLAIN" 和 "EXTERNAL" 是一些可选项.

"PLAIN" 认证模式是三者之中最简单的了. 它是这样工作的:

Client: 客户端按照自己选择的认证模式发送一个将用户名和密码以base64编码的 stream. 用户名和密码按这种格式组织:

"\0UserName\0Password".  

例如我想以用户名为"mbed@ceit.org"登录, 密码是"mirror". 那么, 在进行base64编码之前, 用户名和密码按照上面的格式组织为一个新的字符串,"mbedmirror", 再进行base64编码, 得到字符串"AG1iZWQAbWlycm9y".

然后, 客户端发送下列 stream 到服务器.

<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AG1iZWQAbWlycm9y</auth>  

Server: 如果服务器接受了认证信息, 服务器会发回 带 "success" 标签的 stream.

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

或者:

Server: 如果密码和用户名不匹配, 或者上面的base64编码有错误, 服务器发回错误信息的 stream.

<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>  

"DIGEST-MD5" 认证模式的具体方法可以在这里找到: http://www.ietf.org/rfc/rfc2831.txt.

7.3.3 第三步: 资源绑定(可选)

Client: 客户端要求服务器绑定一个资源(可以理解为客户端的类型, 比如电脑, 手机, Web应用等):

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

或者

Client: 客户端自己绑定一个资源:

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

Server: 服务器发回另外一个 <iq> 片段, 如果"type" 标签的内容是"result", 说明绑定是成功的, 否则说明绑定失败.

<iq type='result' id='bind_2'>  
    <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>  
        <jid>somenode@example.com/someresource</jid>  
    </bind>  
</iq>  

7.3.4 第四步: 请求一个新的session

在 SASL 交涉完成之后或者可选资源绑定之后, 客户端必须建立一个 session 来开始即时消息发送和接收.

Client: 客户端向服务器发送请求:

<iq to='example.com' type='set' id='sess_1'>  
    <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>  
</iq>  

Server: 服务器发回一个 片段表明 session 是否成功创建.

创建成功的消息类似于:

<iq from='example.com' type='result' id='sess_1'/>  

如果服务器未能创建 session, 服务器将会回复一个如下消息或者其他类型的错误消息.

<iq from='example.com' type='error' id='sess_1'>  
    <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>  
    <error type='auth'>  
        <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>  
    </error>  
</iq>  

7.3.5 第五步: 客户端和服务器交换 XMPP 片段

如果以上步骤均成功完成, 那么客户端就可以发送 XMPP 片段到服务器和接收 XML stream了.

客户端可以发送 <iq> 片段来向服务器请求 roster 或者其他信息. 并可以使用 <presence> 片段来改变客户端的 presence 状态(比如在线, 离开等)

即时消息和其他的负载可以通过发送 <message> 片段来完成.

7.3.6 第六步: 关闭 stream

最后, 如果客户端想要结束聊天和关闭 XMPP session, 客户端需要发送一个关闭 stream的片段到服务器.

<presence type='unavailable'/>  

然后, 服务器将会改变客户端的 presence 状态为 "Offline" , 并且关闭 和客户端的 TCP 连接.

8 参考

http://no001.blog.51cto.com/1142339/1317759

http://blog.csdn.net/huyoo/article/details/24353105