轉載自:http://www.juuluu.org/html/softDoc/2011/10/19/09/45/wtpnkjxlwt.html
以下是一個RMI項目的實施筆記:
環境:防火牆(XX衛士) 外網IP:x.x.x.135 port:8400/8500 內網IP: x.x.x.90 port:8400/8500
內外網IP及port互相映射. (外網IP是設定的防火牆上的,管理員進行映射配置)
1.RMI遠程調用的服務註冊到:內網的8400連接埠上 (不用建立stub/skeleton,不使用預設的1099)
//LocateRegistry.createRegistry(rmiServerPort); //定義服務註冊與尋找服務連接埠 8400
2.RMI遠程調用的資料通訊連接埠綁定到:內網的8500連接埠上
// RMISocketFactory.setSocketFactory(new SMRMISocket()); //定義資料轉送連接埠 8500
3.設定訪問外網:x.x.x.135:8400時穿透防火牆訪問內網:x.x.x.90:8400
//在sun.rmi.server.UnicastRef調用時的服務IP為:x.x.x.135,而不是x.x.x.90(客戶的本地不存在RMI服務嘛)
//System.setProperty("java.rmi.server.hostname","x.x.x.135");
4.RMI調用穿透防火牆不是自動的,需要我們手工指定,就如3所指,如果不加設定,在互連網上就不能通過外網IP訪問內網IP上綁定的RMI服務
5.通過本項目,明白了RMI可以不用rmic建立stub/skeleton的,也可以不用啟動rmiRegistry而通過程式直接啟動RMI服務,這樣方便了代碼的修改與項目的實施.
由於是項目代碼,所以不打包上傳代碼了。簡寫如下:
///////RMI服務端代碼
String hostIP = InetAddress.getLocalHost().getHostAddress();
int rmiServerPort = 8400; //Data Transmission Service為8400
String bindName = "licenseServer";
//下面這行代碼不能少,否則當路由器x.x.x.135映射到的內網IP:x.x.x.90時,
//訪問RMI服務時將導向本地的x.x.x.90,那麼用戶端就是訪問本地x.x.x.90,
//這絕對錯誤.服務是在公網路由器(含公用IP)的後面,不在客戶的本地
System.setProperty("java.rmi.server.hostname","x.x.x.135");
try {
RMISocketFactory.setSocketFactory(new SMRMISocket()); //定義資料轉送連接埠 8500
LocateRegistry.createRegistry(rmiServerPort); //定義服務註冊與尋找服務連接埠 8400
}
catch (Exception ex) {
System.out.println("伺服器連接埠綁定時發生錯誤:"+ex.getMessage());
ex.printStackTrace();
}
//建立license產生器的服務物件
MakeLicense licenseService = new MakeLicenseService();
//綁定一個服務物件到一個服務連接埠
//URL format (without the scheme component)
String bindUrl = "//"+hostIP+":"+ rmiServerPort +"/"+bindName;
Naming.rebind(bindUrl, licenseService);
System.out.println(bindUrl+" server is ready.");
////SMRMISocket.java
import java.rmi.server.*;
import java.io.*;
import java.net.*;
public class SMRMISocket
extends RMISocketFactory {
public Socket createSocket(String host, int port) throws IOException {
return new Socket(host, port);
}
public ServerSocket createServerSocket(int port) throws IOException {
if (port == 0)
port = 8500;
System.out.println("RMI伺服器的註冊與資料轉送連接埠 ="+port);
return new ServerSocket(port);
}
}
////用戶端代碼:
private String remoteHost = "x.x.x.135"; //公網IP或區域網路IP
private int rmiServerPort=8400; //尋找服務連接埠 8400
private String bindName = "licenseServer"; //RMI服務名稱
private int revCount = 0;
private MakeLicense remoteObject=null;
public LicenseRequestClient() {
try{
// if(System.getSecurityManager()==null){
// System.setSecurityManager(new RMISecurityManager());
// }
String bindUrl = "//"+remoteHost+":"+ rmiServerPort +"/"+bindName;
System.out.println("請求的遠程服務URL="+bindUrl);
MakeLicense remoteObject = (MakeLicense) Naming.lookup(bindUrl);
this.remoteObject = remoteObject;
// System.out.print("遠程remoteObject="+remoteObject);
}
catch (RemoteException re) {
System.out.println("RemoteException:" + re);
}
catch (NotBoundException nbe) {
System.out.println("NotBoundException:" + nbe);
}
catch (MalformedURLException mfe) {
System.out.println("MalformedURLException:" + mfe);
}
}