一 .RMI概述
RMI(Remote Method Invocation)
RMI是分布式對象軟件包,它簡化了在多臺計算機上的JAVA應(yīng)用之間的通信。必須在jdk1.1以上
RMI用到的類
java.rmi.Remote 所有可以被遠程調(diào)用的對象都必須實現(xiàn)該接口
java.rmi.server.UnicastRemoteObject 所有可以被遠程調(diào)用的對象都必須擴展該類
什么是RMI
遠程方法調(diào)用是一種計算機之間對象互相調(diào)用對方函數(shù),啟動對方進程的一種機制,
使用這種機制,某一臺計算機上的對象在調(diào)用另外一臺計算機上的方法時,使用的程
序語法規(guī)則和在本地機上對象間的方法調(diào)用的語法規(guī)則一樣。
優(yōu)點
這種機制給分布計算的系統(tǒng)設(shè)計、編程都帶來了極大的方便。
只要按照RMI規(guī)則設(shè)計程序,可以不必再過問在RMI之下的網(wǎng)絡(luò)細節(jié)了,如:TCP和Socket等等。
任意兩臺計算機之間的通訊完全由RMI負責(zé)。調(diào)用遠程計算機上的對象就像本地對象一樣方便。
1、面向?qū)ο螅?br />
RMI可將完整的對象作為參數(shù)和返回值進行傳遞,而不僅僅是預(yù)定義的數(shù)據(jù)類型。
也就是說,可以將類似Java哈西表這樣的復(fù)雜類型作為一個參數(shù)進行傳遞。
2、可移動屬性:
RMI可將屬性從客戶機移動到服務(wù)器,或者從服務(wù)器移動到客戶機。
3、設(shè)計方式:
對象傳遞功能使您可以在分布式計算中充分利用面向?qū)ο蠹夹g(shù)的強大功能,如二層和三層結(jié)構(gòu)系統(tǒng)。
如果用戶能夠傳遞屬性,那么就可以在自己的解決方案中使用面向?qū)ο蟮脑O(shè)計方式。
所有面向?qū)ο蟮脑O(shè)計方式無不依靠不同的屬性來發(fā)揮功能,如果不能傳遞完整的對象——包括實現(xiàn)和類型
——就會失去設(shè)計方式上所提供的優(yōu)點。
4、安全性:
RMI使用Java內(nèi)置的安全機制保證下載執(zhí)行程序時用戶系統(tǒng)的安全。
RMI使用專門為保護系統(tǒng)免遭惡意小程序侵害而設(shè)計的安全管理程序。
5、便于編寫和使用
RMI使得Java遠程服務(wù)程序和訪問這些服務(wù)程序的Java客戶程序的編寫工作變得輕松、簡單。
遠程接口實際上就是Java接口。
為了實現(xiàn)RMI的功能必須創(chuàng)建遠程對象任何可以被遠程調(diào)用的對象必須實現(xiàn)遠程接口。但遠程
接口本身并不包含任何方法。因而需要創(chuàng)建一個新的接口來擴展遠程接口。
新接口將包含所有可以遠程調(diào)用的方法。遠程對象必須實現(xiàn)這個新接口,由于新的接口擴展了
遠程接口,實現(xiàn)了新接口,就滿足了遠程對象對實現(xiàn)遠程接口的要求,所實現(xiàn)的每個對象都將
作為遠程對象引用。
個人總結(jié):
RMI說白了,就是提供了一種遠程的方法調(diào)用。 這種調(diào)用簡單方便,可以傳遞復(fù)雜java對象。現(xiàn)在流行的j2ee中的EJB的底層實現(xiàn)技術(shù)就是RMI,EJB的調(diào)用就是經(jīng)過封裝的,更高級的RMI調(diào)用。
下面我們就來寫一個RMI的程序:
一.創(chuàng)建RMI程序的6個步驟:
1、定義一個遠程接口的接口,該接口中的每一個方法必須聲明它將產(chǎn)生一個RemoteException異常。
2、定義一個實現(xiàn)該接口的類。
3、使用RMIC程序生成遠程實現(xiàn)所需的殘根和框架。
4、創(chuàng)建一個服務(wù)器,用于發(fā)布2中寫好的類。
5. 創(chuàng)建一個客戶程序進行RMI調(diào)用。
6、啟動rmiRegistry并運行自己的遠程服務(wù)器和客戶程序。
二. 程序詳細說明
1.定義一個遠程接口的接口,該接口中的每一個方法必須聲明它將產(chǎn)生一個RemoteException異常。
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface I_Hello extends java.rmi.Remote //需要從Remote繼承
{
public String SayHello() throws RemoteException; //需要拋出remote異常
}
上面例子我們定義一個返回字符串的遠程方法 SayHello(),這個遠程接口 I_Hello必須是public的 ,它必須從java.rmi.Remote繼承而來,接口中的每一個方法都必須拋出遠程異常java.rmi.RemoteException。
拋出這個異常的原因
由于任何遠程方法調(diào)用實際上要進行許多低級網(wǎng)絡(luò)操作,因此網(wǎng)絡(luò)錯誤可能在調(diào)用過程中隨時發(fā)生。
因此,所有的RMI操作都應(yīng)放到try-catch塊中。
2、定義一個實現(xiàn)該接口的類。
import java.io.PrintStream;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class Hello extends UnicastRemoteObject //必須從UnicastRemoteObject 繼承
implements I_Hello
{
public Hello() throws RemoteException //需要一個拋出Remote異常的默認初始化方法
{
}
public String SayHello() //這個是實現(xiàn)I_Hello接口的方法
{
return "Hello world !!";
}
}
實現(xiàn)接口的類必須繼承UnicastRemoteObject類。
擴展java.rmi.server.UnicastRemoteObject
UnicastRemoteObject顧名思義,是讓客戶機與服務(wù)器對象實例建立一對一的連接。
3、使用RMIC程序生成遠程實現(xiàn)所需的殘根Stub 和 框架。
2中的Hello 編譯好以后,我們就可以用RMIC命令來生成殘根Stub
在Dos窗口里,到Hello.class 所在目錄,運行以下命令:
rmic Hello
命令執(zhí)行完以后,將會在當(dāng)前目錄生成一個 Hello_Stub.class 這個就是我們遠程調(diào)用時需要的類
參考:
在RMI中,客戶機上生成的調(diào)動調(diào)用參數(shù)和反調(diào)動返回值的代碼稱為殘根。有的書上稱這部分代碼為“主干”。
服務(wù)器上生成的反調(diào)動調(diào)用參數(shù)和進行實際方法調(diào)用調(diào)動返回值的代碼稱為框架。
生成殘根和框架的工具
Rmic命令行工具(RMI Compiler)
格式:
Rmic classname
4、創(chuàng)建一個服務(wù)器,用于發(fā)布2中寫好的類。
import java.rmi.*;
public class RMI_Server
{
public static void main(String[] args)
{
try
{
Hello hello = new Hello(); //實例化要發(fā)布的類
Naming.rebind("RMI_Hello", hello); //綁定RMI名稱 進行發(fā)布
System.out.println("=== Hello server Ready === ");
}
catch(Exception exception)
{
exception.printStackTrace();
}
}
}
5. 創(chuàng)建一個客戶程序進行RMI調(diào)用。
import java.rmi.*;
public class RMI_Client {
public static void main(String[] args) {
try
{
I_Hello hello = (I_Hello) Naming.lookup("RMI_Hello"); //通過RMI名稱查找遠程對象
System.out.println(hello.SayHello()); //調(diào)用遠程對象的方法
} catch (Exception e)
{
e.printStackTrace();
}
}
}
Naming.lookup("RMI_Hello") 其中的參數(shù)“RMI_Hello”只是針對本機的RMI查找,如果是異地的RMI調(diào)用請參照 rmi://127.0.0.1:1099/RMI_Hello 端口1099是默認的RMI端口,如果你啟動 rmiregistry 的時候(見第6點)沒有指定特殊的端口號,默認就是1099
到此 我們 所有的代碼編寫都完成了,不過不要急著去運行,請跟隨第6點去運行,因為rmi 調(diào)用還會遇到一些特別的情況,偶花了牛勁,才找到原因的,許多剛用RMI的人,常常被這些問題搞得吐血
6、啟動rmiRegistry并運行自己的遠程服務(wù)器和客戶程序。
1)服務(wù)器的運行
先在DOS下運行 rmiregistry 這個命令是開啟RMI的注冊服務(wù),開啟以后我們的server程序才能調(diào)用rebing方法發(fā)布我們的類
然后,運行我們的server程序 RMI_Server 這里是最容易出錯的,參見下面注意事項。
注意:
如果提示找不到Stub類,這個需要用下面的命令來運行
java.exe -Djava.rmi.server.codebase=file:/E:\MIS_Interface\momo\TestEasy\classes/ RMI_Server
藍字部分指定了stub類的路徑。
有人會問,我已經(jīng)把stub 通過-classpath 加到類路徑里面了,為什么還沒有提示這個錯誤呢?原因是這樣的: 這里提示的找不到stub類,不是由你寫的RMI_Server這個程序引起的,是由rmi注冊服務(wù)器報告的異常,也就是我們前面啟動的 rmiregistry ,因為你寫的RMI_Server 要求RMI注冊服務(wù)器注冊一個新的類,自然RMI服務(wù)器必須知道你的類放在哪里,所以我們通過 -Djava.rmi.server.codebase 這個運行參數(shù)來指定
你也可以通過修改操作系統(tǒng)的classpath 環(huán)境變量 來指定stub的位置,只不過太麻煩
2) 客戶端的運行
直接運行RMI_Client 即可 注意 把 Stub 和 接口 I_Hello 加到類路徑里
通常第一次運行 客戶端都會報一個錯誤: Access XXXX 不記得具體的了,反正就是“訪問權(quán)限限制”, 這是因為RMI的服務(wù)需要授權(quán),外部程序才能訪問,所以我們要改動 jre的安全配置文件,來開放權(quán)限, 具體如下:
打開你的jdk目錄下的這個文件 C:\Program Files\Java\jdk1.5.0_04\jre\lib\security\java.policy
在文件最后加入下面代碼:
grant {
permission java.net.SocketPermission "*:1024-65535",
"connect,accept";
permission java.net.SocketPermission "*:80","connect";
};
此代碼,開放了端口的connect訪問權(quán)限
注意 你應(yīng)該修改服務(wù)器那臺機子的安全配置文件,也就是你運行 rmiregistry 和 RMI_Server的機子
另外,很多人修改完以后,仍然報這個錯誤,多數(shù)情況是由于你沒有修改到正確的jdk 下的文件,而是修改到其他jdk的文件, 我們安裝oracle , Weblogic等等軟件的時候都會自帶一個 jdk,他們會自動在操作系統(tǒng)的環(huán)境變量里面 加入jdk的路徑,所以,你先要確定你運行服務(wù)器端程序是用哪個jdk,再修改這個jdk下的配置文件,確定當(dāng)前jdk的路徑很簡單 開始 -》運行-》rmiregistry 看看這個DOS窗口標(biāo)題 的路徑,就是你當(dāng)前系統(tǒng)默認jdk的路徑了
客戶端正常運行以后,就會出現(xiàn)以下結(jié)果:
Hello world !!
這些字符是通過RMI調(diào)用遠程服務(wù)器的類返回的結(jié)果
RMI 實例
Calculator.java

