疯狂

STANDING ON THE SHOULDERS OF GIANTS
posts - 480, comments - 486, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
场景: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.blogjava.net/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("//10.10.XX.XX:8808/SAMPLE-SERVER", server);  
    }

(二)客户端 调用服务:

public static void main(String[] args) throws Exception {
        RmiInterface server=(RmiInterface) Naming.lookup("//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("//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 throwUnknownHostExceptions 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("//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("//10.10.116.74:8808/SAMPLE-SERVER", server);  
    }
为什么呢:
    RmiServer 这个实现类使用了UnicastRemoteObject去联接RMI系统。在我们的例子中,我们是直接的从UnicastRemoteObject这个类上继承的,事实上并不一定要这样做,当然也可以不是从UnicastRmeoteObject上继承,那必须使用它的exportObject()方法去联接到RMI。如果一个类继承自UnicastRemoteObject,那么它必须提供一个构造函数并且声明抛出一个RemoteException对象。当这个构造函数调用了super(),它久激活UnicastRemoteObject中的代码完成RMI的连接和远程对象的初始化。而此时应该已经决定了使用哪个hostname来实例化远程对象。因此必须在服务对象创建之前指定绑定的hostname。

~~~完。

只有注册用户登录后才能发表评论。


网站导航: