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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/56687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:52:39  来源:igfitidea点击:

How do I ensure that RMI uses only a specific set of ports?

javarmi

提问by Bob Cross

In our application, we are using RMI for client-server communication in very different ways:

在我们的应用程序中,我们以非常不同的方式使用 RMI 进行客户端-服务器通信:

  1. Pushing data from the server to the client to be displayed.
  2. Sending control information from the client to the server.
  3. Callbacks from those control messages code paths that reach back from the server to the client (sidebar note - this is a side-effect of some legacy code and is not our long-term intent).
  1. 将数据从服务器推送到客户端进行显示。
  2. 从客户端向服务器发送控制信息。
  3. 来自那些控制消息代码路径的回调从服务器返回到客户端(侧边栏注意 - 这是一些遗留代码的副作用,不是我们的长期意图)。

What we would like to do is ensure that all of our RMI-related code will use only a known specified inventory of ports. This includes the registry port (commonly expected to be 1099), the server port and any ports resulting from the callbacks.

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

Here is what we already know:

这是我们已经知道的:

  1. LocateRegistry.getRegistry(1099) or Locate.createRegistry(1099) will ensure that the registry is listening in on 1099.
  2. Using the UnicastRemoteObject constructor / exportObject static method with a port argument will specify the server port.
  1. LocateRegistry.getRegistry(1099) 或 Locate.createRegistry(1099) 将确保注册中心正在侦听 1099。
  2. 使用带有端口参数的 UnicastRemoteObject 构造函数/exportObject 静态方法将指定服务器端口。

These points are also covered in this Sun forum post.

这些要点也包含在Sun 论坛帖子中

What we don't know is: how do we ensure that the client connections back to the server resulting from the callbacks will only connect on a specified port rather than defaulting to an anonymous port?

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

EDIT: Added a longish answer summarizing my findings and how we solved the problem. Hopefully, this will help anyone else with similar issues.

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

SECOND EDIT: It turns out that in my application, there seems to be a race condition in my creation and modification of socket factories. I had wanted to allow the user to override my default settings in a Beanshell script. Sadly, it appears that my script is being run significantly after the first socket is created by the factory. As a result, I'm getting a mix of ports from the set of defaults and the user settings. More work will be required that's out of the scope of this question but I thought I would point it out as a point of interest for others who might have to tread these waters at some point....

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

采纳答案by Dave Webb

You can do this with a custom RMI Socket Factory.

您可以使用自定义 RMI 套接字工厂来完成此操作。

The socket factories create the sockets for RMI to use at both the client and server end so if you write your own you've got full control over the ports used. The client factories are created on the server, Serialized and then sent down to the client which is pretty neat.

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

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

这是 Sun 的指南,告诉您如何操作。

回答by user207421

You don't need socket factories for this, or even multiple ports. If you're starting the Registry from your server JVM you can use port 1099 for everything, and indeed that is what will happen by default. If you're not starting the registry at all, as in a client callback object, you can provide port 1099 when exporting it.

为此,您不需要套接字工厂,甚至不需要多个端口。如果您从您的服务器 JVM 启动注册表,您可以将端口 1099 用于所有内容,实际上这就是默认情况下会发生的情况。如果您根本没有启动注册表,如在客户端回调对象中,您可以在导出时提供端口 1099。

The part of your question about 'the client connections back to the server resulting from callbacks' doesn't make sense. They are no different from the original client connections to the server, and they will use the same server port(s).

关于“客户端连接回由回调产生的服务器”的问题的一部分没有意义。它们与原始客户端到服务器的连接没有什么不同,它们将使用相同的服务器端口。

回答by Bob Cross

Summary of the long answer below: to solve the problem that I had (restricting server and callback ports at either end of the RMI connection), I needed to create two pairs of client and server socket factories.

下面的长答案摘要:为了解决我遇到的问题(限制 RMI 连接两端的服务器和回调端口),我需要创建两对客户端和服务器套接字工厂。

Longer answer ensues:

更长的答案随之而来:

Our solution to the callback problem had essentially three parts. The first was the object wrapping which needed the ability to specify that it was being used for a client to server connection vs. being used for a server to client callback. Using an extension of UnicastRemoteObjectgave us the ability to specify the client and server socket factories that we wanted to use. However, the best place to lock down the socket factories is in the constructor of the remote object.

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

So, the first argument specifies the part on which the object is expecting requests, whereas the second and third specify the socket factories that will be used at either end of the connection driving this remote object.

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

Since we wanted to restrict the ports used by the connection, we needed to extend the RMI socket factories and lock down the ports. Here are some sketches of our server and client factories:

由于我们想限制连接使用的端口,我们需要扩展 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);
    }
}
// ....
}

Note that the server socket factory above ensures that only the port that you previously specified will ever be used by this factory. The client socket factory has to be paired with the appropriate socket factory (or you'll never connect).

请注意,上面的服务器套接字工厂确保该工厂仅使用您之前指定的端口。客户端套接字工厂必须与适当的套接字工厂配对(否则您将永远无法连接)。

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);
    }
}
// ....
}

So, the only thing remaining to force your two way connection to stay on the same set of ports is some logic to recognize that you are calling back to the client-side. In that situation, just make sure that your factory method for the remote object calls the RemoteObjectWrapper constructor up top with the callback parameter set to true.

因此,强制您的双向连接保持在同一组端口上的唯一剩下的事情是一些逻辑来识别您正在回调客户端。在这种情况下,只需确保远程对象的工厂方法调用 RemoteObjectWrapper 构造函数,并将回调参数设置为 true。

回答by Iraklis

I've been having various problems implementing an RMI Server/Client architecture, with Client Callbacks. My scenario is that both Server and Client are behind Firewall/NAT. In the end I got a fully working implementation. Here are the main things that I did:

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

Server Side , Local IP: 192.168.1.10. Public (Internet) IP 80.80.80.10

服务器端,本地IP:192.168.1.10。公共(互联网)IP 80.80.80.10

On the Firewall/Router/Local Server PC open port 6620. On the Firewall/Router/Local Server PC open port 1099. On the Router/NAT redirect incoming connections on port 6620 to 192.168.1.10:6620 On the Router/NAT redirect incoming connections on port 1099 to 192.168.1.10:1099

在防火墙/路由器/本地服务器 PC 上打开端口 6620。在防火墙/路由器/本地服务器 PC 上打开端口 1099。在路由器/NAT 上将端口 6620 上的传入连接重定向到 192.168.1.10:6620 在路由器/NAT 上重定向传入端口 1099 到 192.168.1.10:1099 的连接

In the actual program:

在实际程序中:

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);

Client Side, Local IP: 10.0.1.123 Public (Internet) IP 70.70.70.20

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

On the Firewall/Router/Local Server PC open port 1999. On the Router/NAT redirect incoming connections on port 1999 to 10.0.1.123:1999

在防火墙/路由器/本地服务器 PC 上打开端口 1999。在路由器/NAT 上,将端口 1999 上的传入连接重定向到 10.0.1.123:1999

In the actual program:

在实际程序中:

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

Hope this helps. Iraklis

希望这可以帮助。伊拉克利斯