簡體   English   中英

如何確保RMI僅使用一組特定的端口?

[英]How do I ensure that RMI uses only a specific set of ports?

在我們的應用程序中,我們使用RMI以非常不同的方式進行客戶端 - 服務器通信:

  1. 將數據從服務器推送到客戶端以進行顯示。
  2. 從客戶端向服務器發送控制信息。
  3. 來自那些控制消息的回調代碼從服務器返回到客戶端的路徑(側邊欄注釋 - 這是一些遺留代碼的副作用,而不是我們的長期意圖)。

我們想要做的是確保我們所有與RMI相關的代碼僅使用已知的指定端口庫存。 這包括注冊表端口(通常預期為1099),服務器端口以及回調產生的任何端口。

這是我們已經知道的:

  1. LocateRegistry.getRegistry(1099)或Locate.createRegistry(1099)將確保注冊表正在監聽1099。
  2. 將UnicastRemoteObject構造函數/ exportObject靜態方法與port參數一起使用將指定服務器端口。

這些Sun論壇帖子也涵蓋了這些要點。

我們不知道的是:我們如何確保回調產生的客戶端連接回服務器只能連接到指定的端口,而不是默認為匿名端口?

編輯:添加了一個很長的答案,總結了我的發現以及我們如何解決問題。 希望這可以幫助其他有類似問題的人。

第二次編輯:事實證明,在我的應用程序中,我在創建和修改套接字工廠時似乎存在競爭條件。 我曾希望允許用戶在Beanshell腳本中覆蓋我的默認設置。 遺憾的是,在工廠創建第一個套接字后,我的腳本似乎正在運行。 因此,我從默認設置和用戶設置中獲得了混合端口。 將需要做更多的工作,這超出了這個問題的范圍,但我想我會指出它是其他可能不得不在某些時候踏上這些水域的人的興趣點....

您可以使用自定義RMI套接字工廠執行此操作。

套接字工廠為RMI創建套接字,以便在客戶端和服務器端使用,因此如果您自己編寫,則可以完全控制所使用的端口。 客戶端工廠在服務器上創建,序列化,然后發送到客戶端,非常整潔。

這是Sun的一個指南,告訴你如何做到這一點。

您不需要套接字工廠,甚至不需要多個端口。 如果您從服務器JVM啟動注冊表,則可以將端口1099用於所有內容,實際上這是默認情況下會發生的情況。 如果您沒有像在客戶端回調對象中那樣啟動注冊表,則可以在導出時提供端口1099。

關於“回調導致服務器連接的客戶端連接”的問題部分沒有意義。 它們與服務器的原始客戶端連接沒有區別,它們將使用相同的服務器端口。

下面的長答案總結:為了解決我遇到的問題(限制RMI連接兩端的服務器和回調端口),我需要創建兩對客戶端和服務器套接字工廠。

更長的答案隨之而來:

我們對回調問題的解決方案基本上有三個部分。 第一個是對象包裝,它需要能夠指定它用於客戶端到服務器連接而不是用於服務器到客戶端回調。 使用UnicastRemoteObject的擴展使我們能夠指定我們想要使用的客戶端和服務器套接字工廠。 但是,鎖定套接字工廠的最佳位置是遠程對象的構造函數。

public class RemoteObjectWrapped extends UnicastRemoteObject {
// ....
private RemoteObjectWrapped(final boolean callback) throws RemoteException {
  super((callback ? RemoteConnectionParameters.getCallbackPort() : RemoteConnectionParameters.getServerSidePort()),
        (callback ? CALLBACK_CLIENT_SOCKET_FACTORY : CLIENT_SOCKET_FACTORY),
        (callback ? CALLBACK_SERVER_SOCKET_FACTORY : SERVER_SOCKET_FACTORY));
}
// ....
}

因此,第一個參數指定對象期望請求的部分,而第二個和第三個參數指定將在驅動此遠程對象的連接的任一端使用的套接字工廠。

由於我們想限制連接使用的端口,我們需要擴展RMI套接字工廠並鎖定端口。 以下是我們的服務器和客戶端工廠的一些草圖:

public class SpecifiedServerSocketFactory implements RMIServerSocketFactory {
/** Always use this port when specified. */
private int serverPort;
/**
 * @param ignoredPort This port is ignored.  
 * @return a {@link ServerSocket} if we managed to create one on the correct port.
 * @throws java.io.IOException
 */
@Override
public ServerSocket createServerSocket(final int ignoredPort) throws IOException {
    try {
        final ServerSocket serverSocket = new ServerSocket(this.serverPort);
        return serverSocket;
    } catch (IOException ioe) {
        throw new IOException("Failed to open server socket on port " + serverPort, ioe);
    }
}
// ....
}

請注意,上面的服務器套接字工廠確保此工廠僅使用您先前指定的端口。 客戶端套接字工廠必須與相應的套接字工廠配對(或者您永遠不會連接)。

public class SpecifiedClientSocketFactory implements RMIClientSocketFactory, Serializable {
/** Serialization hint */
public static final long serialVersionUID = 1L;
/** This is the remote port to which we will always connect. */
private int remotePort;
/** Storing the host just for reference. */
private String remoteHost = "HOST NOT YET SET";
// ....
/**
 * @param host The host to which we are trying to connect
 * @param ignoredPort This port is ignored.  
 * @return A new Socket if we managed to create one to the host.
 * @throws java.io.IOException
 */
@Override
public Socket createSocket(final String host, final int ignoredPort) throws IOException {
    try {
        final Socket socket = new Socket(host, remotePort);
        this.remoteHost = host;
        return socket;
    } catch (IOException ioe) {
        throw new IOException("Failed to open a socket back to host " + host + " on port " + remotePort, ioe);
    }
}
// ....
}

因此,唯一可以強制您的雙向連接保持在同一組端口上的是一些邏輯,以識別您正在回調客戶端。 在這種情況下,只需確保遠程對象的工廠方法調用RemoteObjectWrapper構造函數,並將callback參數設置為true。

我在使用客戶端回調實現RMI服務器/客戶端體系結構時遇到了各種問題。 我的方案是服務器和客戶端都在防火牆/ NAT之后。 最后,我得到了一個完全正常的實施。 以下是我做的主要事情:

服務器端,本地IP:192.168.1.10。 公共(互聯網)IP 80.80.80.10

在防火牆/路由器/本地服務器PC上打開端口6620.在防火牆/路由器/本地服務器PC上打開端口1099.在路由器/ NAT上將端口6620上的傳入連接重定向到192.168.1.10:6620關於路由器/ NAT重定向傳入端口1099到192.168.1.10:1099上的連接

在實際的程序中:

System.getProperties().put("java.rmi.server.hostname", IP 80.80.80.10);
MyService rmiserver = new MyService();
MyService stub = (MyService) UnicastRemoteObject.exportObject(rmiserver, 6620);
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
registry.rebind("FAManagerService", stub);

客戶端,本地IP:10.0.1.123公共(Internet)IP 70.70.70.20

在防火牆/路由器/本地服務器PC上打開端口1999.在路由器/ NAT上將端口1999上的傳入連接重定向到10.0.1.123:1999

在實際的程序中:

System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
UnicastRemoteObject.exportObject(this, 1999);
MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");

希望這可以幫助。 伊拿克里斯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM