RMI가 특정 포트 세트만 사용하는지 어떻게 확인합니까?

StackOverflow https://stackoverflow.com/questions/56687

  •  09-06-2019
  •  | 
  •  

문제

우리 애플리케이션에서는 매우 다양한 방식으로 클라이언트-서버 통신을 위해 RMI를 사용하고 있습니다.

  1. 서버에서 클라이언트로 데이터를 푸시하여 표시합니다.
  2. 클라이언트에서 서버로 제어 정보를 보냅니다.
  3. 서버에서 클라이언트로 다시 연결되는 제어 메시지 코드 경로의 콜백(사이드바 참고 - 이는 일부 레거시 코드의 부작용이며 장기적인 의도가 아닙니다).

우리가 원하는 것은 모든 RMI 관련 코드가 알려진 지정된 포트 인벤토리만 사용하도록 하는 것입니다.여기에는 레지스트리 포트(일반적으로 1099로 예상됨), 서버 포트 및 콜백으로 인한 모든 포트가 포함됩니다.

우리가 이미 알고 있는 내용은 다음과 같습니다.

  1. LocateRegistry.getRegistry(1099) 또는 Locate.createRegistry(1099)는 레지스트리가 1099에서 수신 대기하는지 확인합니다.
  2. UnicastRemoteObject 생성자/exportObject 정적 메소드를 포트 인수와 함께 사용하면 서버 포트가 지정됩니다.

이러한 사항은 본 문서에서도 다룹니다. 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);
    }
}
// ....
}

따라서 양방향 연결을 동일한 포트 집합에 유지하기 위해 남은 유일한 것은 클라이언트 측으로 다시 호출하고 있음을 인식하는 일부 논리입니다.이러한 상황에서는 원격 개체에 대한 팩터리 메서드가 콜백 매개 변수가 true로 설정된 RemoteObjectWrapper 생성자를 맨 위에서 호출하는지 확인하세요.

나는 클라이언트 콜백을 사용하여 RMI 서버/클라이언트 아키텍처를 구현하는 데 다양한 문제를 겪고 있습니다.내 시나리오는 서버와 클라이언트가 모두 방화벽/NAT 뒤에 있다는 것입니다.결국 나는 완전히 작동하는 구현을 얻었습니다.제가 한 주요 작업은 다음과 같습니다.

서버 측, 로컬 IP:192.168.1.10.공용(인터넷) IP 80.80.80.10

방화벽/라우터/로컬 서버 PC에서 포트 6620을 엽니다.방화벽/라우터/로컬 서버 PC에서 포트 1099를 엽니다.라우터/NAT에서 포트 6620 ~ 192.168.1.10:6620의 수신 연결을 리디렉션으로 Router/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 공용(인터넷) 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 ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top