结构:

1.定义一个继承Remote类的接口(服务端和客户端一致):
/**
* 2019年7月1日下午2:25:15
*/
package testrmiserver.remoteobject;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @author XWF
*
*/
public interface IRemoteObj extends Remote {
public int getStrLen(String str) throws RemoteException;//需要抛异常,实现类继承的UnicastRemoteObject构造方法抛异常
}
2.服务端实现接口:
/**
* 2019年7月1日下午2:28:20
*/
package testrmiserver.remoteobject;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* @author XWF
*
*/
public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj {
public RemoteObjImpl() throws RemoteException {
super();
}
@Override
public int getStrLen(String str) throws RemoteException {
System.out.println("收到调用,str=" + str);
return str == null ? -1 : str.length();
}
}
3.服务端:
/**
* 2019年7月1日下午2:24:01
*/
package testrmiserver;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.util.Arrays;
import testrmiserver.remoteobject.IRemoteObj;
import testrmiserver.remoteobject.RemoteObjImpl;
/**
* @author XWF
*
*/
public class TestRmiServer {
private static int registryPort = 9999;//服务端注册端口
private static int serverPort = 1234;//服务端服务端口
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out.println("开始服务端");
//指定服务端口
RMISocketFactory.setSocketFactory(new RMISocketFactory() {
@Override
public Socket createSocket(String host, int port) throws IOException {
System.out.println("createSocket, host=" + host + ",port=" + port);
return new Socket(host, port);
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
if(port == 0) {
port = TestRmiServer.serverPort;//服务端口
}
System.out.println("createServerSocket,port=" + port);
return new ServerSocket(port);
}
});
//注册端口
Registry r = LocateRegistry.createRegistry(TestRmiServer.registryPort);
System.out.println("----");
System.setProperty("java.rmi.server.hostname", "127.0.0.1");//创建服务端口时会用hostname,不要使用localhost或者127.0.0.1,客户端创建对应的端口时也会使用该值
IRemoteObj obj = new RemoteObjImpl();
Naming.bind("rmi://192.168.1.30:9999/testrmi", obj);
// Naming.bind("rmi://192.168.1.30:9999/testrmi", obj);//bind不能重复绑定name
Naming.bind("rmi://192.168.1.30:9999/A", obj);//bind不同name可以绑定同一个obj
Naming.rebind("rmi://192.168.1.30:9999/testrmi", obj);//重复绑定用rebind
System.out.println(Arrays.asList(r.list()));
System.out.println("服务端启动完毕");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.客户端:
/**
* 2019年7月1日下午2:29:32
*/
package testrmiclient;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.RMISocketFactory;
import testrmiserver.remoteobject.IRemoteObj;
/**
* @author XWF
*
*/
public class TestRmiClient {
private static int registryPort = 9999;//服务端注册端口
private static int serverPort = 1234;//服务端服务端口
private static int forRegistryPort = 8888;//客户端对接注册端口
private static int clientPort = 4321;//客户端对接服务端口
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out.println("远程调用");
RMISocketFactory.setSocketFactory(new RMISocketFactory() {
//失败会三次重试
@Override
public Socket createSocket(String host, int port) throws IOException {
System.out.println("createSocket, host=" + host + ",port=" + port);
Socket s = new Socket();
if(port == TestRmiClient.registryPort) {
s.bind(new InetSocketAddress(TestRmiClient.forRegistryPort));
}else if(port == TestRmiClient.serverPort) {
s.bind(new InetSocketAddress(TestRmiClient.clientPort));
}
s.connect(new InetSocketAddress(host, port));
System.out.println(s.getLocalSocketAddress().toString());
System.out.println(s.getRemoteSocketAddress().toString());
return s;
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
System.out.println("createServerSocket,port=" + port);
return new ServerSocket(port);
}
});
IRemoteObj obj = (IRemoteObj) Naming.lookup("rmi://192.168.1.30:9999/testrmi");
System.out.println("len:" + obj.getStrLen("hello"));
for(int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello" + i + " len:" + obj.getStrLen("hello" + i));
}
} catch (MalformedURLException | RemoteException | NotBoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:


注意事项:
1.接口里的远程方法需要抛出RemoteException异常,服务端的实现类继承了UnicastRemoteObject类,该类构造方法抛出异常,实现类构造方法也要有;
2.服务端和客户端的接口要保持一致(包括包名也要一致);

3.Naming的bind方法不要重复绑定相同name值,需要用rebind重新绑定;
4.System.setProperty的hostname不要用localhost或者127.0.0.1,主要是客户端对接服务器服务端口时使用了该值,客户端和服务端通常不是在一台机器上;
否则客户端会出下面这种异常:


参考:
https://blog.youkuaiyun.com/5iasp/article/details/8707596
https://blog.youkuaiyun.com/lmy86263/article/details/72594760
本文详细介绍RMI(远程方法调用)的实现步骤,包括定义远程接口、服务端实现、服务注册及客户端调用过程。通过具体示例,演示如何解决常见问题,如异常处理、端口设置和跨机器通信。
2184

被折叠的 条评论
为什么被折叠?



