学习从头开始构建 P2P 应用
Sing Li (westmakaha@yahoo.com)
作家,Wrox 出版社
Jxta 工程按社区方式运作,旨在为对等应用构建实用应用程序底层。Jxta 最初的参考实现包含一个命令行 shell,使您无须编程就能试验核心 Jxta 平台。在本系列(共 3 部分)的第 2 部分,Sing Li 将带我们去实际体验一下 Jxta shell。您将?索它的命令集并用 Java 编程语言编写您自己的定制命令来扩展它的功能。请在讨论论坛与作者和其他读者共享您关于本文的心得。
Jxta 命令 shell 正是多数 Jxta 开发者将碰到的第一个 Jxta 应用。事实上,Jxta 开发包(Jxta development kit)把 shell 作为缺省应用包含在其中。这样做有一个很好的理由:一行代码都不用写,通过 shell 您就可以体会组成 Jxta 平台的组件。与大家熟悉的 UNIX 或 DOS shell 一样,shell 也是一个大量使用环境变量的命令行工具。在本系列的这一部分中,您将熟悉这个万能 shell 的基本命令,并学习如何编写您自己的命令来增强它的功能。
在我们开始之前,您必须先安装 Jxta shell。您可以从本文末尾的参考资料部分中下载 Jxta shell 分发包(请选择一个稳定版本)。
安装 Jxta shell
在您的机器上安装 Jxta,至少到 29i 版(正在编写的当前版本)为止,在多数家用和办公系统上是很不轻易的。主要的困难是源于 Jxta 与防火墙代理和事实上标准的家用网络 NAT 路由器之间的工作方式。
要在因特网之外与其它对等节点进行交互,您必须根据 Jxta 安装文档进行具体配置。然而,就本文来说,我们无须连接到我们的本地网之外,就可以试验 Jxta。只要安装该分发包并执行本文的指导就行了。在本系列的下一篇即最后一篇论文中,我们将讨论如何为因特网配置我们的 Jxta 系统。
安装 Jxta
请按照下列步骤安装进行本文试验所需的环境:
下载本文的分发源代码并将它解压到您选定的目录。在本文所有地方,我们都将称这个目录为 CODEROOT。
解压缩 Jxta shell 的二进制分发文件(jxtashell.zip)到同一个 CODEROOT 目录。
检查 CODEROOT 下的 lib 目录,确保其中已经包含 jxta.jar 和 jxtashell.jar。
进入 CODEROOT 下的 shell 目录并运行 runshell.bat 文件(包含在本文的分发代码中)。这将启动图 1 所示的 GUI 配置实用程序。
请在 GUI 配置实用程序的 peer name(对等机名)处输入 node1。确保选上 TCP 传输部分而且您所输入的主机名或 IP 地址须是机器的有效本地地址。同时确保您已经把本节点的 TCP 端口号指定为 9700。所有这些都显示在如下的图 1 中。
禁用 HTTP 传输,为此请不复选相应的复选框。HTTP 传输用于与防火墙之外的对等机进行通信(通过集中服务(rendezvous service));我们不需要它。
单击 OK 按钮启动 Jxta shell 的一个实例。
图 1. Jxta GUI 配置实用程序
剖析对等机
是进行一些试验的时候了。跟 UNIX shell 中的情形一样,Jxta shell 的命令也与环境变量交互。要找出对等机的更多信息,试一下以下命令:
JXTA>whoami
您的输出将是一个语法上类似于 XML 的结构化文档。
<Peer>node1</Peer>
<KeyWords>Netpeer group by default</Keywords>
<PeerId>
jxta://59616261646162614A78746150325033DB1EB6636DCE4B2990CA888B36CD96C7000
0000000000000000000000000000000000000000000000000000000000301
</PeerId>
<TransportAddress>tcp://169.254.101.152:9700/</TransportAddress>
这里,我们可以看到本地对等机名 node1 与一个很长的对等机 ID(包在 <PeerId> 元素中)相对应。请注重我们已经在一个名称为 Netpeer 的缺省对等组中。通达本对等机的传输地址包在 <TransportAddress> 元素中。假如我们没有特地修改过 shell 配置,那么 shell 引导机制将把我们放到缺省对等组中。事实上,我们可以通过以下命令更多地了解这个缺省组:
JXTA>whoami -g
这个 whoami 命令的变体,使用 -g 开关,将检查我们当前是其中成员的组并将获得更多该组的信息。我们的输出看起来可能像这样:
<peer group>Netpeer group</peer group>
<Keywords>Netpeer group by default</Keywords>
<peer groupId>
jxta://59616261646162614A757874614D504700000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000201
</peer groupId>
<Service>jxta.service.discovery</Service>
<Service>jxta.service.pipe</Service>
<Service>jxta.service.resolver</Service>
<Service>jxta.service.rendezvous</Service>
<Service>jxta.service.peerinfo</Service>
<Service>jxta.service.membership</Service>
这就是缺省 Netpeer 组。请注重表中列出的所有可用核心对等组服务;每个服务都被包在 <Service> 标记中。
P2P 基础通信
在对等机开机、运行并且是 Netpeer 组的成员的情况下,我们现在可以尝试进行一些 P2P Jxta 通信。我们将:
创建一个管道
创建一条消息
通过管道把消息发送到另一个对等机
为使用 Jxta shell 上可用的命令,我们须将上述任务分解为如下步骤:
创建一条管道广告。
创建一条基于该广告的管道。
启动一个新的 shell 实例。
在新 shell 上,创建一条基于该广告的输出管道。
在新 shell 上,创建一条消息。
在新 shell 上,附加一个文件到该消息上。
阻塞老 shell,等待来自输入管道的消息。
在新 shell 上,发送该消息。
现在,让我们一步一步地完成这个过程,并分析我们将用到的所有 shell 命令。我们将在这个过程中熟悉几乎所有当前可用的 shell 命令。
创建一条管道广告
我们可以用如下的 shell 命令创建一条管道广告并将它指定给名为 dwAdv 的环境变量:
JXTA>dwAdv = mkadv -p
mkadv 命令用于为对等组或管道创建广告。(在这个案例中,-p 开关表示这是一条管道广告。)在 Jxta 的当前发展阶段上,-t 开关尚不能用,我们将用它来指定管道类型。最终您将可以用这个命令创建不同类型的管道广告(例如,广播管道广告,对等管道广告,等等;请参阅本系列的第一部分获取更多具体信息)。
我们可以用如下命令看到 mkadv 的效果:
JXTA>env
env 列出所有环境变量。您将看到环境变量 dwAdv 现在已经被设置:
dwAdv = PipeService Advertisement (class net.jxta.impl.protocol.PipeAdv)
创建一条基于该广告的输入管道
现在我们将创建一条与名为 dwinpipe 的环境变量联系在一起的输入管道:
JXTA>dwinpipe = mkpipe -i dwAdv
-i 开关表示输入管道,dwAdv 则是我们前面创建的管道广告。
用 env 命令将可以看到 dwinpipe 现在已经与该输入管道联系在一起:
dwinpipe = InputPipe of dwAdv (class net.jxta.impl.pipe.InputPipeImpl)
启动一个新 shell
您可以用以下命令生成一个新的、带有当前环境变量的 shell 实例:
JXTA>Shell -s
-s 开关在一个新窗口中创建一个新的、继续老 shell 的所有环境变量的 shell。假如我们未使用 -s 开关,那么新 shell 的创建仍将继续所有环境变量,但它将被“嵌套”或“堆”在同一个窗口中现有 shell 上。没有新窗口会被显示,并且您将不能访问老 shell,直到您退出新 shell 为止。
在我们的示例中,请记住在两个 shell 实例中我们将处理同一个对等机。在本文的后一部分中,我们将与多个对等机一起工作。
创建一条输出管道
在新近创建的 shell 实例中,我们可以创建一个与 dwAdv 广告联系在一起的输出管道。用到的命令是:
JXTA>dwoutpipe = mkpipe -o dwAdv
这条新的、可通过环境变量 dwoutpipe 访问的管道将与我们前面创建的那条输入管道联系在一起,因为它们俩都基于同一条广告(dwAdv)。假如要用这条管道来连接两台不同的对等机,我们就须为这条管道做广告,这是我们将在以后讨论的主题。
创建一条消息
我们现在要创建一条能通过该管道发送的消息。为此,我们首先创建一条与环境变量 dwMsg 联系在一起的空消息。
JXTA>dwMsg = mkmsg
附加一个文件到该消息
导入名为 simple.txt 的文本文件(在 shell 目录,它包含在参考资料中提供的源代码中),并用如下命令把它与名为 dwData 的环境变量联系起来:
JXTA>importfile -f simple.txt dwData
-f 开关是必须的。它表示紧接在后面的是一个文件名;这个用法跟 UNIX 的 tar 命令相似。省略它将导致未定义的行为,包括异常!
现在,将导入的文件作为一个标记值(与名为 dwTag 的标记联系在一起)附加到消息上。从概念上说,我们是在把属性附加到消息对象中。属性将与消息一起通过管道传送并可在另一端将它拆离。在下层,一个额外的 XML 片段被插入到结构化文档(消息)中,它包含附加文档,并以标记值命名;相关的报头也被修改以反映新的、更大的大小。用 put 命令就可以完成这一切。稍后,可以用 get 命令从消息中抽取相同的标记值,get 命令通过逆向进行上述过程找回所导入的文件。
为把标记值作为一个属性进行附加,我们用 put 命令:
JXTA>put dwMsg dwTag dwData
这个命令将通过 dwTag 把文本文件添加到消息正文中。
预备接收消息
在老的 shell 实例上,用以下命令开启在输入管道上阻塞着的接收:
JXTA>dwNewMsg = recv dwinpipe
这个命令将设置 shell 以等待消息并把环境变量 dwNewMsg 与任何通过管道收到的进入消息联系起来。
通过管道发送该消息
回到新 shell 实例中,现在我们可以用这个命令通过管道发送带有附加文件的消息:
JXTA>send dwoutpipe dwMsg
验证消息和文件收据
在接收器处于阻塞状态的老 shell 实例中,我们将看到:
JXTA>dwNewMsg = recv dwinpipe
recv has received a message
现在,试一下 env 并请注重环境变量 dwNewMsg 现在已经与接收到的消息联系在一起。
dwNewMsg = Message from dwinpipe (class net.jxta.impl.endpoint.MessageImpl)
事实上,我们可以用 dwTag 标记轻易地从管道中抽取文件。用 get 命令使文件与环境变量 dwNewFile 联系在一起:
JXTA>dwNewFile = get dwNewMsg dwTag
为查看传输文件的内容,我们可以使用如下命令:
JXTA>cat dwNewFile
文件内容将作为一个 XML 文档显示:
<?xml version="1.0"?>
<ShellDoc>
<Item>
This is a simple file for Jxta transfer. It can contain anything.
</Item>
</ShellDoc>
要把消息保存到名为 received.txt 的文件,请用这个命令:
JXTA>eXPortfile -f received.txt dwNewFile
这将把文件保存在 shell 最初从中启动的目录。-f 开关仍表示后面所跟的是一个文件名。
在两个独立的对等机之间建立管道
既然我们已经通过管道实际传输了一个文件,那就让我们把 Jxta 系统的一些独有特征重新整理一下:
管道用它的广告唯一标识,广告是嵌入在 XML 文档中的一个标识符。
创建管道广告的时候并没有生成物理管道或连接;管道虽然存在但并未绑定到任何端点。
您可以在创建广告之后的任何时候独立地把输入和输出管道绑定到端点。
端点之间数据的实际传输通常由对等组内的下层管道服务协调。
我们上面的示例只使用一个对等机或端点。我们可以轻易地将它修改成能使用同一个缺省对等组内的两台对等机。为此,您可以按如下所述在同一台机器上安装 Jxta shell 的另一个对等机实例,或者也可以在同一个局域网的两台独立机器上启动 Jxta shell。
GUI 配置实用程序
GUI 配置实用程序只在您第一次启动一个 shell 实例时出现。此后,配置信息被存储在磁盘上并可被重用。这就是为什么我们要创建 shell 和 shell2 目录的原因 ? 包含配置数据的两套设置。假如您想再经历一遍配置过程,您可以在下次启动 shell 实例时,当您在 Jxta shell 中的时候输入 peerconfig 命令来启动 GUI 配置实用程序。
在同一台机器上安装两台独立的对等机
为模拟有两个节点的 P2P 网络,我们将在我们安装第一台对等机的同一台机器上安装第二台独立的对等机。有了这个安装,我们将不必在网络的几台机器之间往返奔波就可以工作。顺利进行这项工作的要害是把平台上的各个实例设置成在不同的 TCP 端口上运行。为此,您可以采取这些步骤:
进入 CODEROOT 目录下的 shell2 目录。
运行 runshell.bat 文件启动 shell。
这样,Jxta GUI 配置实用程序将被启动。把 TCP 端口改成 9701(回想一下,另一个 shell 实例,即对等机 node1 是在端口 9700),并输入一个新的对等机名(本示例用的是 node2)。
禁用 HTTP 传输协议(不复选相应的框)并完成配置。
上述过程将启动一台独立的对等机,这台对等机虽然与 node1 在同一台机器上运行,但 TCP 端口是不同的。现在我们可以创建管道并在两台独立的对等机之间发送消息。从本质上说,我们可以把我们在前面的单个对等机的案例中所做的所有事情再做一遍 ? 但这次,我们将在新的对等机实例而不是第二个 shell 实例上做这些事情。
为方便起见,我们将把创建输入管道实例的对等机用它的对等机名 ? node1 来称呼它 ? 新的对等机实例则称为 node2。在 node1 上创建了输入管道之后,您必须使它的广告对 node2 可见。在独立的对等机之间,环境变量不自动共享;对同一台机器上的独立 shell 实例和连接在一个网络上的不同机器上的对等机而言,情况也是如此。要在对等组内发布广告,请使用 share 命令:
JXTA>share dwAdv
要在 node2 上查看广告,您必须执行远程 search:
JXTA>search -r
-r 开关表示该命令将搜索同一对等组内的远程对等机所发布的广告。记住,“远程”对等机可以是运行在同一台机器上的一个独立的 shell 实例。
接着,再次输入如下命令:
JXTA>search
这个动作将轮询任何新近被发现的广告。最后您将在 node2 上看到一些类似这样的东西:
JXTA>search
JXTA Advertisement adv0
shell 将为每个被发现的广告指定一个名为 advn 的环境变量,其中 n 从 0 开始。在我们的示例中,shell 把变量 adv0 指定给新近被发现的广告。
现在您可以用 adv0 这个共享广告来建立输出管道:
JXTA>dwoutpipe = mkpipe -o adv0
上面讨论了安装两台对等机的必要步骤;剩下的全部步骤跟单个对等机的案例中的步骤一样。用管道在一个对等组的对等机之间轻易地交换文件、数据和代码是可能的。
扩展 shell:一个简单示例
Jxta shell 被设计成用户可以轻易地对它进行扩展的。这意味着您可以往 shell 添加定制命令。因为 Jxta shell 的参考实现是用 Java 语言实现的,所以动态类装入意味着可以动态地添加 Jxta shell 扩展,而不用重新编译 shell。这还意味着不同的用户可以使用同一个基础 shell 二进制文件,但却保留他们自己喜欢的扩展设置。事实上,要编写一个扩展,您甚至不必访问 Jxta shell 的源代码。说明这种令人惊异的可扩展性的最好办法是举一个小的例子。我们打算往 shell 添加一个名为 dwcmd(developerWorks command 的缩写)的命令。执行 dwcmd 时只是打印一个文本字符串:
JXTA>dwcmd
This is a trivial Jxta shell extension.
这里是 dwcmd 扩展的源代码(它很简单):
package net.jxta.impl.shell.bin.dwcmd;
import net.jxta.impl.shell.*;
public class dwcmd extends ShellApp {
public dwcmd() {
}
public int startApp (String[] args) {
println ("This is a trivial Jxta shell extension.");
return ShellApp.appNoError;
}
public void stopApp () {
}
}
我们可以看到:
扩展继续了 net.jxta.impl.shell.ShellApp 类。
它必须实现 StartApp() 和 stopApp() 方法。
它是 net.jxta.impl.shell.bin 包的一部分。
所有 shell 扩展都按照这种模式。当从 shell 命令行调用该命令时,StartApp() 方法被调用,而当退出 shell 时,StopApp() 方法被调用。
编译并配置 shell 扩展
为成功编译dwcmd.java 文件,当要调用编译器时,我们必须把 jxtashell.jar 添加到 classpath 中。这一步使得 Java 编译器对 Jxta 核心类和 shell 扩展代码所使用的特定于 Jxta shell 的库类都能进行处理。code 目录已经提供了 makeit.bat 文件;文件内容如下:
set CODEROOT=..
javac -classpath
%CODEROOT%libjxta.jar;%CODEROOT%libjxtashell.jar
-d %CODEROOT%dwclasses netjxtaimplshellindwcmd*.java
请注重 dwcmd 命令是 net.jxta.impl.shell.bin 包的一部分。事实上,所有 shell 命令都是以这种方式配置的。这是 Jxta shell 找到命令的方式(使用内省)。这也正是在运行期间动态地把扩展添加到 shell 中,而不要求重新编译 shell 本身的机制。
我们的 makeit.bat 编译过程把输出的类文件(classfiles)放在名为 dwclasses 的目录中。我们必须把这个目录添加到我们所运行的 shell 实例的 classpath 中。这在运行时把 shell 扩展类和 Jxta shell 实现类有效地合并在了一起。假如您看一下 shell 目录中的 runshell.bat 文件,那么您将可以看到 classpath 中包含了它:
java -classpath
..libjxta.jar;..libjxtashell.jar;..liblog4j.jar;..dwclasses
net.jxta.impl.peergroup.Boot
这里,我们已经把 ..dwclasses 目录添加到了运行 shell 的 VM 的 classpath 中。这把我们的 net.jxta.impl.shell.bin.dwcmd 包和 jxtashell.jar 中的其它 shell 命令有效地合并在了一起。图 2 说明了这些命令合并的工作机制。
图 2. 把定制命令 dwcmd 添加到 shell
现在假如您在 shell 目录中启动一个新的 shell 实例(用 runshell.bat),那么您将可以在该 shell 内访问新的 dwcmd 命令。亲自试试!
编写一个复杂的 shell 扩展
既然我们已经熟悉创建 shell 扩展的基础知识,那我们就能够编写一个更复杂的、实际能执行有用的 Jxta 工作的扩展。事实上,我们将创建一个能做以下工作的 shell 扩展:
创建一条管道广告
创建一条基于该广告的输入管道
在组内共享该广告
在输入管道上等待消息
从消息中抽取所附加的文本文件
打印出该文本文件的内容
这与我们前面在 node1 上使用 shell 命令和 shell 变量时的顺序是一样的。现在,在扩展的帮助下,我们用一个定制命令就可以完成一切事情。
新的命令将被称为 waitptext(wait pipe text 的缩写)。您可以在子树 net.jxta.impl.shell.bin(所有 shell 命令都必须位于此处)下找到它的源代码。看看下面 waitptext.java 的导入清单以了解我们须导入的各个 Jxta 平台包。
package net.jxta.impl.shell.bin.waitptext;
...
import net.jxta.pipe.*;
import net.jxta.endpoint.*;
import net.jxta.discovery.*;
import net.jxta.document.*;
import net.jxta.protocol.*;
这里是所包含的包及其功能的概略介绍:
包 用途
net.jxta.pipe 包含用于管道实现 InputPipe 和 Pipe 接口
net.jxta.endpoint 包含用于消息实现的 Message 接口
net.jxta.document 包含用于结构化文档的一个类工厂(class factory)和一些接口,例如用在 Jxta 消息和广告中的 XML 文档(适用于 Jxta 的参考实现)
net.jxta.protocol 包含用于管道广告实现的 PipeAdvertisement 接口;还包含一个用于创建新管道广告的类工厂
net.jxta.protocol 包含使用 Jxta 协议的类和接口
在类 waitptext 中,我们定义了将要使用的 Jxta 对象类型(广告、管道、消息和结构化文档等等)的私有(private)实例。在命令行上,一切都被指定为 shell 变量;在 Jxta 中,我们必须更具体地说明类型。
public class waitptext extends ShellApp {
private PipeAdvertisement myPipeAdv = null;
private InputPipe myInpPipe = null;
private Message myMesg = null;
private InputStream myInpStream = null;
private StrUCturedDocument myStructDoc = null;
private Discovery myDiscovery = null;
private PeerGroup myGroup;
waitptext.java 中最重要的方法是 startApp(),命令的大部分工作都在这里完成。在这个案例中,我们把参数传递到命令中。这个参数是结构化文档标记的名称(在我们前面的示例中是 dwTag),它就是我们为拆离随消息一起发送的文本文档所要找的。我们判定用户是否为消息标记名指定了参数;假如没有,我们就使用缺省值(预先设置为 tmpTag):
private String tagName = "tmpTag";
public int startApp (String[] args) {
if (args.length != 0)
tagName = args[0];
创建一条管道广告
我们用 AdvertisementFactory 静态类来创建管道广告,它是 net.jxta.document 包的一部分(因为广告是一个结构化文档)。您可以从本系列的第 1 部分中回想起每条广告都有一个唯一的 ID;这里,我们通过创建一个新的 PipeID 实例来创建 ID。我们把当前组的 ID 作为参数传进去以构造 PipeID,因为每个 PipeID 都包含它从中被创建的对等组的 ID。UUID 生成算法是内部平台核心库的一部分,并且新的 PipeID() 实现将在内部调用它。PipeID 实际上包含一个嵌入的组 ID,因为管道总是属于一个对等组。它实际上是两个 UUID 的组合 ? 这就是为什么在构造 PipeID 期间要把组 ID 传进去的原因。请注重 group 是一个对每一个 shell 应用(包括 shell 的当前对等组)都可用的受保护(protected)变量。
try {
myPipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(
PipeAdvertisement.getAdvertisementType());
myPipeAdv.setPipeID( new PipeID(group.getID()) );
} catch (Exception e) {
e.printStackTrace();
return ShellApp.appMiscError;
}
if (myPipeAdv == null) {
println("waitptext: Cannot create a Pipe Advertisement");
return ShellApp.appMiscError;
}
println("created a pipe advertisement...");
创建一条输入管道
创建了管道广告之后,我们必须创建一条基于该广告的输入管道。通过使用 net.jxta.pipe.Pipe 接口完成这项任务。这里,我们使用从 ShellApp 继续来的受保护 pipes 变量;pipes 总是包含 shell 的缺省组的缺省管道服务。
try {
myInpPipe = pipes.createInputPipe (myPipeAdv);
} catch (IOException e) {
println("waitptext: Cannot create a Pipe");
return ShellApp.appMiscError;
}
println("created an input pipe based on the advertisement...");
发布管道广告
现在我们须使该管道广告对组内的其它对等机可见。我们将使用原始的 Jxta 发现协议来发布该广告。在一个 P2P 应用产品中,当然,我们可以使用其它方式(也就是说,其它服务)传播广告。
try {
myDiscovery = group.getDiscovery();
myDiscovery.publish(myPipeAdv, Discovery.ADV);
} catch( Exception e ) {
println("waitptext: publish of pipe advertisement failed");
return ShellApp.appMiscError;
}
println("published the pipe advertisement to the group...");
等待输入消息
使该广告可用之后,我们在该输入管道上阻塞,以等待传进来的消息。通过使用 Pipe 实例的 poll() 方法完成这项任务。timeout(超时)值为 0 表示我们将无限期地等待传入的消息。
println("waiting at the input pipe for a message...");
try {
myMesg = myInpPipe.poll(0);
} catch (IOException e) {
return ShellApp.appNoError;
}
抽取并打印附加文件
假如代码执行到了这一步,我们将已经接收到一条消息。然后我们根据用户指定的数据标记创建一个结构化文档并把这个结构化文档的文本内容打印到命令窗口。
try {
myInpStream = myMesg.pop (tagName);
println("tagName used is " + tagName);
myStructDoc = StructuredDocumentFactory.newStructuredDocument (
new MimeMediaType ("text/xml"),
myInpStream);
OutputStream out = new ByteArrayOutputStream();
myStructDoc.sendToStream( out );
println("received a message...");
print ( out.toString() );
} catch (Exception e) {
println("waitptext: failed in messge receive");
return ShellApp.appMiscError;
}
return ShellApp.appNoError;
}
这就是 waitptext 的全部!现在您可以用 makeit.bat 编译这个新命令。
测试 waitptext 命令
启动 node1(在 shell 目录)和 node2(在 shell2 目录)上的 shell 实例。在两个实例中都输入如下命令:
jxta> search -f
这将刷新所有现有的活动广告。因为 Jxta 节点应该免于重启,广告在缺省状态下是持久的。使用 search -f 将确保我们不会有从前面的试验中遗留下来的广告。
现在我们可以在 node1 上输入我们的新命令。您应该看到以下输出:
JXTA>waitptext dwTag
created a pipe advertisement...
created an input pipe based on the advertisement...
published the pipe advertisement to the group...
waiting at the input pipe for a message...
此时,shell 实例正在等待传入的消息,
在 node2 实例上,重复我们在前面的 shell 试验中采用的步骤以:
查找发布的管道广告(search -r)
生成一条基于该广告的输出管道(dwoutpipe = mkpipe -o adv0)
创建一条消息(dwMsg = mkmsg)
把文本文件装入到一个数据变量中(importfile -f simple.txt dwData)
把文本文件作为一个名为 dwTag 的元素附加上去(put dwMsg dwTag dwData)
通过管道发送消息(send dwMsg dwoutpipe)
回到node1,我们应该看到发送文件的内容:
tagName used is dwTag
received a message...
<?xml version="1.0"?>
<ShellDoc>
<Item>
This is a simple file for Jxta transfer. It can contain anything.
</Item>
</ShellDoc>
JXTA>
我们已经完成了一个复杂的 shell 扩展的创建,并且揭示了如何用 Jxta 平台的功能创建 shell 扩展。
谈谈互操作性
因为我们的所有示例都基于 Java 平台,一些读者或许会怀疑 Jxta 的编程语言/平台的中立性,它是我们在本系列的第一篇论文强调的一个特征。但请记住,我们在这里用的是 Jxta 核心的 Java 实现(Jxta 的参考实现)。另外,请考虑到 Jxta shell 实际上是在 Jxta 核心上工作的、基于 Java 的 Jxta 应用,并且您可以看到我们拥有的是一个完全以 Java 为中心的环境。但平台和 shell 的其它实现也都是可能的。
事实上,做实际工作的协议交互动作是完全可互操作的。例如:要加入一个对等组,我们须用 Jxta 对等成员资格(Jxta Peer Membership)协议来联系成员资格对等组服务。要和一个管道沟通,我们必须用 Jxta 管道绑定(Jxta Pipe Binding)协议来联系管道服务。要共享一个广告,我们必须用 Jxta 发现(Jxta Discovery)协议在对等组上发布它。假设根据 Jxta 协议规范中描述的基础协议来实现,那么您就可以在另一个平台上构建一个能与我们的 Jxta shell 应用轻易地相互操作的 Jxta 和 Jxta shell 的实现。(要了解更多协议,请参阅本系列的第一篇论文。)
下一步:延伸到网络
Jxta shell 是试验和学习 P2P 基本原理的重要工具。它简单而优秀的可扩展性使我们可以轻易地添加新功能。在本文中,我们已经广泛使用了 shell 的命令行,并粗略地创建了我们自己的复杂的 shell 扩展。
我们大部分时候只在同一个局域网内的一个对等组内的机器上工作。(事实上,我们基本上都在一台机器上工作)。回想一下本系列的第一篇论文,可以看到 Jxta 对等组的边界不受底层的物理网络拓扑结构的限制。在本系列的最后一篇论文中,我们将直接看看 Jxta 发现如何能够扩展到广域网(通过路由器)和越过因特网上的防火墙(通过集中服务(rendezvous service))。
参考资料
参加本文讨论论坛。
本系列的第 1 部分概述了 Jxta 工程和这种新技术如何能在不硬性规定不必要的规则或具体的应用操作模型的情况下简化 P2P 应用的设计。
下载本文的源代码。
请访问 Jxta 社区的官方站点 Jxta.org,您可在这里找到最新的规范、文档、源代码和二进制文件。
假如您对基于 Jini 的技术的更多具体信息感爱好,请参阅 Sing Li 的 Professional Jini 这本书(Wrox Press,2000)。
另一个早期采用者对等工作组已经建立。这个组的战略会是什么,它与 Jxta 的关系又会是什么,这一点还不清楚。
Todd Sundsted 的位于 developerWorks 的对等计算专栏讨论基于 P2P 的技术的所有方面。
要了解一个可与之替换的开放源代码的 P2P 系统,请查看 Freenet 工程。
来自 IBM 的高级对等连网(Advanced Peer-to-Peer Networking,APPN)提供了强伸缩性、高可用性、安全的网络解决方案。
IBM 的 Magstar Peer-to-Peer Virtual Tape Server 被设计用来提高数据的可用性及改善您的灾难恢复的基础构造。
在 developerWorks Java 技术专区可找到更多的 Java 参考资料。
关于作者
SingLi 是 Professional Jini 的作者,也是 Wrox 出版社的其他许多书籍的作者。他定期为技术杂志投稿,同时还是 P2P 革命的积极推动者。Sing 是一位咨询专家和自由撰稿人,可通过 westmakaha@yahoo.com 与他联系。