如何使用 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。
该应用程序是 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。