注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

和申的个人主页

专注于java开发,1985wanggang

 
 
 

日志

 
 

RMI及其调试(JDK1.6)  

2010-12-01 14:37:40|  分类: rmi |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一 RMI系统运行机理
    RMI应用程序通常包括两个独立的程序:服务器程序和客户机程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调 用这些远程对象的方法。而典型的客户机程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方法。RMI为服务器和客户机进行通信和信息传 递提供了一种机制。
在与远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色。调用程序将调 用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用 stub的方法时将执行下列操作:
(1) 初始化与包含远程对象的远程虚拟机的连接;
(2) 对远程虚拟机的参数进行编组(写入并传输);
(3) 等待方法调用结果;
(4) 解编(读取)返回值或返回的异常;
(5) 将值返回给调用程序。为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以 有相应的skeleton(在JDK1.2环境中无需使用skeleton)。Skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时 执行下列操作:(1) 解编(读取)远程方法的参数;(2) 调用实际远程对象实现上的方法;(3) 将结果(返回值或异常)编组(写入并传输)给调用程序。stub和skeleton由rmic编译器生成。
    利用RMI编写分布式对象应用程序需要完成以下工作:(1) 定位远程对象。应用程序可使用两种机制中的一种得到对远程对象的引用。它既可用RMI的简单命名工具rmiregistry来注册它的远程对象,也可以将 远程对象引用作为常规操作的一部分来进行传递和返回。(2)与远程对象通信。远程对象间通信的细节由RMI处理,对于程序员来说,远程通信看起来就像标准 的Java方法调用。(3)给作为参数或返回值传递的对象加载类字节码。因为RMI允许调用程序将纯Java对象传给远程对象,所以,RMI将提供必要的 机制,既可以加载对象的代码又可以传输对象的数据。在RMI分布式应用程序运行时,服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器上的 注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方法。
二 远程接口概念:
    RMI对接口有着强烈的依赖。在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细节。所以客户得到远程对象的一个句柄正好同一些本地的根代码连接,有后者负责通过网络通信。但我们并不关心这些事情,通过自己的接口句柄发送消息即可。
创建一个远程接口时,必须遵守下列规则:
1)  远程接口必须为public属性(不能有“包访问”;也就是说,他不能是“友好的”)。否则,一旦客户试图装载一个实现了远程接口的远程对象,就会得到一个错误。
2)  远程接口必须扩展接口java.rmi.Remote。
3)  除与应用程序本身有关的违例,远程接口中的每个方法都必须在自己的throws从句中声明java.rmi.RemoteException.
4)  作为参数或返回值传递的一个远程对象(不管是直接,还是本地对象中嵌入)必须声明为远程接口,不可声明为实施类。
三 远程接口的实施:
    服务器必须包含一个扩展了UnicastRemoteObject类,并实现远程接口。这个类也可以含有附加的方法,但客户只能使用远程接口中的方法。因为客户是指向接口的一个句柄,而不是它的哪个类。
必须为远程对象定义构造方法,即使只准备定义一个默认构造方法,用它调用基础类构造方法。必须把它明确地编写出来,因为它必须“掷”出RemoteException违例。
四 代码存根:
    当客户代码调用一个远程对象上的远程方法是,实际上是调用一个Java编程语言的普通方法,这个方法是封装在stub(代码存根)的代用对象。存根 (Stub)是远程对象在客户端的代理,它将RMI调用传递给服务器端的骨架(Skeleton),后者负责将该调用传递给实际的远程方法。
    要完成这个工作可使用rmic编译器,rmic编译器生成远程对象的存根和骨架。
    代码存根在服务器端创建,必须驻留于客户端。
