要促进消息传递,每个 XMPP 客户端用户必须拥有一个全局惟一标识符。基于历史原因,这些标识符称为 Jabber IDs,或称为 JIDs。鉴于这个协议的分布式特征,重要的是 JID 应包含联系用户所需的所有信息:不存在将用户链接到他们连接到的服务器的中央知识库。JID 的结构类似于电子邮件地址(但不要求 JID 同时也是有效的电子邮件收件人)。
客户端和服务器节点,我将它们统称为 XMPP 实体,都拥有 JIDs。SomeCorp 公司的员工 John Doe 可能拥有 JID John.Doe@somecorp.com。这里,somecorp.com 是 SomeCorp 公司的 XMPP 服务器的地址,John.Doe 是 John Doe 的用户名。
JIDs 还拥有连接到它们的资源。这允许在一个 XMPP 实体标识符之外进一步处理细粒度;例如,尽管上面的示例总体上能够表示 John Doe,但 John.Doe@somecorp.com/Work 可以用于将数据发送到与他的工作相关的工具。
这些资源可以采用任意用户定义的名称,一个 XMPP 实体可以拥有任意数量的资源。除了可以是上下文依赖的外,它们还可以绑定到设备、工具或工作站。对于您的 Pingstream 示例,web 站点的每个访问者都将作为同一个用户登录 XMPP 服务器,但他们拥有不同的资源。
通信类别使用 XMPP 的实时消息传递系统包含三大通信类别:
这些类别是互补的。例如,如果用户或实体离线(尽管在许多用例中,理想的状态是服务器在用户返回之前一直持有用户的消息),则没有将数据发送给用户或发起一个实体的信息/查询请求的点。这些消息中的每一条都将通过一个完整的 XML 节 传递 — XML 节是以 XML 表达的独立信息项。
这三种类型的 XMPP 节都拥有以下公共属性:
基于 XMPP 的数据传输发生在一些 XML 流上,默认在端口 5222 上操作。这些 XML 流实际上是两个完整的 XML 文档,每个文档对应一个通信方向。一旦会话建立,stream 元素将打开。这个元素将封装整个通信文档。然后,一些节被注入这个文档的第二层。最后,一旦通信结束,stream 元素将关闭,形成一个完整的文档。
例如, 展示了一个 stream 元素,它建立了从客户端到服务器的通信。
清单 1. 建立从客户端到服务器的通信的 stream 标记<stream:stream from="[server]" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
消息一旦通信建立,客户端就能使用 message 元素将消息发送到另一个用户,message 元素包含以下任意子元素:
但是,消息也可以非常简单,如 所示:
清单 2. 样例消息<message from="sendinguser@somedomain" to="recipient@somedomain" xml:lang='en'> <body> Body of message </body> </message>
对于提供实时 web 界面而言,消息节是最有用的节。“发布-订阅” 模型 — 在实时 web 应用程序中使用消息来传输数据的一种替代方法 — 将稍后介绍。
信息/查询信息/查询节拥有广泛的功能。一个例子就是 “发布-订阅” 模型,在该模型中,发布者通知服务器某个特定资源进行了更新,服务器则通知已选择订阅这些通知并拥有适当授权的所有 XMPP 用户。
来自发布者的一系列项目被编码为一些节,格式为基于 XML 的 Atom 发布格式。每个项目都包含在一个 item 元素内,然后合并到一个 pubsub 元素中,最后成为一个信息/查询节。在 (选自 XMPP 发布-订阅规范)中,Shakespeare's Hamlet(JID 为 hamlet@denmark.lit/blogbot)用他著名的独白发布一个更新到 pubsub.shakespeare.lit pubsub 更新节点:
清单 3. 对 pubsub.shakespeare.lit pubsub 更新节点的更新<iq type="set" from="hamlet@denmark.lit/blogbot" to="pubsub.shakespeare.lit"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="princely_musings"> <item> <entry xmlns="http://www.w3.org/2005/Atom"> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link type="text/html" href="http://denmark.lit/2003/12/13/atom03"/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq>
信息/查询节也用于请求一个特定 XMPP 实体的有关信息。例如,在 中的节中,boreduser@somewhere 正在查找 friendlyuser@somewhereelse 拥有的公共项目。
清单 4. 用户查找由 friendlyuser@somewhereelse 拥有的公共项目<iq type="get" from="boreduser@somewhere" to="friendlyuser@somewhereelse"> <query xmlns="http://jabber.org/protocol/disco#items"/> </iq>
反过来,friendlyuser@somewhereelse 使用一列可被订阅到使用 “发布-订阅” 的项目进行响应,如 所示:
清单 5. 使用一列项目响应<iq type="result" from="friendlyuser@somewhereelse" to="boreduser@somewhere"> <query xmlns="http://jabber.org/protocol/disco#items"> <item jid="stuff.to.do"/> <item jid="stuff.to.not.do"/> </query> </iq>
在 中的信息/查询节中的每个返回项目都拥有一个可以订阅到的 JID。信息/查询还允许超出本教程范围的广泛的服务器信息请求。它们中的许多在针对多服务器环境的 web 应用程序上下文中有用,或者作为复杂的分散型协作框架的基础。
联机状态联机状态信息包含在一个联机状态(presence)节中。如果 type 属性省略,那么 XMPP 客户端应用程序假定用户在线且可用。否则,type 可设置为 unavailable,或者特定于 pubsub 的值:subscribe、subscribed、unsubscribe 和 unsubscribed。它也可以是针对另一个用户的联机状态信息的一个错误或探针。
一个联机状态节可以包含以下子元素:
例如, 中的 boreduser@somewhere 可以用这个节来表明聊天意愿:
清单 6. 样例联机状态通知<presence xml:lang="en"> <show>chat</show> <status>Bored out of my mind</status> <priority>1</priority> </presence>
注意 from 属性此处省略。
另一个用户 friendlyuser@somewhereelse 可以通过发送 中的节来探测 boreduser@somewhere 的状态:
清单 7. 探测用户状态