Сервер RMI (кажется) игнорирует соединения через WAN

#java #rmi #wan

#java #rmi #глобальная сеть

Вопрос:

На первый взгляд это может показаться основной сетевой проблемой, но я думаю, что это не так. Похоже, что сама Java «игнорирует» соединение RMI.

Все Java 8u101 на ПК с Windows 10.

У нас есть довольно зрелое и надежное распределенное Java-приложение, которое взаимодействует через RMI в локальной сети.
У наших клиентов, которые уже много лет запускают наше приложение в своих локальных сетях, не было никаких проблем. Это же приложение также подключается к общей базе данных (на том же «серверном» ПК, что и сервер RMI).

Один из наших клиентов недавно построил новое здание, возможно, в 100 метрах, и их два помещения соединены через глобальную сеть. Однако клиенты Java на удаленном сайте не могут подключиться к серверу Java RMI.

Кажется, что вся сама сеть в порядке следующим образом….

  • в локальной сети все работает как обычно
  • Не RMI в порядке:
    • из удаленного местоположения мы можем запустить другой клиент базы данных и успешно подключиться к серверу базы данных (на том же компьютере, что и сервер RMI)
  • Порт RMI кажется нормальным со стороны клиента
    • используя клиент telnet в удаленном местоположении, мы можем установить соединение с портом RMI
  • Порт RMI, похоже, подключается к серверному ПК:
    • на компьютере, на котором работает сам сервер Java RMI, мы можем наблюдать соединения через «netstat -an», которые показывают установленное соединение, поступающее с соответствующего IP-адреса WAN.

Это указывало (для меня), что соединение попадает в правильное местоположение и не блокируется неправильной конфигурацией, брандмауэром или чем-либо еще. Кажется, что он «входит» в компьютер сервера RMI, но затем не попадает на сам сервер RMI.

Моим первым предположением было попытаться использовать что-то вроде «панели управления Java» и, возможно, настроить параметр безопасности, который может ограничивать трафик RMI, но я не могу распознать ни одного такого переключателя.

Кто-нибудь может предложить какие-либо подсказки по этому поводу?

  • любой способ проверить, что соединение попадает на Java?
  • может ли это быть Java security, которая блокирует это перед передачей на сервер RMI?

Любые предложения или рекомендации будут высоко оценены.

(Отредактировано, добавлено больше деталей …)

Немного больше информации заключается в следующем…

  • в локальной сети серверный компьютер известен как 192.168.0.110
  • из удаленного местоположения (через глобальную сеть) серверный КОМПЬЮТЕР известен как 110.142.83.167
  • удаленный клиент, похоже, получает объект сервера RMI
  • Трассировка стека (подробно описанная ниже) имеет «отказано в подключении к хосту: 192.168.0.110;»
  • таким образом, кажется, что существует путаница в отношении того, какой это IP-адрес. Клиент запросил 110.142.83.167, но сервер считает, что это 192.168.0.110

Трассировки стека следующие…

 2016-10-18 15:02:11,123 [S38P4][43983  ][AWT-EventQueue-0] WARN  StackTraceLogger log: StackTraceLogger ------- INFO=ClientRMIObject.connectToSentry(): RemoteException: sentryIP=110.142.83.167<<
StackTraceLogger ------ Throwable.MSG=Connection refused to host: 192.168.0.110; nested exception is: 
    java.net.ConnectException: Connection timed out: connect< Throwable=java.rmi.ConnectException: Connection refused to host: 192.168.0.110; nested exception is: 
    java.net.ConnectException: Connection timed out: connect
StackTraceLogger.START TRACE .................................
   sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
   sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
   sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
   sun.rmi.server.UnicastRef.invoke(Unknown Source)
   java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
   java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
   com.sun.proxy.$Proxy13.registerWithSentry(Unknown Source)
   com.my.co.package.ClientRMIObject.connectToSentry(ClientRMIObject.java:198)
 

Following super cut down version (no boiler plate etc) of the code …

На сервере RMI…

 // auto called at server on startup ...
public final class MyServer {
  public final static int SRVR_PORT = 21099;
  public final static String LOOKUP_NAME = "MyLookupName";
  public MyServer() {
      java.rmi.registry.Registry reg = java.rmi.registry.LocateRegistry.createRegistry(SRVR_PORT);
      ServerRMIObject srvrRmiObj = new ServerRMIObject();
      reg.rebind(LOOKUP_NAME, srvrRmiObj);
  }
}
 

ServerRMIObject …

 public final class ServerRMIObject extends java.rmi.server.UnicastRemoteObject {
  private List<ClientRMIObject> clients = ...

  public ServerRMIObject() throws java.rmi.RemoteException {
    super(MyServer.SRVR_PORT);
  }

  public void registerWithSrvr(ClientRMIObject client) {
    logger.info("This line is never run when called from remote location");
    clients.add(client);
  }

  // example method ...
  public void broadcastToClients(Delivery d) {
    for (ClientRMIObject client : clients) {
      client.receiveFromSrvr(d);
    }
  }
}
 

