JXTA源码分析

JXTASun推出的一个P2P计算平台,设计的初衷是一个提供基础的P2P计算功能,它改进了目前已有P2P系统的一些不足之处,比如直接支持许多地址解析和物理层协议、用Java开发带来的平台独立性和可用于PC之外的物理设备等多样性。

 

JXTA也因为领域不同而分了JXSEJXME两个方向,跟J2SEJ2ME一样,后者都是用于移动设备平台开发的库。笔者先从JXSE开始,通过源码阅读分析来研究JXTA的原理和特点。目前JXTA的最新版本是2.5。官方网站是https://jxta.dev.java.net/

 

相信大家都用过Java里的SocketServerSocket了,Jxse在遵循它自己的基础协议情况下也提供了JxtaSocketJxtaServerSocket两个higher-level的通信类。这里就先来看看它们的内部如何实现的:

 

ØJxtaServerSocket

先看看该类的声明:

public class JxtaServerSocket extends ServerSocket implements PipeMsgListener

 

可以看到它其实是继承了java.net.ServerSocket类并实现了自身协议的通用消息监听接口的(可以回想一下Design Pattern的观察者模式)。

 

该类的成员数据包含的一些关键变量举例如下:

private final static int DEFAULT_BACKLOG = 50; private final static long DEFAULT_TIMEOUT = 60 * 1000L; protected static final Message QUEUE_END_MESSAGE = new Message(); protected InputPipe serverPipe; protected BlockingQueue<Message> queue = null; protected static final String reqPipeTag = "reqPipe";

 

 

首先按照 Jxta 的协议所有的通信其实都是通过 XML 或者 Binary 的格式来进行,这里的 Message 类就是它自身对通信内容的一个 XML 封装,类似 reqPipeTag 这样的 String 静态常量还有很多个, JxtaServerSocket 通过 serverPipe 这个管道来监听所有请求连接,由于实现了 PipeMsgListener 当有 Pipe 消息到达时会触发 pipeMsgEvent 方法来处理 Pipe 消息,通信格式是 XML ,处理方式首先是把收到的 Message 压入 queue 中去排队。

 

在继承自ServerSocketaccept方法中,就开始从queue中依次去除Message对象,遍历寻找之前提到静态常量的那些Tag里的内容,至少都会包含对方请求连接者的PeerPipe信息,Jxta的通信都是Peer(同位体)之间通过建立Pipe进行的。成员变量中的BACKLOG就是queue中默认的消息数量50TIMEOUT就是等待连接建立的默认超时60秒。

 

真正拿到Message建立连接是通过一个processMessage方法,它首先从Msg XMLTag中找到对方Peer的标识即Advertisement、是否需要认证、对方Peer所属的Group信息、是否要求建立raliable的连接等等。所有参数找齐全之后调用JxtaSocket的一个protected签名(只内部使用)的构造函数完成与对方的连接建立。

 

值得一提的是它重写的close方法里,首先关闭了一直监听连接请求的serverPipe、清空消息队列queue,之后会往队列中放一个空消息QUEUE_END_MESSAGE,这样的目的是在accept中成功的跳出一个while(true)的循环。

ØJxtaSocket

接着上面的分析,先看看JxtaSocket中那个被JxtaServerSocket内部调用真正建立连接的构造函数:

 

 protected JxtaSocket(PeerGroup group, PipeAdvertisement pipeAdv, PipeAdvertisement remoteEphemeralPipeAdv, PeerAdvertisement remotePeerAdv, Credential localCredential, Credential remoteCredential, boolean isReliable) throws IOException

 

它在将这一系列参数赋值给自己的成员变量之后,主要是走了一个bind()->connect()的流程,完成这个流程之后构造了一个connectResponseMessage通过PipeService的监听服务发送出去。这里有个细节是JxtaSocket通过一个boolean型变量initiator来标识谁是连接发起方,那么在这个由JxtaServerSocket内部调用完成连接的函数里就自然把initiator赋值为false了,因为连接是Socket Client发起的嘛。

 

接下来看看bind()->connect()流程:

bind方法做的事很简单,从Pipe Server里构造通过一个Advertisement构造了出一个InputPipelocalEphemeralPipeIn。这里对于不熟悉Jxta的朋友稍微解释一下,Jxta的协议有非常严谨的标识(Indentifier)过程,任何一个通信同位体Peer或者一个同位体组Peer Group,或者一个用于通信的Pipe都需要一个唯一的标识。Jxta使用Advertisement(广告)来发布一个新的标识,所以构造出任何一个Peer/Group/Pipe都需要提供一个实现准备好的Adv(广告)。说白了这样在通信的过程中,Jxta的服务可以很方便的定位查找鉴别任何一个标识对应的是什么玩意。

 

connect()方法中主要是初始化各种通信的管道,其中内置的Outgoing对象被reliable的连接模式下的output/input stream用于发送所有Message。如果是reliable模式,输入输出分别使用的是RaliableInputStream/ReliableOutputStream,非reliable模式输入输出流使用的是JxtaInputStream/JxtaOutputStream

 

Outgoing是通过在构造函数中拿到对方的Group/Pipe/Peer的所有Adv之后利用Endpoint Service提供的通信协议管道服务构造出来的。这样说可能比较难理解,还是得结合一下背景,Jxta这个平台本身提供了许多协议,主要是用于Peer之间发布各自能提供的服务、或者各Peer之间通信、或者各个Peer Group之间产生某种协作关系共享一些信息阿之类的。所以只要使用Jxta的任何class来建立通信,首先都会加入到一个Jxta网络中去,基本的几个服务已经就默认的开始工作了(类似安全认证那些服务需要由用户自己配置才会启用)。说白了就是ClientServer端都自愿加入进了Jxta网络,它会帮助你请求建立Socket连接的时候转发一些Message,引导你的管道或者Stream能顺利的连接到对方。这样的好处是双方都是主动加入了JxtaP2P计算网络,它就能帮助各个Peer通信不受防火墙的阻碍,可以穿透NAT通信等等。(关于NAT穿透原理之前有转载一些文章介绍)

 

Jxta网络就像一个交通警察一样,帮助疏通各Peer之间通信的通道,在遇到Firewall时候它会告诉某Peer对方无法连过来,得你连过去,起到一个指挥的作用。

 

由于Jxta国内使用还不多,太多细节无法一一介绍,讲得可能比较乱,希望大家见谅!

下次将深入到raliable和非-reliable的具体管道类里面看看data是如何发送和接受的。


你可能感兴趣的:(xml,socket,网络,Stream,平台,p2p)