XMPP学习及使用1
XMPP 简单介绍
本小节将简要介绍 XMPP,它的起源。以及为何它是一个适合实时 web 通信的协议。您将检查 XMPP 通信设置的组件,并查看展示这些组件怎样使用的演示样例。
XMPP 是一组基于 XML 的技术。用于实时应用程序。最初。XMPP 作为一个框架开发。目标是支持企业环境内的即时消息传递和联机状态应用程序。当时的即时消息传递网络是私有的,很不适合企业使用。比如。AOL Instant Messenger 不能针对公司内的安全通信进行调整。虽然存在一些商业解决方式,但它们固定的特性集通常不能进行调整,以满足组织的特殊需求。XMPP,当时名为 Jabber,同意组织构建自己的定制工具来促进实时通信。并同意安装现成的第三方解决方式。
XMPP 是一个分散型通信网络,这意味着,仅仅要网络基础设施同意,不论什么 XMPP 用户都能够向其它不论什么 XMPP 用户传递消息。
多个 XMPP server也能够通过一个专门的 “server-server” 协议相互通信。提供了创建分散型社交网络和协作框架的有趣可能性。但这个主题已超出了本教程的讨论范围。
顾名思义,XMPP 可用于满足广泛的、对时间敏感的特性要求。实际上。Google Wave。一个大型多用户协作环境。将 XMPP 作为其联合协议的基础。虽然 XMPP 的出现是为了满足 “个人-个人” 即时消息传递的要求。但它全然不必局限于此任务。
要促进消息传递,每一个 XMPP client用户必须拥有一个全局惟一标识符。基于历史原因。这些标识符称为 Jabber IDs。或称为 JIDs。
鉴于这个协议的分布式特征。重要的是 JID 应包括联系用户所需的全部信息:不存在将用户链接到他们连接到的server的中央知识库。
JID 的结构类似于电子邮件地址(但不要求 JID 同一时候也是有效的电子邮件收件人)。
client和server节点。我将它们统称为 XMPP 实体,都拥有 JIDs。SomeCorp 公司的员工 John Doe 可能拥有 JIDJohn.Doe@somecorp.com
。
这里,somecorp.com
是 SomeCorp 公司的 XMPP server的地址。John.Doe
是 John Doe 的username。
JIDs 还拥有连接到它们的资源。
这同意在一个 XMPP 实体标识符之外进一步处理细粒度。比如,虽然上面的演示样例整体上可以表示 John Doe。但 John.Doe@somecorp.com/Work
可以用于将数据发送到与他的工作相关的工具。
这些资源能够採用随意用户定义的名称。一个 XMPP 实体能够拥有随意数量的资源。除了能够是上下文依赖的外,它们还能够绑定到设备、工具或工作站。对于您的 Pingstream 演示样例,web 网站的每一个訪问者都将作为同一个用户登录 XMPP server。但他们拥有不同的资源。
使用 XMPP 的实时消息传递系统包括三大通信类别:
- 消息传递。当中数据在有关各方之间传输;
- 联机状态,它同意用户广播其在线状态和可用性;
- 信息/查询请求。它同意 XMPP 实体发起请求并从还有一个实体接收响应。
这些类别是互补的。比如。假设用户或实体离线(虽然在很多用例中,理想的状态是server在用户返回之前一直持实用户的消息),则没有将数据发送给用户或发起一个实体的信息/查询请求的点。
这些消息中的每一条都将通过一个完整的 XML节 传递 — XML 节是以 XML 表达的独立信息项。
这三种类型的 XMPP 节都拥有下面公共属性:
from
:源 XMPP 实体的 JID;to
:目标接收者的 JID;id
:这次对话的可选标识符;type
:节的可选子类型。xml:lang
:假设内容是人们可读的,则为消息语言的描写叙述。
基于 XMPP 的传输数据发生在一些 XML 流上。默认在port 5222 上操作。这些 XML 流实际上是两个完整的 XML 文档。每一个文档相应一个通信方向。
一旦会话建立。stream
元素将打开。
这个元素将封装整个通信文档。
然后。一些节被注入这个文档的第二层。
最后,一旦通信结束,stream
元素将关闭,形成一个完整的文档。
比如。清单 1 展示了一个 stream
元素,它建立了从client到server的通信。
清单 1. 建立从client到server的通信的 stream 标记
<stream:stream from="[server]" id="[unique ID over conversation]" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0"> |
一旦通信建立,client就能使用 message
元素将消息发送到还有一个用户,message 元素包括下面随意子元素:
subject
:一个可读的字符串。表示消息主题。body
:一个可读的字符串,表示消息体。假设每一个消息体标记都拥有一个不同的
xml:lang
值,那么能够包括多个消息体标记。(
xml:lang
是惟一可能的属性。)thread
:一个惟一标识符。表示一个消息线程。client软件能够使用这个子元素将相关消息串联在一起。
可是,消息也能够很easy,如 清单 2 所看到的:
<message from="sendinguser@somedomain" to="recipient@somedomain" xml:lang='en'> <body> Body of message </body> </message> |
对于提供实时 web 界面而言,消息节是最实用的节。
“公布-订阅” 模型 — 在实时 web 应用程序中使用消息来数据传输的一种替代方法 — 将稍后介绍。
信息/查询节拥有广泛的功能。一个样例就是 “公布-订阅” 模型,在该模型中,公布者通知server某个特定资源进行了更新。server则通知已选择订阅这些通知并拥有适当授权的全部 XMPP 用户。
来自公布者的一系列项目被编码为一些节。格式为基于 XML 的 Atom 公布格式。
每一个项目都包括在一个 item
元素内,然后合并到一个 pubsub
元素中。最后成为一个信息/查询节。在 清单
3(选自 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" id="pub1"> <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 rel="alternate" 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 实体的有关信息。比如,在 清单 4 中的节中,boreduser@somewhere
正在查找friendlyuser@somewhereelse
拥有的公共项目。
清单 4. 用户查找由 friendlyuser@somewhereelse
拥有的公共项目
<iq type="get" from="boreduser@somewhere" to="friendlyuser@somewhereelse" id="publicStuff"> <query xmlns="http://jabber.org/protocol/disco#items"/> </iq> |
反过来,friendlyuser@somewhereelse
使用一列可被订阅到使用 “公布-订阅” 的项目进行响应,如 清单 5 所看到的:
<iq type="result" from="friendlyuser@somewhereelse" to="boreduser@somewhere" id="publicStuff"> <query xmlns="http://jabber.org/protocol/disco#items"> <item jid="stuff.to.do" name="Things to do"/> <item jid="stuff.to.not.do" name="Things to avoid doing"/> </query> </iq> |
在 清单 5 中的信息/查询节中的每一个返回项目都拥有一个能够订阅到的 JID。信息/查询还同意超出本教程范围的广泛的server信息请求。
它们中的很多在针对多server环境的 web 应用程序上下文中实用,或者作为复杂的分散型协作框架的基础。
联机状态信息包括在一个联机状态(presence)节中。假设 type
属性省略。那么 XMPP client应用程序假定用户在线且可用。
否则,type
可设置为 unavailable
,或者特定于 pubsub 的值:subscribe
、subscribed
、unsubscribe
和unsubscribed
。它也能够是针对还有一个用户的联机状态信息的一个错误或探针。
一个联机状态节能够包括下面子元素:
show
:一个机器可读的值。表示要显示的在线状态的整体类别。这能够是away
(临时离开)、chat
(可用且有兴趣交流)、dnd
(请勿打搅)、或xa
(长时间离开)。status
:一个可读的 show 值。该值为用户可定义的字符串。
priority
:一个位于 -128 到 127 之间的值。定义消息路由到用户的优先顺序。假设值为负数,用户的消息将被扣留。
比如。清单 6 中的 boreduser@somewhere
能够用这个节来表明聊天意愿:
<presence xml:lang="en"> <show>chat</show> <status>Bored out of my mind</status> <priority>1</priority> </presence> |
注意 from
属性此处省略。
还有一个用户 friendlyuser@somewhereelse
能够通过发送 清单 7 中的节来探測 boreduser@somewhere
的状态:
<presence type="probe" from="friendlyuser@somewhereelse" to="boreduser@somewhere"/> Boreduser@somewhere's server would then respond with a tailored presence response: <presence xml:lang="en" from="boreduser@somewhere" to="friendlyuser@somewhereelse"> <show>chat</show> <status>Bored out of my mind</status> <priority>1</priority> </presence> |
这些联机状态值源自 “个人-个人” 消息传递软件。show
元素的值 — 通经常使用于确定将向其它用户显示的状态图标 — 在聊天应用程序之外怎样使用如今还不清楚。
状态值可能会在微博工具中找到用武之地。比如,Google Talk(一个 XMPP 聊天服务)中的用户状态字段的更改能够被导入为 Google Buzz 中的微博条目。
还有一种可能性就是将状态值用作每用户应用程序状态数据的携带者。
虽然此规范将状态定义为可读,但没有什么能够阻止您在那里存储随意字符串来满足您的要求。
对于某些应用程序而言。它能够不是可读的,或者。它能够携带微格式形态的数据负载。
您能够为一个 XMPP 实体拥有的每一个资源独立设置联机状态信息。以便訪问和接收连接到一个应用程序中的单个用户的全部工具和上下文的数据仅仅需一个用户帐户。每一个资源都能够被分配一个独立的优先级;XMPP server将首先尝试将消息传递给优先级较高的资源。
要通过使用 JavaScript 的 XMPP 进行通信的 web 应用程序必须符合一些特殊要求。出于安全考虑。不同意 JavaScript 从 web 页面的域与不同域上的多个server通信。假设您的 web 应用程序界面被托管在 application.mydomain.com
。全部 XMPP 通信也必须发生在 application.mydomain.com
。
防火墙是还有一个问题所在。理想情况下,假设您将 XMPP 用作您的 web 界面的实时元素的基础,那么您希望它对防火墙后面的用户有效。可是。公司防火墙通常仅仅对少数几个协议开放几个port,以便同意 web 数据、电子邮件和类似的通信通过。默认情况下,XMPP 使用port 5222,这非常可能是公司防火墙阻止的port。
如果您知道您的用户前面的防火墙在port 80 上同意 HTTP(这是用于訪问 web 的默认协议和port)。理想情况是您的 XMPP 通信可以越过该port上的 HTTP。可是,HTTP 的设计并不针对持续连接。
web 的架构不同于实时数据所需的通信架构。
以下我们看看 Bidirectional-streams Over Synchronous HTTP (BOSH) 的标准,该标准为双向同步数据提供一个模拟层。
借助这个标准,能够与一个 XMPP server建立一个较长的 HTTP 连接(时长一分钟或两分钟)。
假设新数据在那个期间到达,则 HTTP 请求返回数据并关闭。否则,该请求仅仅是失效。无论是哪种情况,一旦一个请求关闭,还有一个请求将又一次建立。虽然结果是对一个 web server的一系列反复连接,但它是一个比 Ajax 轮询更有效的数量级。特别是由于连接到的是一个专业server而不是直接连接到 web 应用程序。
BOSH 上的 XMPP 同意 web 应用程序通过一个原生连接持续与 XMPP server通信。client通过port 80 上的 HTTP 上的一个标准 URL 连接。然后,web server将这个连接代理到由 XMPP server操作的一个不同port — 一般是 7070 — 上的 HTTP URL。这样,不管何时数据被发送到 XMPP server,web 应用程序仅仅需使用一些资源,而 web client能够使用通常支持的 web 标准从防火墙后操作。维持 BOSH 的较长 HTTP 轮询的开销主要由 XMPP server而不是 web server或 web 应用程序承担。
web server和 XMPP server都不会受到与使用 JavaScript 进行通信一样的域限制。正是由于这一点。消息才可以被发送到其它 XMPP server和client。
如今,您理解了 XMPP 怎样适合实时 web,能够下载并设置它。以便開始创建这个 Pingstream 应用程序。