• 解决RMI的Connection refused to host: 127.0.0.1


    1、前言

    在学习RMI原理时,遇到Connection refused to host: 127.0.0.1; 这么一个问题,网络上关于该问题的解决有很多种,贴出遇到的两个解决
    1、由于解析java通过host获取ip时获取到127.0.1.1,然后需要修改hosts文件。或者需要在java中指定
    2、java.rmi.server.hostname识别有问题,会解析到127.0.0.1,需要加入这么一行代码System.setProperty("java.rmi.server.hostname","所部属的服务器公网Ip地址");
    当然,以上的办法都没能解决我的问题。贴出我的代码

    package com.weblogictest.rmitest;
    
    import com.Hello;
    
    import java.net.Inet4Address;
    import java.rmi.AlreadyBoundException;
    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;
    //ServerDemo类运行后报错
    public class ServerDemo {
        public static void main(String[] args) {
            try {
                Naming.bind("rmi://127.0.0.1:1089/Test",new RemoteQing());
                System.out.println("Server is ok");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //App类运行后无报错
    class App {
    
        public static void test(String[] args) {
            try {
                Registry registry = LocateRegistry.createRegistry(1089);
                registry.bind("hello", new RemoteQing());
                System.out.println("Server is ok");
            } catch (RemoteException e) {
                e.printStackTrace();
            } catch (AlreadyBoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    2、报错提示

    报错提示显示127.0.0.1的连接被拒绝,如下

    java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: 
    	java.net.ConnectException: Connection refused: connect
    	at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:623)
    	at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
    	at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
    	at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:342)
    	at sun.rmi.registry.RegistryImpl_Stub.bind(RegistryImpl_Stub.java:65)
    	at java.rmi.Naming.bind(Naming.java:128)
    	at com.weblogictest.rmitest.ServerDemo.main(ServerDemo.java:16)
    Caused by: java.net.ConnectException: Connection refused: connect
    	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
    	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    	at java.net.Socket.connect(Socket.java:606)
    	at java.net.Socket.connect(Socket.java:555)
    	at java.net.Socket.<init>(Socket.java:451)
    	at java.net.Socket.<init>(Socket.java:228)
    	at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
    	at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:148)
    	at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:617)
    	... 6 more
    

    3、解决思路

    1、由于这方面没有一定的先验知识,我按照经验排查了防火墙,代理,端口占用之后问题都没解决
    2、我开始从代码层面探究成功类和失败类的区别,首先是调试跟入了函数内部,Naming.bind内部是这样的

       public static void bind(String name, Remote obj)
            throws AlreadyBoundException,
                java.net.MalformedURLException,
                RemoteException
        {
            ParsedNamingURL parsed = parseURL(name);
            Registry registry = getRegistry(parsed);
    
            if (obj == null)
                throw new NullPointerException("cannot bind to null");
    
            registry.bind(parsed.name, obj);
        }
    

    3、我们注意到它最后同样采用了registry.bind,和App中最后的执行方法没有区别,parsed.name等同于App类中的Hello
    4、既然如此,为什么最后还是产生报错,经过更深入的调试,我进入到了他们的底部,但是并没有结果产生
    5、到了这里,我冷静思考,想要观察registry,发现这样的情况如图
    非Naming.bind时registry的最外层是一个Registryimpl

    而Naming.bind时registry的最外层是一个Registryimpl_stub

    6、在这之前,我了解到在RMI过程中,stub是客户端的存根,服务端类似的代理应该是Skelton,很显然,这不符合服务端的模式,
    同时,我在之前排查端口的过程中发现当程序运行报错后,对应的端口仍旧是开启的,除非主动关闭java程序。
    7、有了以上经验,我做出如下判断,Naming.bind创建了一个对于远程服务端的绑定,我们输入一个未开放对应RMI服务端的ip和端口,自然就被refused,
    所以要想成功创建服务端,我们想要使用Naming.bind似乎是不可以的,只能使用LocateRegistry.createRegistry(1089);
    8、但其实我们可以这样实现

    LocateRegistry.CreateRegistry(1089);
    Naming.bind("rmi://127.0.0.1:1089/Test",new RemoteQing());
    

    9、为什么可以如此呢,当我深入调试时,发现Naming.bind最终调用了LocateRegistry.getRegistry(1089);
    注意,是get,不是create,就是基于此,Naming就会连接服务端,而不会产生客户端,可以这样理解。于是就产生了我们上述的报错
    10、基于上面给出的经验和试错过程我最终理解了正确的RMI创建,并没有再次在程序中产生如上报错,如果有看到文章的朋友遇到了同样的问题,欢迎提出讨论和质疑

  • 相关阅读:
    每日一练4
    每日一练3
    每日一练2 字符串逆序输出
    每日一练1
    python全局变量
    python __file__ 与argv[0]
    python 静态方法和类方法
    常用的python库(不断更新)
    django 自定义用户user模型的三种方法
    实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(三)
  • 原文地址:https://www.cnblogs.com/qianxinggz/p/13277093.html
Copyright © 2020-2023  润新知