隨筆-11  評論-10  文章-8  trackbacks-0
          rmi有兩個主要問題:
          1. 調用如何從客戶端傳輸到服務器端
             這個問題的是通過stub來解決的,stub負責和服務器通信,將調用傳輸到服務器并接收
             返回值
          2. 由于stub類可以通過工具生成,但初始化必須在服務器端完成,所有如何將一個可用的
             stub傳輸到客戶端就是我們最關心的問題

          注:在1.4中stub類是必須的,在5.0中使用UnicastRemoteObject類可以不需要stub類,
          而由動態生成的Proxy類(實現遠程接口,InvocationHandler是
          RemoteObjectInvocationHandler)代替,具體見5.0的文檔。如果不使用
          UnicastRemoteObject類,則stub類在服務器端是必須的

          rmi的核心問題是如何將一個可用的stub傳輸到客戶端,大致有兩種方法:
          1. 最直觀的方法,服務器和客戶端直接建立連接,用任何可用的協議傳輸stub,缺點顯而
             易見,客戶端必須明確的知道服務器的地址及相關信息
          2. 使用注冊中心,如:rmiregistry和JNDI。這里主要討論使用rmiregistry的情況

          有一點需要注意,這里傳輸的只是stub的實例,而不是stub類的定義。所有stub類文件必須
          在客戶端和服務器端的classpath中,否則會拋出ClassNotFoundException。這樣要求客戶
          端確實有些過分,我們希望客戶端只要遠程接口的定義就夠了,這實際上很容易辦到,下面
          我們就來討論這個問題

          Dynamic code downloading using RMI
          (http://java.sun.com/j2se/1.4.2/docs/guide/rmi/codebase.html)

          要下載類必須設置RMISecurityManager,默認是禁止下載的

          客戶端可以通過網絡自動下載stub,此時stub不需要在客戶端的classpath中,只需要遠程
          接口即可。

          由rmiregistry作為注冊中心的遠程調用

          在服務器綁定遠程對象到rmiregistry時,rmiregistry必須能夠找到該對象的stub,它在三
          個地方尋找:
          1. 啟動它時的CLASSPATH環境變量

          2. 啟動它時所在的文件夾作為classpath尋找

          3. 該stub中的codebase,這個值由服務器通過系統屬性 java.rmi.server.codebase 設置

          若找不到則無法綁定

          客戶端通過rmiregistry lookup到stub對象,虛擬機從兩個位置尋找stub類的定義:
          1. classpath中尋找

          2. stub中的codebase,這個值由rmi服務器通過系統屬性 java.rmi.server.codebase 設置

          由以上分析可見,服務器端只要正確設置了codebase,無論何種情況,遠程調用都能順利完成

          不僅客戶端可以從服務器下載類,服務器也可以從客戶端下載類,只需要在客戶端設置
          codebase,安裝RMISecurityManager即可

          若服務器允許下載類,則客戶端有可能在服務器上運行任何他希望的代碼,如以下代碼:
          // 遠程接口定義
          package
          rmi.server;

          import java.rmi.Remote;
          import java.rmi.RemoteException;

          public interface Service extends Remote {
          public void hello() throws RemoteException;

          public void runThread(Runnable thread) throws RemoteException;
          }

          // 遠程接口實現,main方法作為服務器
          package
          rmi.server;

          import java.rmi.Naming;
          import java.rmi.RMISecurityManager;
          import java.rmi.RemoteException;
          import java.rmi.server.RemoteStub;
          import java.rmi.server.UnicastRemoteObject;
          import java.security.Permission;

          public class ServiceImpl implements Service {

          private RemoteStub stub;

          private int count;

          public ServiceImpl() throws RemoteException {
          stub
          = UnicastRemoteObject.exportObject(this);
          count
          = 0;
          }

          public void hello() throws RemoteException {
          System.out.println(
          "Hello World: count = " + (++count));
          }

          public void runThread(Runnable thread) throws RemoteException {
          Thread t
          = new Thread(thread);
          t.start();
          }

          public RemoteStub getStub() {
          return stub;
          }

          public static void main(String[] args) {
          System.setProperty(
          "java.rmi.server.ignoreStubClasses", "true");
          System.setProperty(
          "java.rmi.server.codebase",
          "file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiserver\\\\classes\\\\");
          if (System.getSecurityManager() == null) {
          System.setSecurityManager(
          new RMISecurityManager() {
          public void checkPermission(Permission p) {
          }
          });
          }
          try {
          ServiceImpl service
          = new ServiceImpl();
          Naming.rebind(
          "service", service);
          System.out.println(
          "Server ready");
          }
          catch (Exception e) {
          e.printStackTrace();
          }
          }

          }

          // 客戶端
          package
          rmi.client;

          import java.rmi.Naming;
          import java.rmi.RMISecurityManager;
          import java.security.Permission;

          import rmi.server.Service;

          public class ServiceClient {

          /**
          *
          @param args
          */
          public static void main(String[] args) {
          System.setProperty(
          "java.rmi.server.codebase",
          "file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiclient\\\\classes\\\\");
          if (System.getSecurityManager() == null) {
          System.setSecurityManager(
          new RMISecurityManager() {
          public void checkPermission(Permission p) {
          }
          });
          }
          try {
          Service service
          = (Service) Naming.lookup("service");
          service.hello();
          Runnable runner
          = new Runner();
          service.runThread(runner);
          }
          catch (Exception e) {
          e.printStackTrace();
          }
          }

          }
          package rmi.client;

          import java.io.Serializable;

          public class Runner implements Runnable, Serializable {

          public void run() {
          System.out.println(
          "i'm runner");
          }

          }

          服務器端生成stub并放在classpath中,codebase設置為能找到stub的路徑,運行rmiregistry,
          運行服務器。

          客戶端只需要Service類在classpath中,設置codebase為能找到Runner的路徑,運行客戶端。
          客戶端會從服務器下載stub,服務器會從客戶端下載Runner。Runner可以執行任何操作甚至破壞
          服務器上的數據。

          多次運行客戶端可以發現count在增長,也就是說對于export出的遠程對象,狀態是始終保存的
          posted on 2005-11-11 19:49 JBahamut 閱讀(1542) 評論(1)  編輯  收藏

          評論:
          # re: 關于rmi的研究 2009-03-31 16:14 | 夏登成
          謝謝你,讓我明白了遠程方法調用!  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 滦南县| 邯郸市| 五莲县| 奉节县| 康马县| 耒阳市| 布尔津县| 栖霞市| 和林格尔县| 健康| 遵义县| 宁城县| 永兴县| 海晏县| 鄱阳县| 娄底市| 达州市| 镇远县| 乃东县| 华亭县| 故城县| 额济纳旗| 迁西县| 托克托县| 平远县| 临武县| 凤台县| 尚志市| 鄂温| 彰化市| 湘潭县| 晋城| 宾川县| 平和县| 东城区| 屏南县| 潮安县| 榆林市| 鹰潭市| 安乡县| 武山县|