在我们的应用程序中,我们使用RMI以非常不同的方式进行客户端 - 服务器通信:
我们希望确保所有与RMI相关的代码仅使用已知的指定端口清单。这包括注册表端口(通常预期为1099),服务器端口以及回调产生的任何端口。
以下是我们已经知道的事情:
这些要点也包含在Sun forum post中。
我们不知道的是:我们如何确保回调产生的客户端连接回服务器只能连接到指定的端口,而不是默认为匿名端口?
编辑:添加了一个很长的答案,总结了我的发现以及我们如何解决问题。希望这可以帮助其他有类似问题的人。第二次编辑:事实证明,在我的应用程序中,我在创建和修改套接字工厂时似乎存在竞争条件。我曾希望允许用户在Beanshell脚本中覆盖我的默认设置。遗憾的是,在工厂创建第一个套接字后,我的脚本似乎正在运行。因此,我从默认设置和用户设置中获得了混合端口。需要做更多的工作,这超出了这个问题的范围,但我想我会指出它是其他可能不得不在某些时候踏上这些水域的人的兴趣点....
答案 0 :(得分:3)
您可以使用自定义RMI套接字工厂执行此操作。
套接字工厂为客户端和服务器端的RMI创建套接字,因此如果您自己编写,则可以完全控制所使用的端口。客户端工厂在服务器上创建,序列化,然后发送到客户端,非常整洁。
答案 1 :(得分:2)
您不需要套接字工厂,甚至不需要多个端口。如果您从服务器JVM启动注册表,则可以将端口1099用于所有内容,实际上这是默认情况下会发生的情况。如果你根本没有启动注册表,就像在客户端回调对象中一样,你可以在导出它时提供端口1099.
您的问题部分是关于回调产生的客户端连接回服务器'没有意义。它们与服务器的原始客户端连接没有区别,它们将使用相同的服务器端口。
答案 2 :(得分:1)
下面的长答案摘要:为了解决我遇到的问题(限制RMI连接两端的服务器和回调端口),我需要创建两对客户端和服务器套接字工厂。
接下来会有更长的答案:
我们对回调问题的解决方案基本上有三个部分。第一个是对象包装,它需要能够指定它用于客户端到服务器连接而不是用于服务器到客户端回调。使用UnicastRemoteObject
的扩展使我们能够指定我们想要使用的客户端和服务器套接字工厂。但是,锁定套接字工厂的最佳位置是远程对象的构造函数。
public class RemoteObjectWrapped extends UnicastRemoteObject {
// ....
private RemoteObjectWrapped(final boolean callback) throws RemoteException {
super((callback ? RemoteConnectionParameters.getCallbackPort() : RemoteConnectionParameters.getServerSidePort()),
(callback ? CALLBACK_CLIENT_SOCKET_FACTORY : CLIENT_SOCKET_FACTORY),
(callback ? CALLBACK_SERVER_SOCKET_FACTORY : SERVER_SOCKET_FACTORY));
}
// ....
}
因此,第一个参数指定对象期望请求的部分,而第二个和第三个参数指定将在驱动此远程对象的连接的任一端使用的套接字工厂。
由于我们想限制连接使用的端口,我们需要扩展RMI套接字工厂并锁定端口。以下是我们的服务器和客户端工厂的一些草图:
public class SpecifiedServerSocketFactory implements RMIServerSocketFactory {
/** Always use this port when specified. */
private int serverPort;
/**
* @param ignoredPort This port is ignored.
* @return a {@link ServerSocket} if we managed to create one on the correct port.
* @throws java.io.IOException
*/
@Override
public ServerSocket createServerSocket(final int ignoredPort) throws IOException {
try {
final ServerSocket serverSocket = new ServerSocket(this.serverPort);
return serverSocket;
} catch (IOException ioe) {
throw new IOException("Failed to open server socket on port " + serverPort, ioe);
}
}
// ....
}
请注意,上面的服务器套接字工厂可确保此工厂仅使用您之前指定的端口。客户端套接字工厂必须与相应的套接字工厂配对(或者您永远不会连接)。
public class SpecifiedClientSocketFactory implements RMIClientSocketFactory, Serializable {
/** Serialization hint */
public static final long serialVersionUID = 1L;
/** This is the remote port to which we will always connect. */
private int remotePort;
/** Storing the host just for reference. */
private String remoteHost = "HOST NOT YET SET";
// ....
/**
* @param host The host to which we are trying to connect
* @param ignoredPort This port is ignored.
* @return A new Socket if we managed to create one to the host.
* @throws java.io.IOException
*/
@Override
public Socket createSocket(final String host, final int ignoredPort) throws IOException {
try {
final Socket socket = new Socket(host, remotePort);
this.remoteHost = host;
return socket;
} catch (IOException ioe) {
throw new IOException("Failed to open a socket back to host " + host + " on port " + remotePort, ioe);
}
}
// ....
}
因此,唯一可以强制您的双向连接保留在同一组端口上的东西是一些逻辑,可以识别您正在回调客户端。在这种情况下,只需确保远程对象的工厂方法调用RemoteObjectWrapper构造函数,并将callback参数设置为true。
答案 3 :(得分:0)
我在使用客户端回调实现RMI服务器/客户端架构时遇到了各种问题。我的方案是服务器和客户端都在防火墙/ NAT之后。最后,我得到了一个完全正常的实施。以下是我做的主要事情:
在防火墙/路由器/本地服务器PC上打开端口6620。 在防火墙/路由器/本地服务器PC上打开端口1099。 在路由器/ NAT上将端口6620上的传入连接重定向到192.168.1.10:6620 在路由器/ NAT上将端口1099上的传入连接重定向到192.168.1.10:1099
在实际节目中:
System.getProperties().put("java.rmi.server.hostname", IP 80.80.80.10);
MyService rmiserver = new MyService();
MyService stub = (MyService) UnicastRemoteObject.exportObject(rmiserver, 6620);
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
registry.rebind("FAManagerService", stub);
在防火墙/路由器/本地服务器PC上打开端口1999。 在路由器/ NAT上将端口1999上的传入连接重定向到10.0.1.123:1999
在实际节目中:
System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
UnicastRemoteObject.exportObject(this, 1999);
MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");
希望这会有所帮助。 伊拿克里斯