import java.rmi.Remote;

public interface Calculator extends Remote

...{

public long add(long a, long b) throws java.rmi.RemoteException ;
public long sub(long a, long b) throws java.rmi.RemoteException ;
public long mul(long a, long b) throws java.rmi.RemoteException ;
public long div(long a, long b) throws java.rmi.RemoteException ;
}

CalculatorClient.java:

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;


public class CalculatorClient ...{


public static void main(String[] args) ...{

try ...{
Calculator c = (Calculator)
Naming.lookup(
"rmi://localhost/CalculatorService");
System.out.println( c.sub(4, 3) );
System.out.println( c.add(4, 5) );
System.out.println( c.mul(3, 6) );
System.out.println( c.div(9, 3) );
}

catch (MalformedURLException murle) ...{
System.out.println();
System.out.println(
"MalformedURLException");
System.out.println(murle);
}

catch (RemoteException re) ...{
System.out.println();
System.out.println(
"RemoteException");
System.out.println(re);
}

catch (NotBoundException nbe) ...{
System.out.println();
System.out.println(
"NotBoundException");
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException

ae) ...{
System.out.println();
System.out.println(
"java.lang.ArithmeticException");
System.out.println(ae);
}
}
}


CalculatorImpl.java:

import java.rmi.server.UnicastRemoteObject;

