RMI分布式调用

14.4.1 RMI

介绍

 

RMI

Remote 

Method 

Invocation

(远程方法调用)的

 

所写。它允许一个

Java

程序调

用网络中另一台计算机上的

Java

方法,就如调用本机的方法一样。实现

RMI

调用的程序和

被调用的方法,都必须是

Java

代码,即客户端和服务器端都必须通过纯

Java

实现。

 

RMI

是基于

Java

的分布式编程模型,

使用

RMI

进行远程方法调用时,

无须考虑方法底

层的网络传输细节。

 

 

 

要使用

RMI

,必须构建四个主要的类:远程对象的本地接口、远程对象实现、

RMI

客户机和

RMI

服务器。

RMI

服务器生成远程对象实现的一个实例,并用一个专有的

URL

注册。

RMI

客户机在远程

RMI

服务器上查找服务对象,并将它转换成本地接口类型,然后

像对待一个本地对象一样使用它。

 

下面使用

RMI

的示例程序:

 

 

1

、先编写

RMI

服务器端(接口)

 

RMI

需要通过远程接口提供服务。也就是说,所有想被客户机调用的方法都必须在

Remote

接口里声明,否则无法完成调用。远程接口如下:

 

 

package

 cn.ac.ict.rmi; 

 

import

 java.rmi.Remote; 

import

 java.rmi.RemoteException; 

 

public

 

interface

 Server 

extends

 Remote  

    

//

所有在

Remote

接口里声明的方法都必须抛出

RemoteException

异常

 

 

    String helloWorld(String name) 

throws

 RemoteException; 

    Person getPerson(String name,

int

 age)

throws

 RemoteException; 

}

 

注意:

 

1

 

远程接口必须继承

java.rmi.Remote

接口。

 

2

 

远程接口里声明的方法会通过网络传输,而网络是不可靠的,因此,所有的远程方

法都必须抛出

RemoteException

 

3

 

RMI

服务是典型的面向接口编程,

只有在远程接口里定义的方法才会作为远程服务。

 

 

2

、接口实现类

 

 

远程服务提供类必须实现远程接口,并继承

java.rmi.server.UnicastRemoteObject

对象,继承

该类能提供远程服务。

 

package

 cn.ac.ict.rmi; 

 

import

 java.rmi.RemoteException; 

import

 java.rmi.server.UnicastRemoteObject; 

 

//

远程服务类,远程服务类必须继承

UnicastRemoteObject

,并实现

Remote

接口

 

 

public

 

class

 

ServerImpl 

extends

 

UnicastRemoteObject 

implements

 

Server 

