下面的长答案总结:为了解决我遇到的问题(限制 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 构造函数,并将回调参数设置为 true。