"java.rmi.ConnectException: Connection refused to host" when exporting an object client-side

23,029

Solution 1

Ok, finally solved it. The problem was, that I was passing the same java.rmi.server.hostname argument in both programs, that means that both client and server got the address of the server here. It turned out, that if the client wants to export its own objects, it has to provide its own IP to the java.rmi.server.hostname. This way everything works fine.

So, to sum it up, I had to give 3 arguments to the server:
RMI_IP RMI_port server_hostname

And 3 analogous arguments to the client:
RMI_IP RMI_port client_hostname

Solution 2

Here's what I'd do to get to the bottom of this.

  1. Run your server app
  2. find out what port it's using for RMI (it's ephemeral, so it should change for each process you create of the server).

    • find the PID with ps -ef
    • then netstat -anp|grep This should then give you the port number. And it should be bound to 0.0.0.0
  3. on the client VM, use nc or telnet to verify you can connect to the port. If this fails, you probably have a firewall/iptables issue.

  4. use wireshark to verify your client is actually attempting to connect to the port/ip combo you learned from step #2.

Remember, just because you can ping, doesn't mean you can connect to a given port. Also check "iptables -L".

Also, Naming says not to put the scheme component of the URL. The format should be //host:port/name, so you should check that as well.

Share:
23,029
Wojtek
Author by

Wojtek

Updated on July 09, 2022

Comments

  • Wojtek
    Wojtek almost 2 years

    I'm stuck with that for at least 5 hours now and have no other resort but to ask here. I'm writing an RMI application. I'd like the server to bind a remote object (NoteBoardImpl here), that will be looked-up by the client. Client passes its listener (NoteBoardListener here) to the server, the listener is also a remote object exported by the client.

    I've prepared a simple SSCCE here, so I really hope somebody can look into it. All the classes are in the same folder the default package. I know it's discouraged and I should've split the application in three jars, but I wanted to keep it as simple as possible here.

    Remote interfaces:

    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    public interface INoteBoard extends Remote {
        public void test(INoteBoardListener listener) throws RemoteException;
    }
    


    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    public interface INoteBoardListener extends Remote {
        public void onNewText(String text) throws RemoteException;
    }
    

    Interfaces implementations:

    import java.rmi.RemoteException;
    
    public class NoteBoardImpl implements INoteBoard {
        @Override
        public void test(INoteBoardListener listener) throws RemoteException {
            listener.onNewText("server call the listener");
        }
    }
    


    import java.rmi.RemoteException;
    
    public class NoteBoardListener implements INoteBoardListener {
        @Override
        public void onNewText(String text) throws RemoteException {
            System.out.println(text);
        }
    }
    

    Client and server:

    import java.rmi.Naming;
    import java.rmi.server.UnicastRemoteObject;
    
    public class Client {
        public static void main(String[] args) {
            if (args.length < 2) {
                System.out.println("2 arguments required:\nRMI_IP RMI_port");
                return;
            }
            System.setProperty("java.rmi.server.hostname", args[0]);
            try {
                INoteBoard nb = (INoteBoard) Naming.lookup(String.format("rmi://%s:%s/note", args[0], args[1]));
                INoteBoardListener l = (INoteBoardListener) UnicastRemoteObject.exportObject(new NoteBoardListener(), 0);
                nb.test(l);
                l.onNewText("client invokes listener");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    


    import java.rmi.Naming;
    import java.rmi.server.UnicastRemoteObject;
    
    public class Server {
        public static void main(String[] args) {
            if (args.length < 2) {
                System.out.println("2 arguments required:\nRMI_IP RMI_port");
                return;
            }
            System.setProperty("java.rmi.server.hostname", args[0]);
            try {
                INoteBoard noteBoard = (INoteBoard) UnicastRemoteObject.exportObject(new NoteBoardImpl(), 0);
                Naming.rebind(String.format("rmi://%s:%s/note", args[0], args[1]), noteBoard);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    I've tried to simulate a distributed system for testing purposes and ran the client on a virtual machine. The host-VM network has the following specs - host IP = 192.168.56.1, VM IP = 192.168.56.101.
    First I ran the client and the server locally, using the following commands (having started rmiregistry 1099 beforehand). The working directory is the project's root and the compiled classes are in bin directory:
    java -cp bin -Djava.rmi.server.codebase=http://student.agh.edu.pl/~grajewsk/bin/ Server 192.168.56.1 1099
    java -cp bin Client 192.168.56.1 1099
    And it worked.

    Then I ran the client program on the VM using the same command and here's the exception I got:

    java.rmi.ConnectException: Connection refused to host: 192.168.56.1; nested exception is: 
        java.net.ConnectException: Connection refused
        at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
        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.invoke(UnicastRef.java:128)
        at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
        at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
        at sun.proxy.$Proxy0.test(Unknown Source)
        at Client.main(Client.java:14)
    Caused by: java.net.ConnectException: Connection refused
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
        at java.net.Socket.connect(Socket.java:546)
        at java.net.Socket.connect(Socket.java:495)
        at java.net.Socket.<init>(Socket.java:392)
        at java.net.Socket.<init>(Socket.java:206)
        at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
        at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146)
        at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
        ... 7 more
    

    Notice how the object is successfully looked-up in the server's registry, then the client-side remote object is exported (also with success) and the execution breaks in the 14-th line, where I'm trying to invoke a method on the server-side object passing the client-side object.

    I have no firewalls on either of the systems, pings in both directions go flawlessly. I know that there must be some conceptual problem here and certainly I misunderstood something about the RMI. I'd very much appreciate your help.

    The binary codebase is on my student's server, as well as the source code. Thank you in advance!