Вы правы в своем предположении.
Для активного уведомления push-стиля от сервера к клиентам клиенты также должны быть «серверами».
Так что в клиентском приложении вам также необходимо иметь удаленный интерфейс. При первом подключении клиента к серверу вы передаете ему ссылку на экземпляр удаленного интерфейса.
Этот объект необходимо экспортировать как удаленный объект RMI, но его не нужно регистрировать в реестре, так как вы напрямую передадите ему ссылку на сервер, которому необходимо вызвать методы на нем.
На сервере хранится реестр всех клиентов, чтобы он мог перезвонить по мере необходимости. Как правило, карта с ключом является значимым идентификатором клиентов, а значение - удаленной ссылкой на клиента.
Когда клиентское приложение отключено, клиенту необходимо отменить регистрацию.
И сервер, вероятно, захочет периодически проверять всех клиентов, чтобы он не хранил ссылки на мертвых клиентов.
Ваш интерфейс сервера будет выглядеть примерно так:
public interface Server extends Remote {
void register(Client client) throws RemoteException;
void unregister(Client client) throws RemoteException;
void doSomethingUseful(...) throws RemoteException;
...
}
и ваш клиентский интерфейс:
public interface ClientCallbackInterface extends Remote {
void ping() throws RemoteException;
void notifyChanges(...) throws RemoteException;
}
И где-то в клиентском коде запуска приложения:
ClientCallbackInterface client = new ClientImpl();
UnicastRemoteObject.exportObject(client);
Registry registry = LocateRegistry.getRegistry(serverIp, serverRegistryPort);
Server server = (Server) registry.lookup(serviceName);
server.register(client);
Это вполне возможно его реализовать. Но не тривиально. Есть много вещей, о которых вы должны заботиться:
- Вы должны позаботиться о том, что может быть проблемой при наличии брандмауэров.
- Может быть проблемы с местным брандмауэром ОС тоже ваш клиент приложение на самом деле должен открыть местные входящие порты
- Если вы попытаетесь запустить несколько клиентов на той же машине, вы будете иметь порт конфликта, должны позаботиться о том, что слишком
- Полностью не будет работать за пределами LAN
Я реализовал такую систему, и она отлично работает. Но все, что сказал, если бы мне пришлось это сделать снова, я бы определенно использовал что-то еще, возможно, REST
и WebSockets
для обратных вызовов. Это будет гораздо меньше ограничений для сетевой части, просто необходим HTTP.
Не могли бы вы сохранить соединение живым, как в http://stackoverflow.com/questions/227276/java-rmi-not-closing-socket-after-lease-expiration – bhantol
@bhantol Этот вопрос не о том, как сохранить живое соединение и сохранение его жизни не является решением этой проблемы. – EJP
@EJP Ну, мой вопрос в том, собирается ли связь, воссоздана или сохранена в живых? Я хотел знать об этом, так как решение, которое я предлагаю, было в строках WebSockets или именно это, а не даже RMI. – bhantol