Java RMI:在特定的网络地址和端口上创建服务器

Java RMI:在特定的网络地址和端口上创建服务器,java,rmi,Java,Rmi,我想在我以编程方式选择的端口和网络接口上创建rmi服务器(无jvm设置)。例如,我需要rmi服务器侦听接口127.0.0.1和端口2525。我读过互联网上的信息,这就是我最终找到的解决方案 class ServerSocketFactory implements RMIServerSocketFactory, Serializable { public ServerSocket createServerSocket(int port) throws IOException {

我想在我以编程方式选择的端口和网络接口上创建rmi服务器(无jvm设置)。例如,我需要rmi服务器侦听接口127.0.0.1和端口2525。我读过互联网上的信息,这就是我最终找到的解决方案

class ServerSocketFactory implements RMIServerSocketFactory, Serializable {

    public ServerSocket createServerSocket(int port) throws IOException
    {
        ServerSocket server = new ServerSocket(2525, 0, InetAddress.getByName("127.0.0.1"));
        return server;
    }
}
这就是我创建注册表的方式

registry = LocateRegistry.createRegistry(2525,null,new ServerSocketFactory());
然而,我有一个例外:

java.rmi.server.ExportException: Port already in use: 2525; nested exception is: 
    java.net.BindException: Address already in use
    at sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:341)
我不明白为什么如果在createRegistry中使用ServerSocketFactory,还会有一个端口参数。我犯了什么错


注意:这不是重复的,因为它是关于选择网络接口而不是端口。

BindException
表示端口已在使用中。因此,您正在两次运行此代码,或者其他内容正在端口上侦听

您不必两次指定端口号。只需使用作为参数提供给
createServerSocket()
的端口号即可。它将是导出时指定的端口号,或为零

如果要使用此套接字工厂的多个实例,则需要在套接字工厂类中实现
equals()
,以便该类的所有实例都返回
true

注意:RMIServerSocketFactory实现不需要是可序列化的。事实上,除了绑定到127.0.0.1之外,您根本不需要
RMIServerSocketFactory
实现类。不清楚您为什么要这样做,因为这意味着您在同一个主机中执行所有RMI,这是非常徒劳的

总之,您需要:

registry = LocateRegistry.createRegistry(2525);

如果您需要远程对象(包括注册表)只在一个本地IP地址而不是所有IP地址上侦听,则此时您需要一个
RMIServerSocketFactory

public class MyRMIServerSocketFactory implements RMIServerSocketFactory
{
    private InetAddress address;

    public MyRMIServerSocketFactory(InetAddress address)
    {
        this.address = address;
    }

    @Override
    public ServerSocket createServerSocket(int port) throws IOException
    {
        return new ServerSocket(port, 0, address);
    }

    @Override
    public boolean equals(Object that)
    {
        return that != null && this.getClass() == that.getClass() && this.address.equals((MyServerSocketFactory)that).address);
    }
}
要使用它:

Registry registry = LocateRegistry.createRegistry(2525, null, new MyServerSocketFactory(address));

此游戏的规则是,当且仅当满足以下条件时,从同一JVM(可以包括注册表)导出的远程对象之间共享端口:

  • 它们都不使用服务器套接字工厂,或者都使用在
    equals()下相等的服务器套接字工厂。
  • 它们都使用相同的端口号,或者零,或者没有端口号,或者在导出第一个对象后都使用零或没有端口号(当它们共享第一个对象的端口时)。请注意,这意味着默认情况下会发生端口共享,前提是满足(1)

  • @如果您认为这里有问题,请说明问题所在。但是,这里不允许随机向下投票给人留下正确答案是错误的印象,从而误导OP。正如我发现的那样,我在创建注册表后执行UnicastRemoteObject.export(obj,2525)时遇到了这个异常。您能告诉我如何将对象导出到我用createRegistry()创建的套接字吗?老实说,糟糕的API。你做到了。不清楚你在问什么。当然,如果想要端口共享,注册表和远程对象必须在同一个JVM中,而且它们也必须使用相同的套接字工厂。不清楚您认为API有什么问题。我有rmi服务器-我们只谈论服务器,让我们完全忘记客户端。现在我有以下任务:1)为RMI创建所需的套接字2)在该套接字上创建注册表3)将远程导出到该套接字。我完成了任务1和任务2。但是我不知道怎么做3.我只是回答了这个问题。你做到了。正如我在回答中所建议的那样,我将去掉套接字工厂,只需在端口上导出注册表和远程对象。您也不需要第(1)步。@关闭投票者不是该问题的重复。。
    public class MyRMIServerSocketFactory implements RMIServerSocketFactory
    {
        private InetAddress address;
    
        public MyRMIServerSocketFactory(InetAddress address)
        {
            this.address = address;
        }
    
        @Override
        public ServerSocket createServerSocket(int port) throws IOException
        {
            return new ServerSocket(port, 0, address);
        }
    
        @Override
        public boolean equals(Object that)
        {
            return that != null && this.getClass() == that.getClass() && this.address.equals((MyServerSocketFactory)that).address);
        }
    }
    
    Registry registry = LocateRegistry.createRegistry(2525, null, new MyServerSocketFactory(address));
    
    public MyRemoteObject(int port, InetAddress address) throws RemoteException
    {
        super(port, null, new MyServerSocketFactory(address);
    }