五 RMI实战
一个正常工作的RMI系统由下面几个部分组成:
● 远程服务接口的定义
● 远程服务接口的具体实现
● 桩(Stub)和框架(Skeleton)文件
● 一个运行远程服务的服务器
● 一个RMI命名服务,它允许客户端去发现这个远程服务
● 类文件的提供者(一个HTTP或者FTP服务器)
● 一个需要这个远程服务的客户端程序
如果所有的RMI文件都已经设计好了,那么需要下面的几个步骤去生成系统:
1、  编写并且编译接口的Java代码
2、  编写并且编译接口实现的Java代码
3、  从接口实现类中生成桩(Stub)和框架(Skeleton)类文件
4、  编写远程服务的主运行程序
5、  编写RMI的客户端程序
6、  安装并且运行RMI系统
实现过程如下:(以下代码在Windows Server 2003,JDK1.6环境下调试通过,代码来自互联网)
服务器端:
1接口
第一步就是建立和编译服务接口的Java代码。这个接口定义了所有的提供远程服务的功能,下面是源程序:
Product,java
import java.rmi.*;
public interface Product extends  Remote
{
    String getDescription() throws RemoteException;
}
2接口的具体实现
下一步,我们就要写远程服务的具体实现,这是一个ProductImpl类文件:
ProductImpl.java
import java.rmi.*;
import java.rmi.server.*;
public class ProductImpl extends UnicastRemoteObject implements Product
{
    private String name ;
    public ProductImpl(String n) throws RemoteException
    {
       name = n;
    }
    public String getDescription()
    {
         return "Hello,I am " + name + " . I love you !";
        
}
3 桩(Stubs)和框架(Skeletons)
  下一步就是要使用RMI编译器rmic来生成桩和框架文件,这个编译运行在远程服务实现类文件上。
>rmic ProductImpl
在你的目录下运行上面的命令,成功执行完上面的命令你可以发现一个ProductImpl_stub.class文件,如果你是使用的是1.2以前的SDK,那么你还可以发现ProductImpl_Skel.class文件。
4 主机服务器
远程RMI服务必须是在一个服务器中运行的。
ProductServer.java
import java.rmi.*;
import java.rmi.server.*;
public class ProductServer {
public static void main(String args[])
    {
        try
        {
            System.out.println("Construction server implementats ...");
            ProductImpl p1 = new ProductImpl("Wang.yuanbin");
            ProductImpl p2 = new ProductImpl("Bueaty");
            System.out.println("binding server implementation to registry ...");
            Naming.rebind("wyb",p1);
            Naming.rebind("Beau",p2);
            System.out.println("Waiting for invocations from clients ...");
        }
        catch (Exception ex)
        {
            System.out.println("Error: " + ex );
        }
    }

}

客户端
ProductClient.java
import java.rmi.*;
import java.rmi.server.*;
public class ProductClient
{
    public static void main(String [] args)
    {
        System.setSecurityManager(new RMISecurityManager());
        String url = "rmi://localhost/";
        try
        {
            Product c1 = (Product) Naming.lookup(url + "wyb");
            Product c2 = (Product) Naming.lookup(url + "Beau");
            System.out.println(c1.getDescription());
            System.out.println(c2.getDescription());
        }
        catch (Exception ex)
        {
            System.out.println("Error : " + ex);
        }
        System.exit(0);
    }
}
在该类中使用java.rmi.Naming中的lookup()方法获得对远程对象的引用,依据需要调用该引用的远程方法,其调用方式和对本地对象方法的调用相同。.
6 安全策略文件
因为RMI的安全机制将在服务端发生作用,所以你必须增加一条安全策略。以下是对应安全策略的例子。
ProductServer.policy和ProductClient.policy
grant {
permission java.security.AllPermission "", "";
};
注意:这是一条最简单的安全策略,它允许任何人做任何事,对于你的更加关键性的应用,你必须指定更加详细安全策略。
如果没有上面的安全策略,运行客户端时,会出现如下错误:

E:\wx\2>java ProductClient
Error : java.security.AccessControlException: access denied (java.net.SocketPerm
ission 127.0.0.1:1099 connect,resolve)

7 运行RMI系统

按如下步骤编译、运行系统:

1)为接口、实现和客户、服务器类编译源文件:
Javac Product*.java
2)在实现类上运行rmic
Rmic ProductImpl
3)启动rmi注册程序
Start rmiregistry
4)  启动服务器
start java  -Djava.security.policy=ProductServer.policy ProductServer
如果不指定安全策略,服务器将无法提供服务,一运行即关闭;指定安全策略后,运行后显示:
Construction server implementats ...
binding server implementation to registry ...
Waiting for invocations from clients ...

