<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    京山游俠

    專注技術,拒絕扯淡
    posts - 50, comments - 868, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    對RMI的簡單理解

    Posted on 2006-09-15 22:45 京山游俠 閱讀(2295) 評論(3)  編輯  收藏 所屬分類: J2EE學習及探索

    RMI 的簡單理解

    ?

    RMI (遠程方法)是 Java 平臺中建立分布式計算的基礎, 2 年前我剛開始接觸 J2EE 時,怎么看書都是不得要領,最近這幾天閑著沒事又翻了翻以前沒有看懂的書,突然之間頓悟了。

    ?

    一、 簡單的 RMI 示例:

    要快速入門,最簡單的方法就是看簡單的例子。下面是我寫的一個簡單的示例:

    首先,定義一個接口 IServer ,代碼如下:

    IServer.java

    ?

    1 package ?rmistudy;
    2
    3 import ?java.rmi.Remote;
    4
    5 public ? interface ?IServer? extends ?Remote? {
    6 ??????? public ? void ?doSomeThing()? throws ?java.rmi.RemoteException;
    7 }

    8
    9


    需要注意的是,這個接口從java.rmi.Remote接口擴展,并且這個接口中定義的方法都需要拋出java.rmi.RemoteException異常。

    ?

    接著,我們要根據這個接口來實現自己的服務器對象,所謂服務器對象,就是我們大腦中想的遠程對象,這個對象中定義的方法都是被別人來調用的。代碼如下:

    ServerImp.java

    ?

    package ?rmistudy;

    import ?java.rmi. * ;
    import ?java.rmi.server. * ;

    public ? class ?ServerImp? extends ?UnicastRemoteObject? implements ?IServer? {

    ???????
    public ?ServerImp()? throws ?RemoteException? {
    ??????????????
    super ();
    ???????}


    ???????
    public ? void ?doSomeThing()? throws ?RemoteException? {
    ??????????????System.out.println(
    " 不帶參數的遠程函數doSomeThing()被調用,該信息顯示在服務器端。 " );
    ???????}


    ???????
    public ? static ? void ?main(String[]?args)? {
    ??????????????ServerImp?server?
    = ? null ;

    ??????????????
    try {
    ?????????????????????server?
    = ? new ?ServerImp();
    ??????????????}
    catch (Exception?e) {
    ?????????????????????System.out.println(
    " 創建遠程對象失敗: " );
    ?????????????????????System.out.println(e.getMessage());
    ?????????????????????System.exit(
    0 );
    ??????????????}


    ??????????????
    try {
    ?????????????????????java.rmi.Naming.rebind(
    " //localhost/MyServer " ,?server);
    ?????????????????????System.out.println(
    " 遠程對象綁定成功。 " );
    ??????????????}
    catch (Exception?e) {
    ?????????????????????System.out.println(
    " 遠程對象綁定失敗: " );
    ?????????????????????System.out.println(e.getMessage());
    ?????????????????????System.exit(
    0 );
    ??????????????}

    ???????}

    }


    ?

    這個類很容易理解, doSomeThing() 方法只簡單的輸出被調用的信息。唯一的難點就在 main() 函數中,我們通過 java.rmi.Naming.rebind() 把我們的遠程對象注冊到 rmi 注冊表中,這樣,別人就可以通過 java.rmi.Naming.lookup() 來查找我們的遠程對象。那么, rmi 注冊表在哪里呢? J2SDK bin 目錄下有一個程序 rmiregistry ,運行它就可以得到一個注冊表進程,我們可以通過它來綁定或者查找遠程對象, java.rmi.Naming.rebind 函數的第一個參數就是要指定注冊表進程的位置,因為我這里運行在自己的機器上,所以是 //localhost/ ,如果是在別的機器上,可以用 IP 地址代替。

    ?

    最后,我們寫一個客戶機,來調用這個遠程對象的方法。代碼如下:

    Client.java

    ?

    ?1 package ?rmistudy;
    ?2
    ?3 import ?java.rmi. * ;
    ?4
    ?5 public ? class ?Client? {
    ?6
    ?7 ??????? public ? static ? void ?main(String[]?args)? {
    ?8 ??????????????IServer?server? = ? null ;
    ?9
    10 ?????????????? try {
    11 ?????????????????????server? = ?(IServer)Naming.lookup( " //localhost/MyServer " );
    12 ?????????????????????System.out.println( " 查找遠程對象成功。 " );
    13 ??????????????}
    catch (Exception?e) {
    14 ?????????????????????System.out.println( " 查找遠程對象失敗: " );
    15 ?????????????????????System.out.println(e.getMessage());
    16 ?????????????????????System.exit( 0 );
    17 ??????????????}

    18
    19 ??????????????? try {
    20 ?????????????????????server.doSomeThing();
    21 ?????????????????????System.out.println( " 調用doSomeThing()成功。 " );
    22 ??????????????}
    catch (Exception?e) {
    23 ?????????????????????System.out.println( " 調用doSomeThing()失敗: " );
    24 ?????????????????????System.out.println(e.getMessage());
    25 ?????????????????????System.exit( 0 );
    26 ??????????????}

    27 ???????}

    28 }

    29
    30

    ?

    可以看到,我們的客戶端程序只用到了 IServer 接口,而不需要 ServerImp 類,它只通過 java.rmi.Naming.lookup() 來查找遠程對象的引用。

    ?

    下面,我們就可以開始測試我們的程序了。先編譯以上程序,然后:

    第一步,要先啟動 Rmi 注冊表,如下:

    1.JPG

    ?

    第二步,使用 rmic ServerImp.class 進行編譯,生成代理類 ServerImp_Stub.class ,如下:

    2.JPG
    ?

    第三步,啟動服務器端程序,如下:

    3.JPG?

    第四步,啟動客戶端程序,我們多調用幾次,如下:

    ?4.JPG

    這個時候,我們再看看服務器端是什么反應:

    5.JPG

    可以看到,服務器端的方法被調用,在服務器端的控制臺上打印出了這樣幾行消息。

    ?

    下面,我們使用一個簡單的圖表來表示客戶機、服務器和 RMI 注冊表之間的關系,綠色的數字代表順序:

    ?示意圖.JPG

    二、參數傳遞

      前面的例子沒有涉及到參數的傳遞。如果我們需要向遠程方法傳遞參數,或者要從遠程方法接受返回值,是不是有什么特殊的約定呢?不錯,如果我們要在客戶機和服務器之間傳遞參數,則該對象要么是實現Serializable接口的對象,要么是擴展自UnicastRemoteObject的對象,這兩種對象是有差別的。

      如果參數是實現Serializable接口的對象,則該對象是按值傳遞的,也就是把這整個對象傳遞到遠程方法中。請看下面的例子,我們定義了一個ISerializableWorker接口,擴展自Serializable接口,客戶端創建一個SerializableWorkerImp對象wk,并把它傳遞到服務器端,服務器端調用wk.work()方法,這個方法在服務器端執行,這就說明了我們成功把這個對象傳遞到了服務器端。服務器端返回的String對象,也可以成功傳遞到客戶端。
    ISerializableWorker.java

    1 package ?rmistudy;
    2
    3 import ?java.io.Serializable;
    4
    5 public ? interface ?ISerializableWorker? extends ?Serializable? {
    6 ???? public ? void ?work();
    7 }


    SerializableWorkerImp.java
    1package?rmistudy;
    2
    3public?class?SerializableWorkerImp?implements?ISerializableWorker?{
    4
    5????public?void?work()?{
    6????????System.out.println("該信息由SerializableWorker對象輸出。");
    7????}

    8
    9}

    IServer.java
    1package?rmistudy;
    2
    3import?java.rmi.Remote;
    4import?java.rmi.RemoteException;
    5
    6public?interface?IServer?extends?Remote?{
    7????public?void?doSomeThing()?throws?RemoteException;
    8????public?String?doSomeThing(ISerializableWorker?wk)?throws?RemoteException;
    9}

    ServerImp.java
    ?1package?rmistudy;
    ?2
    ?3import?java.rmi.*;
    ?4import?java.rmi.server.*;
    ?5
    ?6public?class?ServerImp?extends?UnicastRemoteObject?implements?IServer?{
    ?7
    ?8????public?ServerImp()?throws?RemoteException?{
    ?9????????super();
    10????}

    11
    12
    13????public?void?doSomeThing()?throws?RemoteException?{
    14????????
    15????????System.out.println("不帶參數的遠程函數doSomeThing()被調用,該信息顯示在服務器端。");
    16
    17????}

    18????
    19????public?String?doSomeThing(ISerializableWorker?wk)?throws?RemoteException{
    20????????wk.work();
    21????????return?new?String("調用成功,該信息來自服務器端。");
    22????}

    23
    24????/**
    25?????*?@param?args
    26?????*/

    27????public?static?void?main(String[]?args)?{
    28????????ServerImp?server?=?null;
    29????????
    30????????try{
    31????????????server?=?new?ServerImp();
    32????????}
    catch(Exception?e){
    33????????????System.out.println("創建遠程對象失敗:");
    34????????????System.out.println(e.getMessage());
    35????????????System.exit(0);
    36????????}

    37
    38????????try{
    39????????????java.rmi.Naming.rebind("//localhost/MyServer",?server);
    40????????????System.out.println("遠程對象綁定成功。");
    41????????}
    catch(Exception?e){
    42????????????System.out.println("遠程對象綁定失敗:");
    43????????????System.out.println(e.getMessage());
    44????????????System.exit(0);
    45????????}

    46????}

    47
    48}

    Client.java
    ?1package?rmistudy;
    ?2
    ?3import?java.rmi.*;
    ?4
    ?5public?class?Client?{
    ?6
    ?7????/**
    ?8?????*?@param?args
    ?9?????*/

    10????public?static?void?main(String[]?args)?{
    11????????IServer?server?=?null;
    12????????
    13????????try{
    14????????????server?=?(IServer)Naming.lookup("//localhost/MyServer");
    15????????????System.out.println("查找遠程對象成功。");
    16????????}
    catch(Exception?e){
    17????????????System.out.println("查找遠程對象失敗:");
    18????????????System.out.println(e.getMessage());
    19????????????System.exit(0);
    20????????}

    21????????
    22????????try{
    23????????????server.doSomeThing();
    24????????????System.out.println("調用doSomeThing()成功。");
    25????????????String?str?=?server.doSomeThing(new?SerializableWorkerImp());
    26????????????System.out.println("調用帶序列化參數的doSomeThing()成功");
    27????????????System.out.println("從服務器端返回的字符串:"+str);
    28????????}
    catch(Exception?e){
    29????????????System.out.println("調用doSomeThing()失敗:");
    30????????????System.out.println(e.getMessage());
    31????????????System.exit(0);
    32????????}

    33
    34????}

    35
    36}

    37

    程序的運行方法同前,我就不再羅嗦了。這里需要注意的是,該示例在單機上運行可以,但是真的在分布環境下運行就會出錯,畢竟,別人要把一個對象傳遞到你的機器上,怎么著你也要放著別人的對象搞破壞吧。最后我們會討論安全問題。

    另外一種參數的傳遞方式,就是按照引用傳遞,如果作為參數的對象是擴展自java.rmi.server.UnicastRemoteObject類的話,那么該對象傳遞給遠程方法的只是它的引用。比如,客戶端創建了一個擴展自java.rmi.server.UnicastRemoteObject的對象A,把對象A傳遞到服務器端,這個時候服務器端得到的只是對象A的引用,如果服務器調用對象A的方法,這個方法就會在客戶端執行。

    下面的例子說明了這一點,我們定義IRefWorker接口和RefWorkerImp類,在客戶端創建RefWorkerImp類的對象,把該對象傳遞到服務器端,服務器端調用該對象的方法,你會發現該方法在客戶端執行。
    IRefWorker.java
    1package?rmistudy;
    2
    3import?java.rmi.Remote;
    4import?java.rmi.RemoteException;
    5
    6public?interface?IRefWorker?extends?Remote?{
    7????public?void?work()?throws?RemoteException;
    8}

    RefWorkerImp.java
    ?1package?rmistudy;
    ?2
    ?3import?java.rmi.RemoteException;
    ?4import?java.rmi.server.RMIClientSocketFactory;
    ?5import?java.rmi.server.RMIServerSocketFactory;
    ?6import?java.rmi.server.UnicastRemoteObject;
    ?7
    ?8public?class?RefWorkerImp?extends?UnicastRemoteObject?implements?IRefWorker?{
    ?9
    10????public?RefWorkerImp()?throws?RemoteException?{
    11????????super();
    12????}

    13
    14????public?void?work()?throws?RemoteException?{
    15????????System.out.println("該方法在服務器端調用,在客戶端執行。");
    16????}

    17
    18}

    IServer.java
    ?1package?rmistudy;
    ?2
    ?3import?java.rmi.Remote;
    ?4import?java.rmi.RemoteException;
    ?5
    ?6public?interface?IServer?extends?Remote?{
    ?7????public?void?doSomeThing()?throws?RemoteException;
    ?8????public?String?doSomeThing(ISerializableWorker?wk)?throws?RemoteException;
    ?9????public?void?doSomeThing(IRefWorker?wk)?throws?RemoteException;
    10}

    ServerImp.java
    該類中實現接口中定義的方法,和前面的代碼相比,多了如下一行
    1public?void?doSomeThing(IRefWorker?wk)?throws?RemoteException{
    2????????wk.work();
    3????}

    Client.java
    ?1package?rmistudy;
    ?2
    ?3import?java.rmi.*;
    ?4
    ?5public?class?Client?{
    ?6
    ?7????/**
    ?8?????*?@param?args
    ?9?????*/

    10????public?static?void?main(String[]?args)?{
    11????????IServer?server?=?null;
    12????????
    13????????try{
    14????????????server?=?(IServer)Naming.lookup("//localhost/MyServer");
    15????????????System.out.println("查找遠程對象成功。");
    16????????}
    catch(Exception?e){
    17????????????System.out.println("查找遠程對象失敗:");
    18????????????System.out.println(e.getMessage());
    19????????????System.exit(0);
    20????????}

    21????????
    22????????try{
    23????????????server.doSomeThing();
    24????????????System.out.println("調用doSomeThing()成功。");
    25????????????String?str?=?server.doSomeThing(new?SerializableWorkerImp());
    26????????????System.out.println("調用帶序列化參數的doSomeThing()成功");
    27????????????System.out.println("從服務器端返回的字符串:"+str);
    28????????????server.doSomeThing(new?RefWorkerImp());
    29????????????System.out.println("調用帶引用參數的doSomeThing()成功");
    30????????}
    catch(Exception?e){
    31????????????System.out.println("調用doSomeThing()失敗:");
    32????????????System.out.println(e.getMessage());
    33????????????System.exit(0);
    34????????}

    35
    36????}

    37
    38}

    程序的運行方法同前,不再重復。

    三、安全管理與授權策略
      前面提到過,前面的示例代碼,如果真正運行到分布式環境下的話,是會出錯的,原因就在于安全性問題。J2EE中的安全管理廣泛,我們這里僅僅只用到授權,比如我們可以只授權遠程程序訪問某一個文件夾或某一個文件,或者只授權遠程程序訪問網絡等等。

      要使用授權,需要一個授權文件,我們新建一個Policy.txt文件,為了簡單起見,我們授權遠程程序可以訪問所有的本地資源:
    1grant{
    2??permission?java.security.AllPermission?"","";
    3}
    ;

      然后,我們需要在服務器端程序中載入安全管理器,我們這里使用默認的RMISecurityManager,下面是經過修改了的ServerImp.java中的mian()函數:

    ?1public?static?void?main(String[]?args)?{
    ?2????????ServerImp?server?=?null;
    ?3????????
    ?4????????try{
    ?5????????????System.setSecurityManager(new?RMISecurityManager());
    ?6????????}
    catch(Exception?e){
    ?7????????????System.out.println("加載安全管理器失敗:");
    ?8????????????System.out.println(e.getMessage());
    ?9????????????System.exit(0);
    10????????}

    11????????
    12????????try{
    13????????????server?=?new?ServerImp();
    14????????}
    catch(Exception?e){
    15????????????System.out.println("創建遠程對象失敗:");
    16????????????System.out.println(e.getMessage());
    17????????????System.exit(0);
    18????????}

    19
    20????????try{
    21????????????java.rmi.Naming.rebind("//localhost/MyServer",?server);
    22????????????System.out.println("遠程對象綁定成功。");
    23????????}
    catch(Exception?e){
    24????????????System.out.println("遠程對象綁定失敗:");
    25????????????System.out.println(e.getMessage());
    26????????????System.exit(0);
    27????????}

    28????}

    然后,我們需要這樣運行服務器端:
    java -Djava.security.policy=Policy.txt rmistudy.ServerImp

    給幾個貼圖:
    1.運行服務器:
    6.JPG

    2.運行客戶端:
    7.JPG

    3.運行客戶端后服務器的反應:
    8.JPG

    總結
      J2EE規范雖然龐大而復雜,但是如果我們分開來學習,也是可以逐步理解的。J2EE包含企業數據、企業通訊、企業服務、企業Web支持和企業應用程序等方面。而我們的RMI就是企業通訊中的一種,另外一種就是日益流行起來的Web Service通訊,至于其它通訊架構,我們大可以到需要的時候再去學習,比如CORBA。

      EJB是架構在RMI基礎上的,但是它太復雜,很多時候使用簡單的RMI就可以解決很多問題,比如科學領域的分布式計算。大家一定聽說過找外星人的SETI項目,它就是利用全球志愿者的個人PC來進行分布式的運算。在我們中國,大型機還比較缺乏,如果我們的某個實驗室需要強大的計算能力,大可以向SETI項目學習。而使用RMI,建立分布式計算平臺是多么的簡單。

    評論

    # re: 對RMI的簡單理解  回復  更多評論   

    2007-01-25 12:41 by anders0913
    請問要是在Tomcat中直接使用RMI有什么好的方法可以解決安全問題。不想更改Tomcat的安全策略文件?

    # re: 對RMI的簡單理解  回復  更多評論   

    2007-07-30 16:50 by 小白之家
    恩,介紹算詳細吧,可是blog主最好是給些代碼

    # re: 對RMI的簡單理解  回復  更多評論   

    2009-04-18 23:42 by 勉勉強強
    謝謝LZ清晰地講解,代碼和運行結果,理論和注意事項都很完備,多謝~!
    主站蜘蛛池模板: 久久黄色免费网站| 美女一级毛片免费观看| 亚洲第一网站免费视频| 午夜亚洲国产理论秋霞| 亚洲AV午夜福利精品一区二区| 国产亚洲精品a在线无码| 亚洲第一AAAAA片| 亚洲AV天天做在线观看| 亚洲综合久久综合激情久久| 亚洲色图黄色小说| 色噜噜亚洲男人的天堂| 亚洲第一街区偷拍街拍| 黄色网页免费观看| 中文字幕在线免费播放| 视频免费在线观看| 最近免费中文字幕mv电影| 亚洲国产精品免费观看| 在线观看免费a∨网站| 波多野结衣中文一区二区免费| 亚洲VA综合VA国产产VA中| 久久久亚洲精品蜜桃臀| 精品亚洲综合久久中文字幕| 亚洲黄色免费电影| 亚洲日韩一区精品射精| 黄网站在线播放视频免费观看| 中文字幕无线码中文字幕免费| 久久一本岛在免费线观看2020| 99久久精品日本一区二区免费 | 欧洲美女大片免费播放器视频| 国产精品免费一区二区三区| 青青青国产手机频在线免费观看| 免费可以看黄的视频s色| 国产精品免费电影| 久久久无码精品亚洲日韩软件 | 啦啦啦手机完整免费高清观看| 国产v片免费播放| 亚洲AV无码专区国产乱码4SE| 亚洲人成网站在线观看播放青青| 亚洲AV成人一区二区三区观看| 国产精品美女免费视频观看| 97在线视频免费播放|