1 引言 管道的概念源于Unix,是不同线程之间直接传输数据的基本手段。JDK中java.io包中就有管道类,同时,管道在JXTA中是最基本的概念,是对等点之间的数据传输的主要方式。对等管道协议(PBP)明确规范了对等管道的绑定,解析,响应。 本文依次剖析集中式(JDK)和对等环境下(JXTA)管道的实现方式,对比分析其异同,然后尝试在JXTA中建立一个虚拟的全双工的管道。 本文的目标是通过对不同环境下管道的实现方式对比分析,来理解为什么JXTA采用管道作为基本的数据传输手段。 2 管道的形象化描述 一个生活中的情景:现在有两个地区A,B。A是石油生产区,B是石油消费区,现在B地区需要消费A地区的石油,当然可以通过海运,空运获得,然而最通常的方式是架设输油管道。如图所示:  1 引言 管道的概念源于Unix,是不同线程之间直接传输数据的基本手段。JDK中java.io包中就有管道类,同时,管道在JXTA中是最基本的概念,是对等点之间的数据传输的主要方式。对等管道协议(PBP)明确规范了对等管道的绑定,解析,响应。 本文依次剖析集中式(JDK)和对等环境下(JXTA)管道的实现方式,对比分析其异同,然后尝试在JXTA中建立一个虚拟的全双工的管道。 本文的目标是通过对不同环境下管道的实现方式对比分析,来理解为什么JXTA采用管道作为基本的数据传输手段。 2 管道的形象化描述 一个生活中的情景:现在有两个地区A,B。A是石油生产区,B是石油消费区,现在B地区需要消费A地区的石油,当然可以通过海运,空运获得,然而最通常的方式是架设输油管道。如图所示: java中流的概念和管道的概念都可以通过此案例阐述,A与B之间连接的就是管道,负责将A的石油向B输出。A向管道输出数据(output),B从管道输入数据(input),可以这样理解,管道是A的输出对象,是B的数据源。这里就产生了三个类:输出流A,输入流B,管道。输入流B负责如何获取数据(read 操作),输出流A负责如何消费数据(write操作),管道负责连接它们(connect 操作)。其实,在实现时,管道类分解为管道口,管道出口,由入口出口负责连接。在复杂的网络环境中,这种连接方式可以有专门的网络协议负责(例如,JXTA中的PBP,全称Pipe Bind Protocol)。 由以上描述,我们可以清楚知道最原始的管道就是单向的,文章后面介绍的双向管道,是用两个单向管道虚拟的,而非真实的连接方式。不难发现管道最关键的问题是如何协调输出(A)与输入(B)。这在不同的网络环境会遇到不同的问题,最简单的是同一JVM下的不同过程(线程或任务)之间用同步方式传递数据。而对等环境下,如何去发现对方就是一个很现实的问题,这仅仅只是问题的其中之一,下面的章节会依次分析。 3 集中式环境下管道的实现 问题的描述:A与B是在同一JVM中,A,B有一方能够发现另一方的存在,A将数据发往B方,A发送数据与B接收数据是相互独立的。 现在回到问题的最初:为什么要使用管道?A只管发送,B只管接受,那么数据在哪儿呢?经过下面的分析,就会明白管道把管理数据缓冲区的重任交给了他自己,A,B均是围绕这个缓冲区来启停线程的,显然这才是问题的本质。 JDK中,类PipeInputStream(即前面所述的B)与PipeOutputStream(即前面所述的的A)可以很好的解决这一问题。首先给出类图如下。  下面是将类PipeOutputStream的connect方法代码简化后给予注释。 public synchronized void connect(PipedInputStream snk) throws IOException { sink = snk; //将PipeInputStream的实例作为PipeOutputStream的一个属性,以便调用 snk.in = -1;//缓冲区的输入位置,<0表示缓冲区为空 snk.out = 0;//缓冲区的输出位置 snk.connected = true; } 连接以后,PipeOutputStream的write操作直接调用sink.receive(b);这样,对缓冲区buffer的维护,就变成了read()和receive()操作之间的线程同步。JDK对缓冲区的处理非常巧妙,采用了循环列表,它用缓冲区的标志位的变化来代替数据的移动,类似于生活中的时钟把线性的时间规范为24小时来表示。这不属于本文的论述范围,就不继续分析了。 read操作,正常情况下,从out位置读取数据。缓冲区空时进入等待状态。以轮询的方式(1秒间隔)来自我释放。 receive操作,正常情况下,向in位置写入数据。缓冲区满时进入等待状态。同样,以轮询的方式(1秒间隔)来自我释放。 4 JXTA对等管道的实现 通过对JDK的分析,我们可以了解到在集中式环境下,管道的架设方案是比较简单的。在对等环境下(分布式环境下也类似),出于同样的目标,遇到的问题却在急剧的扩大。例如,管道入口和出口之间如何相互发现?数据如何保证在不同的环境下传送?甚至,对管道本身的概念发生质疑:一定是单入口,单出口吗? JXTA规范中,管道是在端点之上的服务或应用之间发送和接收信息的虚拟连接通道,管道提供在对等端点传输之上的网络抽象。管道有点到点和广播两种通信模式。 JXTA是通过管道广告来唯一标示管道的,输出管道要找到与其广告相同的输入管道才能发送数据,广告内容如下 <!DOCTYPE jxta:PipeAdvertisement> <jxta:PipeAdvertisement xmlns:jxta="http://jxta.org";> <Id> urn:jxta:uuid-59616261646162614A787461503250335003093E73074218AE3ABBE08EF3CBE303 </Id> <Type> JxtaUnicast </Type> <Name> PipeExample </Name> </jxta:PipeAdvertisement> 如果您需要对JXTA管道有实例化的概念,请参考Sing Li的使p2p能进行交互操作:Jxta命令shell ,这篇文章有部分内容专门介绍了如何在通过shell使用管道。本文主要是从编程的视角去看管道是如何实现的。 4.1 客户视角 Project JXTA : Java Programmer's Guide Chapter7有个例子阐述如何去在对等点之间发送信息,读者可以到www.jxta.org下载源码。现在从客户视角简要的分析它的传送原理,要深入的了解可以看下一节的系统视角分析。 该例中,有两个对等点,并且构建了两个不同的类:一个负责接收(Pipelistener),一个负责发送(PipeExample)。具体的接收次序可以参考[1] [2] 下一页
|
|