代码之家  ›  专栏  ›  技术社区  ›  Bob Cross n8wrl

如何确保RMI只使用一组特定的端口?

  •  8
  • Bob Cross n8wrl  · 技术社区  · 18 年前

    1. 将控制信息从客户端发送到服务器。
    2. 从那些从服务器返回到客户机的控制消息代码路径进行回调(侧栏注意-这是一些遗留代码的副作用,不是我们的长期意图)。

    我们要做的是确保所有与RMI相关的代码只使用已知的指定端口清单。这包括注册表端口(通常预期为1099)、服务器端口和回调产生的任何端口。

    1. 使用带有端口参数的UnicastRemoteObject构造函数/exportObject静态方法将指定服务器端口。

    这些要点也将在本手册中介绍 Sun forum post .

    我们不知道的是:如何确保回调后返回到服务器的客户端连接只在指定端口上连接,而不是默认连接到匿名端口?

    编辑:添加了一个较长的答案,总结了我的发现以及我们是如何解决问题的。希望这能帮助其他有类似问题的人。

    第二次编辑:事实证明,在我的应用程序中,在我创建和修改套接字工厂时似乎存在竞争条件。我想让用户在Beanshell脚本中覆盖我的默认设置。遗憾的是,在工厂创建第一个套接字之后,我的脚本似乎正在显著运行。因此,我从一组默认值和用户设置中混合获得了端口。需要做更多的工作,这超出了这个问题的范围,但我想我会指出这一点,作为其他人的兴趣点,他们可能不得不在某个时候涉足这些领域。。。。

    4 回复  |  直到 17 年前
        1
  •  4
  •   David Webb    11 年前

    您可以使用自定义RMI套接字工厂来实现这一点。

    套接字工厂为RMI创建用于客户端和服务器端的套接字,因此,如果您编写自己的套接字,则可以完全控制所使用的端口。客户机工厂在服务器上创建,序列化,然后发送到客户机,这非常整洁。

    Here's a guide at Sun telling you how to do it.

        2
  •  2
  •   user207421    10 年前

    这不需要套接字工厂,甚至不需要多个端口。如果您是从服务器JVM启动注册表,那么您可以使用端口1099进行所有操作,实际上,默认情况下会发生这种情况。如果您根本没有启动注册表(如在客户端回调对象中),则可以在导出它时提供端口1099。

        3
  •  1
  •   Bob Cross n8wrl    17 年前

    接下来是更长的回答:

    我们对回调问题的解决方案基本上有三个部分。第一个是对象包装,它需要能够指定它用于客户端到服务器的连接,而不是用于服务器到客户端的回调。使用 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));
    }
    // ....
    }
    

    因此,第一个参数指定对象期望请求的部分,而第二个和第三个参数指定将在驱动此远程对象的连接的任一端使用的套接字工厂。

    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。

        4
  •  0
  •   Flexo - Save the data dump sunny moon    10 年前

    我在实现RMI服务器/客户端体系结构以及客户端回调时遇到了各种问题。我的场景是服务器和客户端都在防火墙/NAT后面。最后,我得到了一个完全有效的实现。以下是我做的主要事情:

    在防火墙/路由器/本地服务器PC上打开端口6620。 在防火墙/路由器/本地服务器PC上打开端口1099。 在路由器/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);
    

    客户端,本地IP:10.0.1.123公共(互联网)IP 70.70.70.20

    在实际程序中:

    System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
    UnicastRemoteObject.exportObject(this, 1999);
    MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");
    

    希望这有帮助。 伊拉克利斯