声明:本案例使用的IDE是Eclipse,客户端程序和服务器程序位于同一个project中。
一、基本原理
RMI是Remote Method Invoke的缩写,是JDK提供的一个完善的、简单易用的远程调用框架,它要求客户端和服务器端都是Java程序。下面简述RMI的基本原理:如下图所示,RMI采用代理来负责客户端和服务器之间socket通信的细节。RMI框架分别为远程对象生成了客户端代理和服务器端代理,位于客户端的代理称为存根(Stub),位于服务器端的代理称为骨架(Skeleton)。
![](https://i-blog.csdnimg.cn/blog_migrate/e54a7d1324bbcd908ac8faea508dcaad.jpeg)
远程对象会在客户端生成存根对象。当客户端调用远程对象的方法时,实际上是调用本地存根的相应方法。然后,存根会把被访问的远程对象名、方法名以及参数编组后发送给服务器,由骨架去调用相应的远程方法并把返回值或异常返回给客户端。
二、基本步骤
一般来说,只要继承java.rmi.server.UnicastRemoteObject类和实现java.rmi.Remote 接口就可以成为远程对象。由于java的单继承,继承了UnicastRemoteObject类之后就不能继承其他的类,这时可以在构造方法中调用exportObect()方法,同样可以将其导为远程对象。实际上,UnicastRemoteObject的构造器也会去调用自身的exportObect()的静态方法。
下面是创建一个RMI程序的基本步骤:
(1)创建远程接口,继承java.rmi.Remote接口;
(2)创建远程类,实现远程接口;
(3)创建服务器程序,在rmiregistry注册表中注册远程对象;
(4)创建客户端程序,负责定位远程对象,并且调用远程方法。
*创建远程接口
远程接口中声明了可以被客户端访问的远程方法,远程接口应符合以下条件:
(1)直接或间接继承java.rmi.Remote接口;
(2)接口中的所有方法声明抛出java.rmi.RemoteException异常或父异常。
- package main;
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- // Inherit the java.rmi.Remote interface
- public interface HelloService extends Remote {
- // Remote method should throw RemoteException
- public String service(String data) throws RemoteException;
- }
三、创建远程类
远程类应符合以下条件:
(1)继承java.rmi.server.UnicastRemoteObject类并实现远程接口;
(2)构造器必须抛出java.rmi.RemoteException异常。
- package main;
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- // Inherit UnicastRemoteObject and implement HelloService interface
- public class HelloServiceImpl extends UnicastRemoteObject
- implements HelloService {
- private static final long serialVersionUID = 1L;
- private String name;
- public HelloServiceImpl(String name) throws RemoteException {
- super();
- this.name = name;
- // UnicastRemoteObject.exportObject(this, 0);
- }
- @Override
- public String service(String data) throws RemoteException {
- return data + name;
- }
- }
四、创建服务器程序
在这里要介绍几个方法:
- bind(String name, Object obj): 注册对象,把对象与服务名绑定。如果该服务名已与其他对象绑定,则会抛出NameAlreadyBoundException异常。
- rebind(String name, Object obj): 注册对象,把对象与服务名绑定。如果该服务名已与其他对象绑定,不会抛异常,而是将新的对象绑定到该服务名上。
- lookup(String name): 查找对象,返回与指定名称相同的对象。
服务器端首先要创建注册表实例,然后将远程对象注册到注册表上。服务器程序运行就绪之后,不会立即结束,而是去监控客户端的连接。关于服务名的命名格式,推荐使用“rmi://主机名:端口号/实例名”的方式,这样可以避免在远程对象很多的时候因服务名一致而引发的冲突。默认的端口号1099可以省略。
- package main;
- import java.rmi.RemoteException;
- import java.rmi.registry.LocateRegistry;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class Server {
- public static void main(String[] args) {
- try {
- LocateRegistry.createRegistry(1099);
- HelloService service1 = new HelloServiceImpl("service1");
- Context namingContext = new InitialContext();
- namingContext.rebind("rmi://localhost:1099/HelloService1",
- service1);
- }
- catch (RemoteException | NamingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println("Successfully register a remote object.");
- }
- }
五、创建客户端程序
- package main;
- import java.rmi.RemoteException;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class Client {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String url = "rmi://localhost:1099/";
- try {
- Context namingContext = new InitialContext();
- HelloService serv = (HelloService) namingContext.lookup(
- url + "HelloService1");
- String data = "This is RMI Client.";
- System.out.println(serv.service(data));
- }
- catch (NamingException | RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
六、运行
在Eclipse中先启动Server,然后启动Client即可。
转自:https://blog.csdn.net/xiaoyaoyulinger/article/details/51452674