Как я могу гарантировать, что RMI использует только определенный набор портов? - PullRequest
8 голосов
/ 11 сентября 2008

В нашем приложении мы используем RMI для взаимодействия клиент-сервер различными способами:

  1. Передача данных с сервера на клиент для отображения.
  2. Отправка управляющей информации с клиента на сервер.
  3. Обратные вызовы из тех путей кодов управляющих сообщений, которые доходят от сервера до клиента (примечание на боковой панели - это побочный эффект некоторого устаревшего кода и не является нашим долгосрочным намерением).

Что мы хотели бы сделать, так это убедиться, что весь наш код, связанный с RMI, будет использовать только известную указанную инвентаризацию портов. Это включает порт реестра (обычно ожидается 1099), порт сервера и любые порты, являющиеся результатом обратных вызовов.

Вот что мы уже знаем:

  1. LocateRegistry.getRegistry (1099) или Locate.createRegistry (1099) обеспечит прослушивание реестра 1099.
  2. При использовании статического метода UnicastRemoteObject / exportObject с аргументом порта будет указан порт сервера.

Эти моменты также рассматриваются в этом сообщении на форуме Sun .

Чего мы не знаем: как мы можем гарантировать, что клиентские подключения обратно к серверу в результате обратных вызовов будут подключаться только к указанному порту, а не по умолчанию к анонимному порту?

РЕДАКТИРОВАТЬ: Добавлен длинный ответ, обобщающий мои выводы и то, как мы решили проблему. Надеюсь, это поможет кому-то еще с подобными проблемами.

ВТОРОЕ РЕДАКТИРОВАНИЕ: Оказывается, в моем приложении, кажется, есть условие гонки в моем создании и модификации фабрик сокетов. Я хотел позволить пользователю переопределить мои настройки по умолчанию в скрипте Beanshell. К сожалению, похоже, что мой скрипт запускается значительно после того, как фабрика создаст первый сокет. В результате я получаю смесь портов из набора настроек по умолчанию и пользовательских настроек. Потребуется дополнительная работа, которая выходит за рамки этого вопроса, но я подумал, что хотел бы указать на это как на интерес для тех, кому в какой-то момент придется идти по этим водам ...

Ответы [ 4 ]

3 голосов
/ 11 сентября 2008

Это можно сделать с помощью пользовательской фабрики сокетов RMI.

Фабрики сокетов создают сокеты для RMI для использования как на стороне клиента, так и на стороне сервера, поэтому, если вы напишите свой собственный, вы получите полный контроль над используемыми портами. Клиентские фабрики создаются на сервере, сериализуются и затем отправляются клиенту, что довольно аккуратно.

Вот руководство в Sun, рассказывающее, как это сделать.

2 голосов
/ 21 мая 2016

Для этого вам не нужны фабрики сокетов или даже несколько портов. Если вы запускаете Реестр с JVM вашего сервера, вы можете использовать порт 1099 для всего, и это действительно происходит по умолчанию. Если вы вообще не запускаете реестр, как в объекте обратного вызова клиента, вы можете указать порт 1099 при его экспорте.

Часть вашего вопроса о «клиентских подключениях к серверу в результате обратных вызовов» не имеет смысла. Они ничем не отличаются от исходных клиентских подключений к серверу и будут использовать один и тот же порт (ы) сервера.

1 голос
/ 19 ноября 2008

Краткое описание длинного ответа ниже: чтобы решить мою проблему (ограничение серверов и портов обратного вызова на обоих концах 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.

0 голосов
/ 16 марта 2010

У меня были различные проблемы при реализации архитектуры RMI Server / Client с клиентскими обратными вызовами. Мой сценарий заключается в том, что сервер и клиент находятся за брандмауэром / NAT. В итоге я получил полностью работающую реализацию. Вот основные вещи, которые я сделал:

Сторона сервера, локальный IP: 192.168.1.10. Общественный (Интернет) IP 80.80.80.10

На ПК с брандмауэром / маршрутизатором / локальным сервером откройте порт 6620. На ПК с брандмауэром / маршрутизатором / локальным сервером откройте порт 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);

Клиентская сторона, локальный IP: 10.0.1.123 Public (Internet) IP 70.70.70.20

На ПК с брандмауэром / маршрутизатором / локальным сервером откройте порт 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 ");

Надеюсь, это поможет. Ираклис

...