我现在至少坚持了 5 个小时,除了在这里问别无他法。我正在编写一个 RMI 应用程序。我希望服务器绑定(bind)一个远程对象(此处为 NoteBoardImpl
),客户端将查找该对象。客户端将自己的监听器(此处为NoteBoardListener
)传递给服务器,监听器也是客户端导出的远程对象。
我在这里准备了一个简单的SSCCE,所以我真的希望有人能研究一下。所有类都在默认包的同一文件夹中。我知道这是不鼓励的,我应该将应用程序分成三个 jar ,但我想在这里保持尽可能简单。
远程接口(interface):
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface INoteBoard extends Remote {
public void test(INoteBoardListener listener) throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface INoteBoardListener extends Remote {
public void onNewText(String text) throws RemoteException;
}
接口(interface)实现:
import java.rmi.RemoteException;
public class NoteBoardImpl implements INoteBoard {
@Override
public void test(INoteBoardListener listener) throws RemoteException {
listener.onNewText("server call the listener");
}
}
import java.rmi.RemoteException;
public class NoteBoardListener implements INoteBoardListener {
@Override
public void onNewText(String text) throws RemoteException {
System.out.println(text);
}
}
客户端和服务器:
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
public class Client {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("2 arguments required:\nRMI_IP RMI_port");
return;
}
System.setProperty("java.rmi.server.hostname", args[0]);
try {
INoteBoard nb = (INoteBoard) Naming.lookup(String.format("rmi://%s:%s/note", args[0], args[1]));
INoteBoardListener l = (INoteBoardListener) UnicastRemoteObject.exportObject(new NoteBoardListener(), 0);
nb.test(l);
l.onNewText("client invokes listener");
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
public class Server {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("2 arguments required:\nRMI_IP RMI_port");
return;
}
System.setProperty("java.rmi.server.hostname", args[0]);
try {
INoteBoard noteBoard = (INoteBoard) UnicastRemoteObject.exportObject(new NoteBoardImpl(), 0);
Naming.rebind(String.format("rmi://%s:%s/note", args[0], args[1]), noteBoard);
} catch (Exception e) {
e.printStackTrace();
}
}
}
出于测试目的,我尝试模拟分布式系统并在虚拟机上运行客户端。主机-VM 网络具有以下规范 - 主机 IP = 192.168.56.1,VM IP = 192.168.56.101。
首先,我使用以下命令在本地运行客户端和服务器(预先启动了 rmiregistry 1099
)。工作目录是项目的根目录,编译后的类在 bin
目录中:
java -cp bin -Djava.rmi.server.codebase=http://student.agh.edu.pl/~grajewsk/bin/服务器 192.168.56.1 1099
java -cp bin 客户端 192.168.56.1 1099
它奏效了。
然后我使用相同的命令在 VM 上运行客户端程序,这是我得到的异常:
java.rmi.ConnectException: Connection refused to host: 192.168.56.1; nested exception is:
java.net.ConnectException: Connection refused
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at sun.proxy.$Proxy0.test(Unknown Source)
at Client.main(Client.java:14)
Caused by: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
at java.net.Socket.connect(Socket.java:546)
at java.net.Socket.connect(Socket.java:495)
at java.net.Socket.<init>(Socket.java:392)
at java.net.Socket.<init>(Socket.java:206)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 7 more
注意对象是如何在服务器的注册表中成功查找的,然后客户端远程对象被导出(也成功)并且执行在第 14 行中断,我正在尝试调用一个传递客户端对象的服务器端对象上的方法。
我在这两个系统上都没有防火墙,双向 ping 都没有问题。我知道这里一定有一些概念上的问题,当然我对 RMI 有一些误解。非常感谢您的帮助。
二进制代码库是on my student's server ,以及 the source code .提前致谢!
最佳答案
以下是我要查明真相的方法。
- 运行您的服务器应用
找出它用于 RMI 的端口(它是临时的,因此它应该随着您为服务器创建的每个进程而改变)。
- 使用 ps -ef 找到 PID
- 然后 netstat -anp|grep 这应该会给你端口号。它应该绑定(bind)到 0.0.0.0
在客户端虚拟机上,使用 nc 或 telnet 验证您是否可以连接到该端口。如果失败,您可能遇到了防火墙/iptables 问题。
使用 wireshark 验证您的客户端是否确实在尝试连接到您在步骤 #2 中学到的端口/ip 组合。
请记住,仅仅因为您可以 ping 通,并不意味着您可以连接到给定端口。还要检查“iptables -L”。
此外,Naming说不要放置 URL 的方案组件。格式应该是//host:port/name,所以你也应该检查一下。
关于java - 客户端导出对象时为 "java.rmi.ConnectException: Connection refused to host",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15648708/