扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:Philip Bishop and Nigel Warren 来源:javaresearch 2007年9月3日
关键字:
值得注意的一点的是上面的例子中使用MarshalledObject。如果我们简单地串行化Remote对象到流,在客户端会发生一个ClassNotFoundException,除非客户端已经访问了服务器的stub(在大多数情况下这是糟糕的)。客户端会得到ClassNotFoundException是因为,不同于通过RMI传递对象,codebase会被附加到流中,在这儿我们是透过一个socket使用串行化,不会包含codebase。
MarshalledObject在Java 2中加入,提供了一个方便的途径来传递串行化的对象及其codebase。在内部,MarshalledObject将对象串行化到字节数组,这意味着当MarshalledObject被解串行化时,内部的对象不会解串行化。这对Jini服务,如查找服务,是极其有用的,因为它们不再被迫去下载注册的代理所代表的类。
要访问内部的对象,你要在客户端调用MarshalledObject的get()方法。
实现客户端的类
前面我们说明了RMI客户端怎样通过指定接口类名字和服务器的唯一名字来发现RMI服务器,如下所示:
Class clazz=Translator.class;
String id="Spanish";
Translator service
=(Translator)RMIDiscovery.lookup(clazz,id);
在考虑怎样实现我们的RMIDiscovery类以前,让我们先扼要重述一下它的职责:
1. 监听来自服务器的RMILookup的单播响应
2. 向多播地址发送UDP包
3. 从流中读取远程对象
4. 停止发送多播数据包
5. 停止在单播socket上监听
6. 使用服务器
建立单播TCP/IP监听器
要建立一个单播TCP/IP socket,我们必须选择一个监听的端口。不过,我们不能简单地将一个固定的端口号定义成常量,因为其他进程可能在使用这个端口。我们因此需要指定一个使用的端口号的范围:
private ServerSocket startListener(int port,int range){
ServerSocket listener=null;
for(int i=port;listener==null && i<(port+range+1);i++){try{
listener =new ServerSocket(i);
}catch(IOException ex){
//端口(可能)已经被使用
//处理违例
}}
return listener;
}
上面的startListener()方法尝试在指定范围内的一个端口上创建ServerSocket。此方法的调用者可以检查返回值是否为null(null意味着ServerSocket不能被创建)并获得使用的端口。另一个选择是在ServerSocket不能被创建时抛出一个违例:
ServerSocket listener=startListener(START_PORT,RANGE);
if(listener!=null){
int port=listener.getLocalPort();
//format message to include port number格式化消息以包含端口号
//start the multicast message dispatcher 启动多播消息分发器
Socket sock=listener.accept();
//read remote stub from stream 从流中读取remote stub
}
当我们成功地建立了单播监听器,我们就可以格式化消息数据包并启动多播消息分发器。
建立多播UDP分发器
如同多播监听器,我们必须使用一个已知的多播地址/端口联合。我们可以通过System属性或者通过一个常量来获取这项数据:
int port=6789;
String multicastAddress="228.5.6.7";
MulticastSocket socket=new MulticastSocket(port);
InetAddress address=InetAddress.getByName(multicastAddress);
socket.joinGroup(address);
//outMsg是用定界符划分的请求
byte [] buf=outMsg.getBytes();
//循环n次或一直到单播监听器收到响应为止
DatagramPacket packet=new DatagramPacket(buf,
buf.length,address,multicastPort);
socket.send(packet);
//结束循环
socket.leaveGroup(address);
socket.close();
一步一步察看上面的代码,你可以看到当我们配置好MulticastSocket之后,outMsg字符串被转换成一个字节数组以便从socket发送出去。注释说明了然后我们将发送消息预先指定的次数或者直到单播监听器收到响应为止。为了使例子简明,我们从中省略了与单播监听器的线程间的协调工作;你可以下载整个源码(见文后Resources)来看看这是怎样完成的。
读取服务器的stub
前面我们已经看到了怎样建立一个单播ServerSocket。现在我们要看看读取服务器的stub的代码。方法ServerSocket.accept()是阻塞的,所以它不会返回一个Socket对象,除非进入的连接已经完成:
Socket sock=listener.accept();
ObjectInputStream ois=new ObjectInputStream(sock.getInputStream());
MarshalledObject mo=(MarshalledObject)ois.readObject();
sock.close();
//server是一个成员域
server=(Remote)mo.get();
当我们获得了服务器的一个引用,我们接着可以唤醒调用RMIDiscovery.lookup()而被阻塞的线程,它将给客户端返回一个Remote对象。
采用Jini
在这篇文章中,我们向你展示了怎样为普通的RMI客户端和服务器应用一项类似Jini的发现概念的技术。虽然我们建议在新的项目中使用Jini,你还是能够用类似发现的机制来增强现有的RMI系统从而获得好处。
前面说明的RMI发现机制有一些Jini能够克服的局限。例如,多播UDP有受限制的范围,通常是一个子网内。这意味着使用我们的多播机制的客户端不能发现在多播范围以外运行的RMI服务器。然而,Jini有联合查找服务的概念可以“加入”不同的子网以使跨越WAN(广域网)的发现过程对客户端透明。
我们鼓励读者下载全部源码(见文后Resources)来进行试验。用一个RMI服务器为许多在多播范围以外运行的服务器的远程引用做委托或代理,并在其中使用RMILookup工具类,会是一个有趣的试验。
根本来说,Jini是一个更好更优雅的解决方案,所以我们强烈建议还没有体验过Jini的读者尽快体验一下。
最后,要指出的是,一般来说多播UDP在没有连接到集线器的独立的机器上不能工作。使用loopback适配器是可选的方案;不过,我们在基于Windows的机器上使用这种方法时遇到了错误。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者