場景:java RMI 在服務端者啟動360 wifi共享,報錯java.rmi.ConnectException: Connection refused to host: xx。
也就是服務端在調用時使用了wifi共享網卡的地址。此地址在RMI客戶端pc上無法ping通。(因為沒有連接此wifi。當然RMI客戶端pc如果連接此wifi是不會報錯的)。
想關資料:
http://docs.huihoo.com/java/rmi/whitepage/index.html
比較全的解釋RMI的英文資料:http://docs.oracle.com/javase/1.5.0/docs/guide/rmi/faq.html#netunknownhost
http://www.tkk7.com/shaolijun/archive/2007/05/22/119213.html
測試代碼:
(一)服務端:
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* rmi remote 接口
* @author joe
* @2014-12-5 @上午11:49:10
*/
public interface RmiInterface extends Remote{
public String say(String name) throws RemoteException;
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class RmiServer extends UnicastRemoteObject implements RmiInterface{
private static final long serialVersionUID = 1L;
protected RmiServer() throws RemoteException {
super();
}
public String say(String name) throws RemoteException {
return "hello,"+name;
}
}
public static void main(String[] args) throws MalformedURLException, RemoteException, AlreadyBoundException {
RmiServer server=new RmiServer();
LocateRegistry.createRegistry(8808);
Naming.rebind("http://10.10.XX.XX:8808/SAMPLE-SERVER", server);
}
(二)客戶端 調用服務:
public static void main(String[] args) throws Exception {
RmiInterface server=(RmiInterface) Naming.lookup("http://10.10.116.XX:8808/SAMPLE-SERVER");
System.out.println(server.say("張三"));
}
此時報錯,java.rmi.ConnectException: Connection refused to host: 192.168.23.X。
RMI的調用原理基本如下:
大致翻譯如下:首先客戶端必須通過Naming.lookup得到服務端服務的一個指針或者叫指針,一旦擁有的這個應用,客戶端將使用服務的引用里面包含的主機名(ip)和端口來訪問服務。
也就是說:雖然我們就服務端的IP和端口去
Naming.lookup("http://10.10.116.XX:8808/SAMPLE-SERVER");,但是服務端返回的服務的引用里面包含的ip并不是lookup時的ip。
官方說法:
【In many versions of the JDK (all versions of the JDK except in v1.1 and the latest releases), Java RMI may default to using an unresolvable server hostname (for example: unqualified names, Windows Internet Naming Service (WINS) names, or unqualified DHCP names). When a Java RMI client invokes a remote method using a reference that contains an unresolvable server hostname, the client will throw an UnknownHostException
.】In order to generate functional remote references, Java RMI servers must be able to supply a fully qualified hostname or IP address that is resolvable from all Java RMI clients (an example of a fully qualified hostname is foo.bar.com). If a Java RMI program provides a remote callback operation, then that program serves a Java RMI object and consequently, must be able to determine a resolvable hostname to use as its server hostname in the remote references it passes to Java RMI clients. VMs that make calls to applets that serve remote objects may throwUnknownHostException
s because the applet has failed to provide a usable server hostname.
If your Java RMI application throws an UnknownHostException
, you can look at the resulting stack trace to see if the hostname that the client is using to contact its remote server is incorrect or not fully qualified.【 If necessary, you can set the java.rmi.server.hostname
property on the server to the correct IP address or hostname of the server machine and Java RMI will use this property's value to generate remote references to the server.】
解決辦法就是在服務端發布注冊服務的之前設置:
System.setProperty("java.rmi.server.hostname", 指定IP);
對應到本文例子就是:
public static void main(String[] args) throws MalformedURLException, RemoteException, AlreadyBoundException {
RmiServer server=new RmiServer();
System.setProperty("java.rmi.server.hostname", 指定IP);
LocateRegistry.createRegistry(8808);
Naming.rebind("http://10.10.116.74:8808/SAMPLE-SERVER", server);
}
但是此時還是報相同的錯沒法訪問,百思不得其解,原來
java.rmi.server.hostname的設置必須在服務對象創建之前。
public static void main(String[] args) throws MalformedURLException, RemoteException, AlreadyBoundException {
System.setProperty("java.rmi.server.hostname", 指定IP);
RmiServer server=new RmiServer();
LocateRegistry.createRegistry(8808);
Naming.rebind("http://10.10.116.74:8808/SAMPLE-SERVER", server);
}
為什么呢:
RmiServer 這個實現類使用了UnicastRemoteObject去聯接RMI系統。在我們的例子中,我們是直接的從UnicastRemoteObject這個類上繼承的,事實上并不一定要這樣做,當然也可以不是從UnicastRmeoteObject上繼承,那必須使用它的exportObject()方法去聯接到RMI。如果一個類繼承自UnicastRemoteObject,那么它必須提供一個構造函數并且聲明拋出一個RemoteException對象。當這個構造函數調用了super(),它久激活UnicastRemoteObject中的代碼完成RMI的連接和遠程對象的初始化。而此時應該已經決定了使用哪個hostname來實例化遠程對象。因此必須在服務對象創建之前指定綁定的hostname。
~~~完。