解決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創建,並沒有再次在程序中產生如上報錯,如果有看到文章的朋友遇到了同樣的問題,歡迎提出討論和質疑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM