RMI 客戶端需硬編碼服務 URL (rmi://host:port/Name);Jini 透過 Lookup Service 實現服務發現,客戶端無需知曉服務位置。為不重寫現有 RMI 系統,實現輕量級「類 Jini」發現層。
多播請求封包格式:RMI-DISCOVERY,回應端口,介面類全名,服務唯一ID
RMI-DISCOVERY 過濾雜訊。ServerSocket 監聽回應端口,發送多播 UDP 封包。MulticastSocket 監聽固定群組 (如 228.5.6.7:6789),收到合法封包解析,比對介面與服務 ID。ServerSocket,序列化 MarshalledObject 包裝的 Remote Stub 傳遞。MarshalledObject.get() 獲得 Stub,開始遠端調用。RMILookup (服務端):綁定 Remote 實例與服務 ID,啟動多播監聽線程,處理請求、比對、單播回傳 Stub。RMIDiscovery (客戶端):lookup(Class clazz, String id),啟動單播監聽、多播發送、接收 Stub、返回 Remote 介面。MarshalledObject:攜帶 Stub 序列化數據與 Codebase,解決客戶端無 Stub 類定義時的 ClassNotFoundException。
// 服務端多播監聽循環
while (active) {
DatagramPacket pkt = new DatagramPacket(buf, buf.length);
socket.receive(pkt);
String msg = new String(pkt.getData()).trim();
if (msg.startsWith("RMI-DISCOVERY")) {
String[] parts = parseMsg(msg); // port, interface, serviceId
if (matchInterface(parts[1]) && matchServiceId(parts[2])) {
Socket sock = new Socket(pkt.getAddress(), Integer.parseInt(parts[0]));
new ObjectOutputStream(sock.getOutputStream()).writeObject(new MarshalledObject(remoteStub));
sock.close();
}
}
}
// 客戶端發現
ServerSocket listener = new ServerSocket(0); // 自動分配端口
int port = listener.getLocalPort();
sendMulticast("RMI-DISCOVERY," + port + "," + clazz.getName() + "," + id);
Socket sock = listener.accept();
Remote stub = (Remote) ((MarshalledObject) new ObjectInputStream(sock.getInputStream()).readObject()).get();