5)  运行客户程序
Java –Djava.security.policy=ProductClient.policy ProductClient
运行后屏幕显示如下:
E:\wx\2>java  -Djava.security.policy=ProductClient.policy ProductClient
Hello,I am Wang.yuanbin . I love you !
Hello,I am Bueaty . I love you !

E:\wx\2>

8 错误分析:(以下代码同样来自互联网,但未调试通过)
  在我测试RMI的过程中,曾经出现如下错误,但未找出原因:
   E:\wx>java -Djava.security.policy=RmiHelloClient.policy RmiHelloClient
java.rmi.ConnectException: Connection refused to host: localhost; nested excepti
on is:
        java.net.ConnectException: Connection refused: connect
        at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
        at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
        at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
        at sun.rmi.server.UnicastRef.newCall(Unknown Source)
        at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
        at java.rmi.Naming.lookup(Unknown Source)
        at RmiHelloClient.main(RmiHelloClient.java:19)
Caused by: java.net.ConnectException: Connection refused: connect
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(Unknown Source)
        at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
        at java.net.PlainSocketImpl.connect(Unknown Source)
        at java.net.SocksSocketImpl.connect(Unknown Source)
        at java.net.Socket.connect(Unknown Source)
        at java.net.Socket.connect(Unknown Source)
        at java.net.Socket.<init>(Unknown Source)
        at java.net.Socket.<init>(Unknown Source)
        at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(Unknown S
ource)
        at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(Unknown S
ource)
        ... 7 more
出错的代码如下:
   远程接口
   import java.rmi.*;

    public interface RmiHelloRemoteIntfc extends Remote
    {
      String helloRemoteObj(String client) throws RemoteException;
    }
    远程接口实现
    import java.rmi.server.*;
    import java.rmi.*;
    public class RmiHelloRemoteObj extends UnicastRemoteObject implements RmiHelloRemoteIntfc
    {
      public RmiHelloRemoteObj() throws RemoteException
    {
      super();
     }
     public String helloRemoteObj(String client) throws RemoteException
     {
       return "Hello World"+client;
     }
     }
     服务器
     import java.io.*;
     import java.rmi.*;
     import java.rmi.server.*;
     import sun.applet.*;
     import java.rmi.registry.LocateRegistry;

     public class RmiHelloServer
     {

      public RmiHelloServer()
      {
      }
      public static void main(String[] args)
      {
       //创建并安装安全管理器
       if(System.getSecurityManager()==null)
       {
         System.setSecurityManager(new RMISecurityManager());
       }

       try{
         //创建远程对象
         RmiHelloRemoteObj ttt=new RmiHelloRemoteObj();
         //启动注册表
         LocateRegistry.createRegistry(4588);
         //奖名称绑定到对象
         //System.setProperty("java.rmi.server.localhost","211.81.207.109");
         Naming.rebind("//localhost/wx",ttt);

         System.out.println("RMI服务器正在运行。。。。。。");
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      }
      }
      客户端
      import java.rmi.*;
      import java.rmi.server.*;

      public class RmiHelloClient
      {

       public RmiHelloClient()
      {
      }
      public static void main(String[] args)
      {
      //创建并安装安全管理器
      if(System.getSecurityManager()==null)
     {
       System.setSecurityManager(new RMISecurityManager());
     }

      try{
           RmiHelloRemoteIntfc c1=(RmiHelloRemoteIntfc)Naming.lookup("rmi://localhost/wx");
           System.out.println(c1.helloRemoteObj("Everyone"));
      }
      catch(Exception e)
      {
         e.printStackTrace();
       }
      System.exit(0);
       }
      }
      策略文件
     
       grant codeBase
       "file:/e:/wx/"
       {
        //  permission java.net.SocketPermission
         // "*:1000-65535","accept,connect,listen,resolve";
         permission java.security.AllPermission "", "";
       };

  评论这张
 
阅读(2226)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016