你的假设是对的。
对于从服务器到客户端的主动推送式通知,客户端也必须是“服务器”。
因此,在客户端应用程序中,您还需要有一个远程接口。客户端第一次连接到服务器时,您向它传递对远程接口实例的引用。
该对象需要导出为远程 RMI 对象,但不需要在注册表中注册,因为您会将对该对象的引用直接传递给需要对其调用方法的服务器。
服务器保留所有客户端的注册信息,以便在需要时进行回调。通常是一个 Map,键是客户端的有意义的标识符,值是对客户端的远程引用。
当客户端应用关闭时,客户端需要注销。
并且服务器可能希望对所有客户端进行定期检查,这样它就不会保留对死客户端的引用。
您的服务器界面看起来像这样:
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);
完全可以实现。但并非微不足道。有很多事情需要注意:
- 如果涉及防火墙,您必须注意哪些可能是问题。
- 本地操作系统防火墙也可能有问题,您的客户端应用实际上必须打开本地传入端口
- 如果您尝试在同一台计算机上启动多个客户端,则会出现端口冲突,也必须注意这一点
- 完全不能在局域网之外工作
我确实实现了这样的系统,它运行良好。但是如果我不得不再做一次,我肯定会使用其他东西,可能REST 服务和WebSockets 用于回调。对网络部分的限制要少得多,只需要 HTTP。