3. XMPP 核心数据包
XMPP 的核心数据包类型有Precense,Message,Iq ,此外加上初始化 stream 用到的 Stream 数据包。这些数据包是 XMPP 信息传输的载体,被用于 XMPP 核心功能和扩展功能的实现。
该部分仅对 XMPP 中使用的数据包进行概览,用于感受基于 XML 的数据包与其他非 XML 数据包协议的差别,不能替代 IETF 关于 XMPP 协议的 RFC 文档 [3920] [3921][3][4],以及 XMPP 的扩展协议文档 [extensions][5] 中描述。
3.1 公有属性
在 XML stream 中,每个数据包都是 XML 格式纯文本。而每个 XML 数据包有以下公有属性:
- to 数据包要发送的目的地址
- from 数据包发送的源地址
- id 数据包标示符
此三项属性在 XML stanza 中最为常见。
to 和 from 属性用于服务器决定该数据包的路由规则。某些情况下,to 和 from 属性可以只有一个,例如:客户端向服务端发送设置配置的 Iq 包只含有 to (不向外路由),客户端向联系人发送 Message 只含有 to (from 属性总是被改写为客户端的地址)。
id 用于节点间判断请求和应答数据包的对应状况,大多数情况可以不处理。
3.2 初始化 XML stream,身份验证
在客户端与服务器产生 TCP 连接后,需要与服务器初始化 XML stream,以及进行身份验证。
初始化时,客户端发送 stream 头部 XML:
<?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
服务器在收到客户端的 stream 头后,回应一个 stream 头:
<?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
接着服务器向客户端发送服务端支持的身份验证方式列表,常见的方式有基于安全传输 SASL 的 BASE64 编码账户密码验证。身份验证的种类多样,且过程较为繁琐,可以参考 《XMPP: The Definitive Guide》第12章的介绍。
在对话结束时,客户端和服务端要先后发送 stream 尾部 XML,以使整个 XMP stream 闭合。(如果 TCP 异常中断,则服务端直接中断对话)
客户端:
</stream:stream>
服务端:
</stream:stream>
3.3 Roster 获取联系人列表
在即时聊天 (IM) 应用中,客户端登录服务器后做的第一个操作通常是获取联系人列表。获取联系人列表需要发送 get 类型的 Iq 数据包。(Iq数据包将会在3.6节解释)
客户端:
<iq from='juliet@example.com/balcony' type='get' id='roster_1'>
<query xmlns='jabber:iq:roster'/>
</iq>
该请求的意义为:名为 juliet 的用户 (登录资源为 balcony) 向 example.com 服务器请求获得 (get) roster 表。
服务器收到请求后,返回 roster 表。
服务端:
<iq to='juliet@example.com/balcony' type='result' id='roster_1'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='Romeo'
subscription='both'>
<group>Friends</group>
</item>
<item jid='mercutio@example.org'
name='Mercutio'
subscription='from'>
<group>Friends</group>
</item>
<item jid='benvolio@example.org'
name='Benvolio'
subscription='both'>
<group>Friends</group>
</item>
</query>
</iq>
可以看到,juliet 的 roster 表内有3个联系人,分别名为 Romeo,Mercutio,Benvolio,都属于 Friends 分组。Roster 列表中的 JID 信息将会用在稍候客户端发送信息包的目的地址中。
Item 中的 subscription 关系到联系人状态信息的传输,有 none,both,from,to 四种。详细的 subscription 操作在 RFC 3921Managing Subscriptions 章节[7]中定义。
3.4 Presence 状态数据包
Presence 数据包被设计用来发送轻量级的节点状态信息,例如 IM 中用来发送用户的在线、离开、忙碌、离线等简单的状态消息。
一个 presence 数据包范例如下:
客户端:
<presence>
<show>away</show>
</presence>
该 presence 向服务端表明,客户端用户处于离线状态。服务端收到 presence 后,会自行填充 from 和 to 属性,发送到订阅 (subscribeb) 了该用户状态信息的联系人服务器上。
Presence 数据包也可以在发送时填上 to 属性,用于指定 presence 的接收方。例如:
客户端:
<presence to="romeo@example.net">
<show>away</show>
</presence>
该 Presence 只会被转发至 romeo@example.net。
3.5 Message 信息数据包
Message 是即时聊天应用中最常用的数据包,其功能是发送用户聊天信息。一个 Message 例子如下:
客户端:
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Wherefore art thou,Romeo?</body>
</message>
该 message 包将会被服务器转发至 example.net 服务器,随后转交给 romeo 已登录的客户端上(如果该用户没有登录,message 信息会储存在服务端直至用户上线)。
要传输格式化的富文本信息,可以通过支持扩展 XEP-0071[8],引入 html 标签。
3.6 Iq 数据包
Iq 是一个为其他操作提供 get,result,set,error 动作的数据包,本身并没有限定用途的范围。核心协议中就有大量需要用到 Iq 数据包的操作,例如添加 roster 联系人,需要用到 set 类型的 Iq 数据包:
客户端:
<iq from='juliet@example.com/balcony' type='set' id='roster_2'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
</item>
</query>
</iq>
服务器用 result 类型的 Iq 数据包返回操作结果:
服务端:
<iq to='juliet@example.com/balcony' type='result' id='roster_2'/>
XMPP 扩展协议大都通过扩展 Iq 数据包的元素来添加操作。Iq 数据包的 type 属性就像 HTTP 协议中的 Method 项一样,提供增删改查 (CURD) 动作,具体实现何种操作并不限制。
[3] http://tools.ietf.org/html/rfc3920
[4] http://tools.ietf.org/html/rfc3921
[5] http://xmpp.org/extensions/
[6] http://books.google.com/books?id=SG3jayrd41cC&lpg=PP1&pg=PA165#v=onepage&q&f=false
[7] http://tools.ietf.org/html/rfc3921#page-26
[8] http://xmpp.org/extensions/xep-0071.html