PP计算被Intel喻为第三代网络革命的点对点分散式网络架构JXTA是Sun旨在建立PP通用技术基础的计划定义了一组PP协议本文以一个简单的聊天应用为例介绍基于JXTA的PP应用开发 概述 PP即PeertoPeer称为对等连接或对等网络是一种点对点计算模式JXTA是项目创始人Sun首席科学家Bill Joy二十多年酝酿的结晶JXTA技术是网络编程和计算的平台用以解决现代分布计算尤其是PP计算中出现的问题JXTA协议是一组为PP网络计算而设计的协议共六种这六种协议分别是Peer Discovery ProtocolPeer Resolver ProtocolPeer Information ProtocolPeer Membership ProtocolPipe Binding Protocol以及Peer Endpoint Protocol利用这些协议我们可以让消息跨越多个网络发送到网络上的任意其他Peer这些协议是所有Java PP应用的基础请参见图一的JXTA应用体系(该图来自) 图一JXTA应用体系 本文的例子是一个基于JXTA的PP应用Peer可以利用它聊天它允许Peer把自己注册到网络上发送消息给其他Peer或者从其他Peer接收消息 编写JXTA应用要求JXTA内核以jxtajar JAR文件的形成存在(jxtajar文件可以从下载)jxtajar文件必须在CLASSPATH中 构造JXTA应用并不是一件很复杂的事情只需实现netjxtaplatformApplication接口并提供该接口定义的三个方法init()startApp()和stopApp()下面显示的SimpleJXTA类就是一个最简单的JXTA应用(注意程序必须导入netjxtadocumentAdvertisement类和netjxtapeergroupPeerGroup接口init()方法需要它们) // 最简单的JXTA应用 import netjxtaplatformApplication; import netjxtadocumentAdvertisement; import netjxtapeergroupPeerGroup; public class SimpleJXTA implements Application { public void init(PeerGroup group Advertisement adv) { } public int startApp(String[] args) { return ; } public void stopApp() { } } 作为一个Java应用上面的程序是否遗漏了static void main()方法呢?其实与普通Java应用相比JXTA应用的运行方式有所不同JXTA应用要求启动JXTA平台JXTA平台启动之后它将启动所有在jxtaConfig配置文件中指定的应用JXTA平台启动一个应用时首先调用应用的init()方法接着调用startApp()方法 JXTA聊天程序 本文的聊天应用改编自JXTA Shell软件包的Talk程序设计这个应用的主要目的是为了学习JXTA编程它很简单与基于Java Socket的许多聊天应用相比它缺少许多高级特性且不具备强健的错误控制机制然而这个程序用到了许多重要的JXTA类和接口初学者可以由此开始深入了解JXTA 程序有一个很简单的GUI如图二所示 图二程序界面 所有来自聊天用户的消息格式为[用户名字] 消息聊天程序本身报告的信息都以>开头 聊天程序的业务规则为 ● 用户在登录文本框中输入自己的名字并点击登录按钮登录 ● 用户名字必须唯一任何两个用户不能有相同的名字如果某个用户输入的名字正在被其他用户使用则应用将报告该信息正在试图登录的用户必须改变自己的名字 ● 成功登录后用户在消息输入框中输入消息所有消息将以广播的形式发送给当前已经登录的所有用户包括消息发送者本身 登录 登录的目的不是进行身份验证任何拥有聊天程序的人都可以加入聊天登录的目的是为了让其他用户知道当前用户的存在使得聊天程序能够发送消息或者从其他聊天用户接收消息 消息通过管道从一个Peer传递到另一个Peer管道由netjxtapipe包里面的Pipe接口描述管道是在两个JXTA应用或服务之间传递消息的核心机制它为两个Peer之间的通信提供了简单的单向的异步的通道换句话说管道把多个Peer终端连接到了一起接收端称为输入管道发送端称为输出管道要让输出管道能够把消息发送到接收管道两个管道首先必须互相认知对方例如聊天应用的一个实例能够把消息发送给另一个实例之前两个实例必须互相认知对方应用的一个实例必须把自己介绍给其他实例这是如何完成的呢? 在JXTA术语中让其他Peer知道某个Peer已经在线叫做广告(advertising)从技术上看advertising意味着把一个广告发送给网络上的所有用户这里的广告可以是任意网络资源的标识符可以用来描述各种核心对象例如PeerPeerGroupServicePipeContent或EndpointJXTA广告是与平台无关的一般以XML文档的形式提供 在聊天程序中由于管道是信息交换的核心对象PipeAdvertisement代表着各个聊天用户PipeAdvertisement描述了一个管道通信通道管道服务利用它创建关联的输入和输出管道终端 当一个Peer发送它的广告给另一个Peer它可以期待另一个Peer的回应即发回它的广告按照这种方式双方都将拥有对方的广告广告由应用缓沖以便以后使用当应用需要广播一个消息时会用到这些缓沖的广告JXTA自动在cm子目录的特定目录下以文件的形式(文件名字唯一)缓沖广告对于本文的聊天应用JXTA创建了一个名为edb的目录在这个目录下有子目录AdvGroupsPeerInfoPeersprivatepublic和tmp如图三所示所有与聊天应用联系的Peer的所有PipeAdvertisement都保存在Adv目录下包括聊天应用自己的PipeAdvertisement educitycn/img_///gif > 图三cm目录结构 当一个用户登录聊天应用首先检查所有本地缓沖的PipeAdvertisement如果其中一个缓沖的PipeAdvertisement的用户名字与聊天用户输入的名字相同则聊天应用将向用户提示名字已经被使用这种提示可能是错误的因为以前使用该用户名字的远程用户当时不一定登录另外由于聊天应用缓沖了它自己的PipeAdvertisement用户不能在下一次会话中使用同一个名字因此如果必要在启动应用之前可以清除Adv目录 当聊天应用搜索完所有本地缓沖的PipeAdvertisement且不能找到匹配的用户名字时它将把搜索扩展到远程用户如果用户名字已经被某个远程用户使用聊天应用将向试图登录的用户提示该名字不可用 至此为止聊天应用已经可以发送消息为了接收消息必须启动执行监听任务的线程 sendAll()方法提取所有本地缓沖的PipeAdvertisement以用户输入的文字为基础构造出一个消息然后把消息发送给每一个PipeAdvertisement已经缓沖的远程用户首先sendAll()方法获取发送者的用户名字接着sendAll()方法调用getAllPipeAd()方法获取一个包含所有本地缓沖的PipeAdvertisement的Vector然后sendAll()方法利用循环依次访问各个PipeAdvertisement利用Pipe接口的createOutputPipe()方法构造一个OutputPipe对象调用createOutputPipe()方法时传入找到的PipeAdvertisement如下所示 // 广播广告 int length = pipessize(); for (int i = ; i<length; i++) { PipeAdvertisement adv = (PipeAdvertisement) pipesget(i); OutputPipe pipeOut = null; try { // 这个值仅用于测试 // 可能需要等待更长时间 pipeOut = pipecreateOutputPipe(adv PipeNonBlocking ); }catch (Exception e) { } 如果成功地创建了OutputPipe对象接下来开始构造Message对象 if (pipeOut != null) { Message msg = null; String userInput = null; InputStream inputStream = null; try { // 构造一个消息 msg = pipecreateMessage(); Message对象的push()方法用来以InputStream的形式插入一组数据因此先把用户消息(messageBoxgetText())转换成一个Byte数组再转换成ByteArrayInputStream然后把InputStream以参数的形式提供给push()方法另外我们还要在消息中加入发送者的名字为此先从sender字符串构造出另一个InputStream再把它作为参数传递给push()方法 inputStream = new ByteArrayInputStream( messageBoxgetText()getBytes()); msgpush(SENDER_TEXT inputStream); inputStream = new ByteArrayInputStream(sendergetBytes()); msgpush(SENDER_ID inputStream); 准备好Message对象之后把它作为参数传递给OutputPipe类的send()方法把消息发送给接收的Peer pipeOutsend(msg); 最后关闭OutputPipe pipeOutclose(); run()方法执行与sendAll()方法相反的操作run()方法创建一个InputPipe监听传入的消息提取出Message对象的用户名字和内容并调用toDisplay()方法显示出消息 首先run()方法利用当前Pipe对象(pipe)的createInputPipe()方法创建一个InputPipe如果createInputPipe()方法返回null则显示不能打开InputPipe错误信息反之如果成功地创建了InputPiperun()方法开始一个无结束条件的while循环监听传入的消息如下所示 // 监听传入的消息 while |