{

 

 

 

//

远程服务类必须拥有构造器,且构造器必须抛出

RemoteException

异常

 

 

public

 ServerImpl()

throws

 RemoteException { 

 

 

super

(); 

 

 

//

实现

Remote

接口必须实现的方法

 

    

public

 String helloWorld(String name)

throws

 RemoteException { 

 

          

return

 name + 

", 

您好

!"

    } 

    

//

实现

Remote

接口必须实现的方法

 

    

public

 Person getPerson(String name,

int

 age)

throws

 RemoteException 

           

return

 

new

 Person(name,age); 

 

    } 

    

//

下面是服务类的本地方法,不会提供为远程服务。

 

    

public

 

void

 info() { 

     

S

ystem.

out

.println(

"I am a Local Method "

); 

 

3

RMI

服务器类

 

package

 cn.ac.ict.rmi; 

 

import

 java.rmi.Naming; 

import

 java.rmi.registry.LocateRegistry; 

 

public

 

class

 HelloServer { 

 

 

 

public

 

static

 

void

 main(String[] args) { 

 

 

try

 

 

 

System.

out

.println(

"Start RMI servering..."

); 

     

 

//

创建远程服务类实例

 

            Server imp = 

new

 ServerImpl(); 

            

//

注册远程服务的端口

 

            LocateRegistry.

createRegistry

(1099); 

            

//

将远程服务实例绑定为远程服务。

 

            Naming.

rebind

(

"rmi://168.160.194.155:1099/aa"

, imp); 

            System.

out

.println(

"Waiting client invoke..."

); 

     

}

catch

(Exception e){ 

     

 

e.printStackTrace(); 

     

}

 

 

 

}

 

将两个编辑好的源文件存盘,然后编译。

 

 

注册远程服务端口:

 

LocateRegistry.

createRegistry

(1099); 

1099

RMI

服务的默认端口。然后执行如下代码绑定远程服务

 

Naming.

rebind

(

"rmi://168.160.194.155:1099/aa"

, imp); 

 

注意:

1

、如果一个类继承自

UnicastRemoteObject

,那么它必须提供一个构造函数并且声明

抛出一个

RemoteException

对象,即使找个构造器什么都不做。当这个构造函数调用了

super()

它就激活

UnicastRemoteObject

中的代码完成

RMI

的连接和远程对象的初始化。

 

2

、这种在程序中直接注册的方式(不使用

rmiregistry.exe

工具)

,不必再使用

rmic

令编译服务类生成

stub

类:在类的根目录下运行

rmic

命令,且类包含完整的包名:

 

bin> rmic cn.ac.ict.rmi.ServerImpl 

 

4

、客户端

 

 

客户端程序面向接口编程,客户端部分需要

Server

接口的

class

文件,还需要

stub

文件。客

户端的源代码如下:

 

package

 cn.ac.ict.rmi; 

 

import

 

 java.rmi.Naming; 

import

 java.rmi.RMISecurityManager; 

 

public

 

class

 RMIClient { 

    

public

 

static

 

void

 main(String[] args)

throws

 Exception { 

     

 

//     

//

安全管理器类

RMISecurityManager.

这是因为

RMI

需要传递对象并进行动态类

装入,所以必须加载安全管理器以防止别人发送伪类

 

//        

if(System.getSecurityManager() == null) {      

 

 

//            

 

//             

System.setSecurityManager(new RMISecurityManager());

 

//            }

 

        

try

     

/

/

通过

JNDI

查找远程服务

 

     

S

erver ser = (Server)Naming.

lookup

(

"rmi:// 

168.160.194.155:1099/aa"

); 

     

/

/

调用远程方法

 

     

S

ystem.

out

.println(ser.helloWorld(

"cuixingchen"

)); 

     

/

/

调用远程方法。

 

     

S

ystem.

out

.println(ser.getPerson(

"cuixingchen"

,28)); 

        }

catch

(Exception e){ 

         

e.printStackTrace(); 

        } 

    } 

 

 

 

上面注释了安全管理的代码,如果把注释去掉,那么需要建立一个安全策略文件。

 

从客户端程序看,无法感受到

Server

的实现在远端。程序调用

Server

实例方法时,与平常

调用方法只有非常细微的区别:调用远程方法必须抛出

RemoteException

 

 

再看远程方法的返回值,

helloWorld

的返回值是

String

,而

getPerson

的返回值则是

Person

对象。

远程方法的返回值必须有一个要求:实现

Serializable

接口

。因为远程的方法的参数、

返回值都必须在网络上传输,网络只能传输字节流,因此,要求参数、返回值都可以转换成

字节流——即实现序列化。

 

 

客户端使用

JNDI

查找,查找远程服务名。将远程服务对象类型转换成远程接口类型,客户

端面向接口编程。

RMI

的具体实现,依然是依赖于底层的

Socket

编程。

RMI

依赖于

TCP/IP

传输协议,服务器端

skeleton

建立

ServerSocket

监听请求,而客户端建立

Socket

请求连接。

RMI

实现了网络传输的多线程、

IO

等底层细节。这些细节的实现就隐藏在

rmic

命令的执行

中:使用

rmic

命令编译时生成的

class

文件:

 

 

stub

:该文件用于与客户端交流,建立

Socket

请求连接。

 

skeleton

用于与服务器端交流,建立

ServerSocket

监听请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值