如何使用 Java RMI 从服务器向客户端发送消息?

How to send a message from Server to Client using Java RMI?

该应用程序是 LAN 的项目管理应用程序,它具有项目、任务等对象。因此 RMI 似乎是可行的方法。

但是当其他客户端触发事件时,它也会向某些客户端发送实时通知。我读到服务器无法跟踪在 RMI 中连接到它的客户端。因此,作为一种选择,我认为服务器可以连接到客户端,就像客户端事先连接到服务器一样。这是怎么做到的?

如果不是,在这种情况下我应该求助于套接字编程吗?

如果这是一个愚蠢的问题,请提前致歉。

正如 Pierre Henry 的回答所建议的那样,您可以在 RMI 中实现使用观察者设计模式的回调模式。


在粒度级别上,您可以实现套接字编程并使用Observer design pattern。每个客户端将首先注册到服务器上。服务器会将它们存储在数据结构中,当要发送某些事件通知时,服务器将遍历这些已注册的客户端并调用方法或向它们推送通知。

考虑使用 JMS (Java messaging service) 的某些实现,例如 Active MQ。您的发件人将异步收听队列。发件人将发送消息,接收者将接收消息。如果您希望所有客户端都接收消息,请使用主题而不是队列,并且会有发布者和订阅者。

你的假设是对的。

对于从服务器到客户端的主动推送式通知,客户端也必须是 "servers"。

所以在客户端应用中你还需要有一个远程接口。客户端第一次连接到服务器时,您传递给它一个对远程接口实例的引用。

此对象需要作为远程 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);

完全可以实现。但不是微不足道的。你有很多事情要处理:

  • 如果涉及防火墙,您必须注意这可能是个问题。
  • 也可能是本地 OS 防火墙的问题,您的客户端应用实际上必须打开本地传入端口
  • 如果您尝试在同一台机器上启动多个客户端,您将遇到端口冲突,也必须注意这一点
  • 完全不会在 LAN 之外工作

我确实实现了这样的系统并且运行良好。但话虽如此,如果我必须再次这样做,我肯定会使用其他东西,可能是 REST 服务和 WebSockets 回调。它对网络部分的限制要少得多,只需要 HTTP。