联机状态信息包含在一个联机状态(presence)节中。如果 type
属性省略,那么 XMPP 客户端应用程序假定用户在线且可用。否则,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 服务器将首先尝试将消息传递给优先级较高的资源。
要通过使用 JavaScript 的 XMPP 进行通信的 web 应用程序必须符合一些特殊要求。出于安全考虑,不允许 JavaScript 从 web 页面的域与不同域上的多个服务器通信。如果您的 web 应用程序界面被托管在 application.mydomain.com
,所有 XMPP 通信也必须发生在application.mydomain.com
。
防火墙是另一个问题所在。理想情况下,如果您将 XMPP 用作您的 web 界面的实时元素的基础,那么您希望它对防火墙后面的用户有效。但是,公司防火墙通常只对少数几个协议开放几个端口,以便允许 web 数据、电子邮件和类似的通信通过。默认情况下,XMPP 使用端口 5222,这很可能是公司防火墙阻止的端口。
假设您知道您的用户前面的防火墙在端口 80 上允许 HTTP(这是用于访问 web 的默认协议和端口)。理想情况是您的 XMPP 通信能够越过该端口上的 HTTP。但是,HTTP 的设计并不针对持续连接。web 的架构不同于实时数据所需的通信架构。
下面我们看看 Bidirectional-streams Over Synchronous HTTP (BOSH) 的标准,该标准为双向同步数据提供一个模拟层。借助这个标准,可以与一个 XMPP 服务器建立一个较长的 HTTP 连接(时长一分钟或两分钟)。如果新数据在那个期间到达,则 HTTP 请求返回数据并关闭;否则,该请求只是失效。不管是哪种情况,一旦一个请求关闭,另一个请求将重新建立。尽管结果是对一个 web 服务器的一系列重复连接,但它是一个比 Ajax 轮询更有效的数量级,特别是因为连接到的是一个专业服务器而不是直接连接到 web 应用程序。
BOSH 上的 XMPP 允许 web 应用程序通过一个原生连接持续与 XMPP 服务器通信。客户端通过端口 80 上的 HTTP 上的一个标准 URL 连接。然后,web 服务器将这个连接代理到由 XMPP 服务器操作的一个不同端口 — 通常是 7070 — 上的 HTTP URL。这样,无论何时数据被发送到 XMPP 服务器,web 应用程序只需使用一些资源,而 web 客户端可以使用通常支持的 web 标准从防火墙后操作。维持 BOSH 的较长 HTTP 轮询的开销主要由 XMPP 服务器而不是 web 服务器或 web 应用程序承担。web 服务器和 XMPP 服务器都不会受到与使用 JavaScript 进行通信一样的域限制,正是因为这一点,消息才能够被发送到其他 XMPP 服务器和客户端。
现在,您理解了 XMPP 如何适合实时 web,可以下载并设置它,以便开始创建这个 Pingstream 应用程序。
在本小节中,您将安装 Openfire XMPP 服务器并配置它来支持您的实时 web 应用程序。
有两个领先的开源 XMPP 服务器可以免费下载。它们都应用广泛并通过 GNU Public License version 2 许可,每个服务器都有自己的优势和缺点:
Openfire 在 http://localhost:7070/http-bind 维护了一个 HTTP 绑定 URL,以便通过 BOSH 访问。要在端口 80 上使用这个 URL,您必须配置 Apache HTTP Server 以将一个 URL 转发到这个位置。为此,您需要启动代理模块。
打开您的 http.conf Apache 配置文件并找到 mod_proxy.so
和 mod_proxy_http.so
的 LoadModule
条目,它们默认被注释掉。移除前导的井字符(#
),取消注释。这个配置文件的 Dynamic Shared Object (DSO) Support 部分中的多个适当的行(不一定在一起)现在应该类似于 清单 8:
LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_module modules/mod_proxy.so
在配置文件的末尾,添加 清单 9 中的行(如果您没有将 locahost 作为您的测试服务器环境,则应将 127.0.0.1 替换为您的服务器 IP 地址):
# XMPP proxy rule ProxyRequests Off ProxyPass /xmpp-httpbind http://127.0.0.1:7070/http-bind/ ProxyPassReverse /xmpp-httpbind http://127.0.0.1:7070/http-bind/
注意,在 清单 9 中,您在端口 80 上使用了一个稍微不同的 URL:/xmpp-httpbind
。这个 URL 是 strophe.js(您稍后将用到的客户端 JavaScript 框架)分配给一个用于设置 BOSH 端点的变量的值。
重启服务器。现在,您可以开始编写使用 XMPP 的 web 应用程序了。
在本小节中,您将编写一些 JavaScript 函数,以便通过 BOSH 上的 XMPP 接收消息,并构建一个 HTML 用户界面来显示接收到的通知。
现在您需要创建用户界面来接收通知。Strophe.js 是用于通过 BOSH 发送和接收 XMPP 数据的常用 JavaScript 库。对于 Pingstream 中的目的,您只需接收数据,尽管有一点是显而易见的:双向通信允许您快速构建丰富的协作环境。
尽管有几个版本,但 Strophe 的 JavaScript 版本作为一个基于浏览器的 XMPP 客户端对您而言是最有用的。下载压缩包(参见 参考资料)并将其解压缩到 pingstream 的 strophejs 文件夹中。
jQuery JavaScript 框架极大地简化了事件处理和 DOM 操作。本文提供的 Strophe.js 示例广泛使用该框架,这两者简直是 “天生一对”。下载 jQuery(参见 参考资料 中的链接)并将这个缩微版放到 pingstream 中的 jquery 文件夹中。
新建一个 index.html 文件。在该文件中包含对刚才下载的 Strophe 和 jQuery 库的引用,以及对稍后即将定义的 pingstream.js 库的引用。在body
元素中,添加一个 ID 为 notifications 的 div
元素,如 清单 23 所示:
<!DOCTYPE html> <html> <head> <title>Latest content</title> <script type="text/javascript" src="jquery/jquery-1.4.2.min.js"></script> <script type="text/javascript" src="strophejs/strophe.js"></script> <script type="text/javascript" src="pingstream.js"></script> </head> <body> <h1>Latest content:</h1> <div id="notifications"></div> </body> </html>
创建 JavaScript 文件 — pingstream.js — 您刚才在 清单 23 中引用的。在 pingstream.js 的顶端,定义此前在 Apache 中配置的 BOSH 代理端点,如 清单 24 所示:
var BOSH_SERVICE = '/xmpp-httpbind'; var connection = null;
当页面完全加载后,您想自动连接到 XMPP 服务器。您可以使用 jQuery 的 $(document).ready
调用实现这个目标;其中,您新建一个strophe.js Strophe.Connection
对象并用它连接到服务器,如 清单 25 所示:
$(document).ready(function () { connection = new Strophe.Connection(BOSH_SERVICE); connection.connect( "sendinguser@127.0.0.1", "sendingpass", onConnect); });
在 清单 25 中,Strophe.Connection.connect
方法包含一个对 onConnect
函数的引用,作为它的一个参数。onConnect
将在连接建立后立即启动。您可以利用这个机会来为入向消息添加一个通知处理程序;您在这里注册了一个名为 notifyUser
函数。随后,您发送了一个简单的联机状态节。
要确保您可以连接并接收新消息,您还需向用户发送一个友好通知。
将 清单 26 中的代码添加到您的 JavaScript 文件中的 $(document)ready
调用上方:
function onConnect(status) { $('#notifications').html('<p class="welcome">Hello! Any new posts will appear below.</p>'); connection.addHandler(notifyUser, null, 'message', null, null, null); connection.send($pres().tree()); }
最后,由于您注册了通知处理程序,因此,只要 XMPP 客户端接收到消息节,Strophe.js 就会调用 notifyUser(msg)
函数。msg
参数是 XML 节本身的一个表示,可以如 清单 27 所示查询:
msg
参数var elems = msg.getElementsByTagName('body'); var body = elems[0]; $('#notifications').append(Strophe.getText(body));
理想情况下,您希望对消息进行限制,以便只显示您的服务器端发送用户发送的消息。您可以将它封装到构成 notifyUser
函数主体的一个 if
语句中,如 清单 28 所示:
notifyUser
函数function notifyUser(msg) { if (msg.getAttribute('from') == "testuser@127.0.0.1/pingstream") { var elems = msg.getElementsByTagName('body'); var body = elems[0]; $('#notifications').append(Strophe.getText(body)); } return true; }
这个函数应位于在 清单 26 中定义的 onConnect
函数上方。
在一个 web 浏览器中打开您的 index.html 文件。您应该会看到一个简单的标题和一条消息,该消息称更新将在下面显示(这可能会使您回想起您发给自己的测试通知,称 XMPP 连接正在成功运行)。
现在加载 backend.php。就像变戏法一样,来自 IBM developerWorks Web development 专区的最新更新将显示在您的页面上。其他带有 RSS 提要的示例源包括 Twitter 帐户、通讯社、以及来自服务器监控软甲的更新提要。
这是开发一个强大平台的简单起点。Strophe.js 能够促进应用程序的双向通信,尽管更简单的方法是使用标准的 jQuery HTTP 回拨来将用户输入送入系统,从而避免为您的应用程序编写一个 XMPP 后台监控进程的麻烦。更令人兴奋的是,当您 web 服务器用作 BOSH 代理时,完全无需太多来自服务器端 web 应用程序的输入,两个或更多 web 客户端就能通过 XMPP 相互通信。这种技术将对从办公室协作软件到游戏的很多软件产生深远影响。
Code
var BOSH_SERVICE = '/xmpp-httpbind'
var connection = null;
function notifyUser(msg)
{
if (msg.getAttribute('from') == "testuser@127.0.0.1/pingstream") {
var elems = msg.getElementsByTagName('body');
var body = elems[0];
$('#notifications').append(Strophe.getText(body));
}
return true;
}
function onConnect(status)
{
if (status == Strophe.Status.CONNECTED) {
$('#notifications').html('<p class="welcome">Hello! Any new posts will appear below.</p>');
connection.addHandler(notifyUser, null, 'message', null, null, null);
connection.send($pres().tree());
}
}
$(document).ready(function () {
connection = new Strophe.Connection(BOSH_SERVICE);
connection.connect( "receivinguser@127.0.0.1",
"receivinguserpass",
onConnect);
});