На каждом клиенте RMI…

 public final class ClientRMIObject extends java.rmi.server.UnicastRemoteObject {

  private final int CLIENT_PORT = 21099;
  private final String SRVR_FROM_LAN = "//192.168.0.110:" MyServer.SRVR_PORT "/" MyServer.LOOKUP_NAME; 
  private final String SRVR_FROM_WAN = "//110.142.83.167:" MyServer.SRVR_PORT "/" MyServer.LOOKUP_NAME;
  private ServerRMIObject rmiSrvr = null;

  public ClientRMIObject() {
    super(CLIENT_PORT);
  }

  // auto called at client on startup
  public void start() {
    if (VIA_LAN) {
        rmiSrvr = java.rmi.Naming.lookup(SRVR_FROM_LAN);
    } else if (VIA_WAN) {
        rmiSrvr = java.rmi.Naming.lookup(SRVR_FROM_WAN);
    }

    // this has been successful to here. 

    // the following throws an exception ...
    // java.rmi.ConnectException: Connection refused to host: 192.168.0.110; 
    // please see more exception details above        
    rmiSrvr.registerWithSrvr(this); 

  }

  // example method ...
  public void receiveFromSrvr(Delivery d) {
     ....
  }

  // example broadcast from one client to all clients ...
  public void broadcastToClients(Delivery d) {
    rmiSrvr.broadcastToClients(d);
  }
}
 

Комментарии:

1. Определите «не удается подключиться». Что происходит вместо этого?

2. вы явно указали оба порта rmi?

3. Спасибо EJP и jtahlborn за ваши ответы.

4. Woops, слишком быстрое нажатие кнопки ввода …. jtahlborn: я полагаю, что я указал оба порта (на стороне клиента * сервера) EJP: я добавил более подробную информацию в вопрос, включая местоположение исключения.

5. Я не вижу никакого кода, который registerWithSentry() где-либо вызывается, и я также не вижу никаких удаленных интерфейсов или удаленных объектов, которые их реализуют. Это еще не правильный RMI.

Ответ №1:

Мы столкнулись с аналогичной проблемой, когда обновили серверные компьютеры RMI до WINDOWS 10.

Это исключение связано с тем, что брандмауэр Windows Windows10, на котором запущен сервер, не разрешает трафик на порты по протоколу TCP.

Чтобы включить брандмауэр windows10 для TCP, нам нужно выполнить следующие действия: 1. Откройте панель управления 2. Брандмауэр Windows -> Дополнительные настройки 3. Выберите правила входящих сообщений 4. В правом столбце -> опция нового правила 5. Создайте правило порта и создайте как правило TCP, так и UDP, оба — локальный порт 1099 / Все порты 6. В разделе действий нажмите «разрешить соединение» 7. Проверьте все, где применяется правило

Ответ №2:

Из https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/javarmiproperties.html

java.rmi.server.hostname

Значение этого свойства представляет строку имени хоста, которая должна быть связана с удаленными заглушками для локально созданных удаленных объектов, чтобы позволить клиентам вызывать методы на удаленном объекте. Значением этого свойства по умолчанию является IP-адрес локального хоста в формате «dotted-quad».

Реестр RMI сообщает клиенту IP-адрес (или имя хоста) и порт, к которому клиент может затем подключиться для вызова удаленного объекта. По умолчанию он использует адрес локального хоста.

Ваш сервер настроен с IP-адресом 192.168.0.110 , и, следовательно, это то, о чем сообщает реестр RMI. Реестр RMI не знает, что вы хотите получить доступ к компьютеру через какой-либо другой IP-адрес (а именно общедоступный маршрутизируемый 110.142.83.167 — и при этом разумно не знать, так ли это, что брандмауэр выполняет преобразование сетевых адресов).

Разговор идет так:

  • Клиент WAN подключается к реестру RMI по адресу 110.142.83.167 .
  • Клиент WAN запрашивает: Я хотел бы знать, как общаться с объектом Foo
  • Отчеты реестра RMI: вы можете связаться Foo с 192.168.0.110:<some port>
  • Клиент WAN пытается подключиться 192.168.0.110:<some port> , но эта машина недоступна

Вы можете установить java.rmi.server.hostname явное значение (например, 110.142.83.167 ), но обратите внимание, что тогда все клиенты будут пытаться подключиться к удаленным объектам, используя этот IP-адрес.

Одним из способов решения этой проблемы является использование имени хоста и разделенной системы DNS, в которой внешние клиенты разрешают имя хоста 110.142.83.167 , а внутренние клиенты разрешают имя хоста 192.168.0.110 . (В качестве альтернативы вы можете использовать запись файла hosts на клиентских компьютерах, но это становится проблемой в административном отношении, если количество клиентских компьютеров велико).

Также обратите внимание, что если вы явно не привязали удаленный объект к определенному порту, тогда будет использоваться эфемерный порт — поэтому вам понадобится ваш брандмауэр, чтобы разрешить доступ ко всем портам. Привязав удаленный объект к определенному порту, ваш брандмауэр может быть настроен так, чтобы пропускать только этот порт (плюс порт реестра).

Комментарии:

1. Спасибо, Грег. Уточнение… При создании ServerRMIObject «жестко» устанавливается как «My-IP = 192.168.0.110 и My-Port = 21099». Затем удаленный клиент получает ServerRMIObject из реестра, и ServerRMIObject по-прежнему считает «My-IP = 192.168.0.110 и My-Port = 21099». Итак, теперь мне нужно, чтобы удаленный клиент «перехватил» вызов 192.168.0.110 (IP-адрес сервера в локальной сети) и вместо этого перенаправил его на 110.142.83.167 (IP-адрес сервера в глобальной сети). Если да, возможно ли это с помощью таблиц маршрутов и т. Д.? — или это сложнее, чем это? Большое спасибо.

2. @DamianC Что именно вы подразумеваете под «жестким набором»? Метод, описанный в этом ответе, является единственным способом связать IP-адрес, отличный от IP-адреса по умолчанию, с удаленным объектом.