public class CalculatorImpl extends UnicastRemoteObject implements Calculator

...{

// 這個實現(xiàn)必須有一個顯式的構(gòu)造函數(shù),并且要拋出一個RemoteException異常
public CalculatorImpl()

throws java.rmi.RemoteException ...{
super();
}

public long add(long a, long b)

throws java.rmi.RemoteException ...{
return a + b;
}

public long sub(long a, long b)

throws java.rmi.RemoteException ...{
return a - b;
}

public long mul(long a, long b)

throws java.rmi.RemoteException ...{
return a * b;
}

public long div(long a, long b)

throws java.rmi.RemoteException ...{
return a / b;
}
}


CalculatorRemoteClient.java:


import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;


public class CalculatorRemoteClient ...{


public static void main(String[] args) ...{

try ...{
Calculator c = (Calculator)
Naming.lookup(
"rmi://10.10.11.209/CalculatorService");
System.out.println( c.sub(4, 3) );
System.out.println( c.add(4, 5) );
System.out.println( c.mul(3, 6) );
System.out.println( c.div(9, 3) );
}

catch (MalformedURLException murle) ...{
System.out.println();
System.out.println(
"MalformedURLException");
System.out.println(murle);
}

catch (RemoteException re) ...{
System.out.println();
System.out.println(
"RemoteException");
System.out.println(re);
}

catch (NotBoundException nbe) ...{
System.out.println();
System.out.println(
"NotBoundException");
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException

ae) ...{
System.out.println();
System.out.println(
"java.lang.ArithmeticException");
System.out.println(ae);
}
}
}


