最近在看《JAVA网络编程精解》到第十章突然迷糊了,怎么都看不懂怎么部署的,特别是在服务器与客户程序分布不同机器上的时候,java.rmi.server.codebase的时候,总是出现ClassNotFoundException。下面说说RMI应用程序的部署过程。
RMI应用由3部分组成,服务器、客户端和注册表程序,这三个程序部署由三种方式部
1.服务器、客户端和注册表程序在同一台的机器上,一般实验的时候可以使用,教材上都采用这种模式。
2.服务器与注册表程序运行在一台机器,而客户端运行在另一台机器上。下面讲的就是这种方式
3.服务器、注册表、客户端程序运行于三个不同的机器,这时就需要设置java.rmi.server.codebase=path,来让注册表程序能够通过path找到远程对象的接口类,并完成注册。实际上jdk5以后,采用动态代理生成stub类,客户端和(服务器-注册表分离时),通过java.rmi.server.codebase=path指名stub类的地址
关于java.rmi.server.codebase具体可以参考官方文档https://docs.oracle.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html
对于第二种部署方式,可以参考:https://segmentfault.com/a/1190000004494341#articleHeader22这里详细说明了两台机器上的不熟方式:关于上面链接程里有些不清楚的,我给出我的理解:
首先客户与服务器分离,分别创建java项目作为服务器和客户端
RMIClient
Client
RMMIServer:Hello
package com.cl.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote{
String sayHello() throws RemoteException;
}
RMMIServer:Server
package com.cl.remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class Server implements Hello{
public Server() {
super();
}
@Override
public String sayHello() throws RemoteException {
// TODO Auto-generated method stub
return "Hello World";
}
public static void main(String[] args) {
System.setProperty("java.security.policy",Server.class.getResource("rmi.policy").toString());
if(System.getSecurityManager()==null){
System.setSecurityManager(new SecurityManager());
}
try {
Registry registry=LocateRegistry.createRegistry(1099);
Server obj=new Server();
Hello stub=(Hello) UnicastRemoteObject.exportObject(obj,1099);
registry.bind("Hello", stub);
System.out.println();
System.err.println("Server ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
RMIClient:Client
package com.cl.client;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import com.cl.remote.Hello;
public class Client {
public static void main(String[] args) {
//System.out.println(Client.class.getResource("rmi.policy").toString());
//file:/E:/myprograms/WorkPlaceForEclipse_Java_NetProgram/RMIApp/bin/com/cl/rmi_test2/rmi.policy
System.setProperty("java.security.policy",Client.class.getResource("rmi.policy").toString());
if(System.getSecurityManager()==null){
System.setSecurityManager(new SecurityManager());
}
String host = (args.length<1)?null:args[0];
String port=(args.length<2)?"1099":args[1];
try {
Registry registry=LocateRegistry.getRegistry(host,Integer.parseInt(port));
Hello stub=(Hello) registry.lookup("Hello");
String response=stub.sayHello();
System.out.println("response:" +response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
RMIClient:Hello
package com.cl.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote{
String sayHello() throws RemoteException;
}
值得注意的是RMIClient和RMMIServer的Hello完全一样(包名也一样)具体客户端的接口类参考https://blog.csdn.net/gaohuanjie/article/details/38338765
客户端一定需要远程的接口类文件,因为需要在客户端调用远程类的远程方法,如果没有这个类编译是过不去的。开始我以为《JAVA网络编程精解》里提到的客户端没有服务端的类文件,以为是客户端可以没有远程接口类文件,结果最后才发现作者提到的是
如果Hello包名在客户端与服务端设置的不一样,会导致客户端Hello stub=(Hello) registry.lookup("Hello");转型时出错,因为返回的是com.cl.remote.Hello的存根类,而Client本地的Hello包名不是com.cl.remote话,是无法强转的。
rmi.policy参考https://segmentfault.com/a/1190000004494341#articleHeader22
部署过程:
1.win10上运行服务器程序:
因为在程序里设置了java.security.policy,不用再使用java -Djava.security.policy设置,客户端也一样。
2.lunix下部署客户端程序:
将RMIClient的bin目录拷到lunix下,如图:
随后输入:
其中192.168.56.1是win10主机的IP,1099是端口,可以发现lunix下输出了:
于是部署运行完成。
欢迎大家与我讨论RMI.