1.定義遠程接口中的參數和返回值類(lèi)型。必須保證其可以被序列化。這點(diǎn)往往被很多人忽視。 RMI規范其中很重要一點(diǎn)就是要求遠程接口中的參數(返回值)應實(shí)現序列化。因為需要將客戶(hù)端的對象(參數)轉化成byte stream,傳輸到服務(wù)端后還原成服務(wù)端的對象進(jìn)行調用?;蛘呤切枰獙⒎?wù)端的對象(返回值)轉化成byte stream,傳輸到客戶(hù)端還原成客戶(hù)端的對象進(jìn)行調用。當然,基本數據類(lèi)型以及String,數組等都已經(jīng)是可序列化的。在演習的例子中,參數等都比較簡(jiǎn)單,沒(méi)有涉及到這個(gè)步驟。(順便查閱了不少關(guān)于序列化的文章,相關(guān)內容將另成一篇。)
2.定義一個(gè)遠程接口,該接口繼承java.rmi.Remote,同時(shí)定義了所有將被遠程調用的方法。這些方法必須拋出 RemoteException 異常。
WeatherForecastRemote.java:
package com.xiaohu_studio.weatherforecast;
import java.rmi.*;
public interface WeatherForecastRemote extends Remote {
String WFToday() throws RemoteException;
String WFTomorrow() throws RemoteException;
}
3.定義一個(gè)繼承于java.rmi.UnicastRemoteObject且實(shí)現上述接口的類(lèi)。將要被遠程調用的對象就是這個(gè)類(lèi)的對象。要注意的是,必須要有最少一個(gè)顯式的構造函數。構造函數必須拋出RemoteException異常。
WeatherForecast.java:
package com.xiaohu_studio.weatherforecast;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class WeatherForecast extends UnicastRemoteObject implements
WeatherForecastRemote {
static String[] description={"Sunny","Cloudy","Rainy","Windy"};
public WeatherForecast() throws RemoteException {}
public String WFToday() throws RemoteException {
return description[(int)(Math.random()*4)];
}
public String WFTomorrow() throws RemoteException {
return description[(int)(Math.random()*4)];
}
}
4.編寫(xiě)服務(wù)端代碼,創(chuàng )建和安裝一個(gè)安全性管道,并注冊遠程對象。發(fā)現可以忽略安裝安全性管道。如果安裝的話(huà),必須創(chuàng )建一個(gè)安全策略文件,否則會(huì )出現AccessControlException??蛻?hù)端也一樣。
WeatherForecastServer.java
package com.xiaohu_studio.weatherforecast;
import java.rmi.*;
import java.rmi.registry.*;
//import java.rmi.server.*;
//import java.net.*;
public class WeatherForecastServer {
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.out.println("Set Security Manager.");
System.setSecurityManager(new RMISecurityManager());
}
try {
WeatherForecast wf=new WeatherForecast();
LocateRegistry.createRegistry(8898); // 注冊端口
Naming.bind("rmi://127.0.0.1:8898/wf",wf);
} catch (Exception e) {
System.out.print("Error---- "+e.toString());
e.printStackTrace();
}
}
}
安全策略文件policy.txt:
grant {
permission java.security.AllPermission;
//Allow everything for now
};
(步驟3和4可以合并成一個(gè)類(lèi)。)
5.編寫(xiě)客戶(hù)端代碼,創(chuàng )建和安裝一個(gè)安全性管道,查找遠程對象,并調用遠程方法
WeatherForecastClient.java:
package com.xiaohu_studio.weatherforecast;
import java.rmi.*;
public class WeatherForecastClient {
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.out.println("Set Security Manager.");
System.setSecurityManager(new RMISecurityManager());
}
try {
WeatherForecastRemote ff;
//System.out.println("Lookup the Remote object.");
ff=(WeatherForecastRemote)Naming.lookup("rmi://127.0.0.1:8898/wf");
//注意是WeatherForecastRemote,而不是WeatherForecast,否則拋出ClassCastException.
System.out.print("Today‘s weahter: ");
System.out .println(ff.WFToday());
System.out.print("Tomorrow‘s weather: ");
System.out .println(ff.WFTomorrow());
} catch (Exception e){
System.out.println("Error:"+e.toString());
}
}
}
6.生成stub和skeltion ,并將stub打包到客戶(hù)端jar中,將skeltion打包到服務(wù)端jar中。
在演習的例子的時(shí)候,發(fā)現用直接mic命令(不帶參數)只生成_Stub.class,將該_Stub.class同時(shí)打包到客戶(hù)端和服務(wù)端即可。
rmic com.xiaohu_studio.weatherforecast.WeatherForecast
(如果需要生成_Slel.calss,則需用rmic -vcompat 命令)
7.用命令 rmiregistry 命令來(lái)啟動(dòng)RMI注冊服務(wù)。
在命令行輸入命令 :
start rmiregistry 8898
如果在服務(wù)端代碼中包含了啟動(dòng)RMI注冊服務(wù)的代碼(LocateRegistry.createRegistry(8808);),則可以省略該步驟。
8.運行服務(wù)端及客戶(hù)端代碼。
服務(wù)端文件:(目錄:d:\server\com\xiaohu_studio\weatherforecast)
policy.txt
WeatherForecastRemote.class
WeatherForecast.class
WeatherForecastServer.class
WeatherForecast_Stub.class
服務(wù)端運行命令:(目錄:d:\client\com\xiaohu_studio\weatherforecast)
java -Djava.security.policy=com\xiaohu_studio\weatherforecast\policy.txt com.xiaohu_studio.weatherforecast.WeatherForecastServer
客戶(hù)端文件:
policy.txt
WeatherForecastRemote.class
WeatherForecast_Stub.class
客戶(hù)端運行命令:
java -Djava.security.policy=com\xiaohu_studio\weatherforecast\policy.txt com.xiaohu_studio.weatherforecast.WeatherForecastClient