CalculatorServer.java:

import java.rmi.Naming;


public class CalculatorServer ...{


public CalculatorServer() ...{

try ...{
Calculator c = new CalculatorImpl();
Naming.rebind("rmi://localhost:1099/CalculatorService", c);

} catch (Exception e) ...{
System.out.println("Trouble: " + e);
}
}


public static void main(String args[]) ...{
new CalculatorServer();
}
}


第二個
服務(wù)端代碼

Multi.java
import java.rmi.*;


public interface Multi extends Remote ...{
int multi(int a, int b) throws RemoteException;
}


Plus.java
import java.rmi.*;


public interface Plus extends Remote...{
int plus(int a, int b) throws RemoteException;
}

MultiImpl.java
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;


public class MultiImpl extends UnicastRemoteObject implements Multi...{
private String name;

public MultiImpl(String s) throws Exception...{
super();
name = s;
}

public int multi(int a, int b) throws RemoteException ...{
System.out.println("Invoke the multiply");
return (a * b);
}
}

PlusImpl.java
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;


public class PlusImpl extends UnicastRemoteObject implements Plus ...{
private String name;

public PlusImpl (String s) throws RemoteException ...{
super();
name = s;
}

public int plus(int a, int b) throws RemoteException ...{
System.out.println("Invoke the add");
return (a + b);
}
}

CMSServer.java
import java.rmi.*;


public class CMSServer ...{

public CMSServer()...{
super();
}

public static void main (String[] args) ...{
//SYstem.setSecurityManager(new RMISecurityManager());

try...{
new CMSServer();
MultiImpl mI = new MultiImpl("MultiServer");
PlusImpl pI = new PlusImpl("PlusServer");
Naming.rebind("pluscxx",pI);
Naming.rebind("multicxx",mI);
System.out.println("pluscxx and multicxx bound in registry");

} catch(Exception e) ...{
System.out.println("pluscxx and multicxx err: " + e.getMessage());
}
}
}

客戶端代碼

Multi.java
import java.rmi.*;


public interface Multi extends Remote ...{
int multi(int a, int b) throws RemoteException;
}

Plus.java
import java.rmi.*;


public interface Plus extends Remote...{
int plus(int a, int b) throws RemoteException;
}

Frame1.java
import java.awt.*;
import java.rmi.*;
import java.awt.event.*;
import javax.swing.*;



public class Frame1 extends JFrame ...{
Container container;
private JTextField jTextFields[];
private JLabel jLabels[];
private JButton jButton;

private String[] labelsName = ...{" Num1", " Num2", " Num3" ," plus then multiply" };

public Frame1() ...{
super("sigle client multi server");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//enableEvents(AWTEvent.WINDOW_EVENT_MASK);
container = getContentPane();
container.setLayout(new GridLayout(5,2,10,10));
jLabels = new JLabel[4];
jTextFields = new JTextField[4];
int i = 0;

for(i = 0; i < 4; i++) ...{
jLabels[i] = new JLabel(labelsName[i]);
jTextFields[i] = new JTextField("jTextField" + i);
//jTextFields[i].addActionListener(new Handler());
container.add(jLabels[i]);
container.add(jTextFields[i]);
}
jButton = new JButton("Compute");
container.add(jButton);
jButton.addActionListener(new Handler());
this.setSize(new Dimension(400, 300));
this.setVisible(true);
}

private class Handler implements ActionListener...{

public void actionPerformed(ActionEvent ae) ...{

if(ae.getActionCommand().equals("Compute")) ...{
int[] num = new int [3];
int i = 0;

for(i = 0; i < 3; i++) ...{
num[i] = Integer.parseInt(jTextFields[i].getText());
}

try...{
Plus p = (Plus)Naming.lookup("pluscxx");
System.out.println("Found plus object");
int sum = p.plus(num[0], num[1]);
System.out.println("Add the first two number");
Multi m = (Multi)Naming.lookup("multicxx");
System.out.println("Found multi object");
int multiply2 = m.multi(sum, num [2]);
System.out.println("Multi with the last number");
System.out.println("result is:" + multiply2);
jTextFields[3].setText("" + multiply2);
//container.validate();
System.out.println("OK");

} catch(Exception ex) ...{
System.out.println(ex.getMessage());
}
}
}
}

public static void main(String[] args) ...{
new Frame1();
}
}
引用地址:http://blog.csdn.net/tgeh23/archive/2008/03/14/2183746.aspx
posted on 2008-04-03 13:32
SIMONE 閱讀(1183)
評論(2) 編輯 收藏 所屬分類:
rmi