티스토리 뷰

예전에 자바 rmi 할때 삽질도 많이 하고 시간 낭비도 많이 했다. 그 원인은 잘못된 블로거들이 자바 rmi 에 관한글을 대충 써서 그런것이다. 좀 여러사람 힘들게 하지 말고 이왕 글 쓸거면 여러사람 힘들게 하지말고 제대로 쓰길 바란다.

이번에도 역시나 엉망인 블로그 글 때문에 삽질과 시간을 많이 낭비했다. 그중 가장 좋은 블로그를 찾았다. 그리고 성공했다.

아래 URL 을 참고로 살을 더 붙여봤다.

Java RMI 예제 코드

Java 1.2 때 RMI 를 해봤었는데 한참 삽질했던 기억이…

이번 프로젝트를 하면서 기억을 더듬으며 샘플 코드를 작성해 보았습니다.

예나 지금이나 역시나 삽질을 했지만 -_-;;

다시는 삽질을 하지 않으리라 다짐하며 블로그에 글을 남겨봅니다.

부디 RMI 코드를 작성하는 다른 분들에게도 많은 도움이 되었으면 합니다.

옛날의 RMI 방식과 현재 바뀐 점이 있다면 stub/skeleton 중 서버쪽 인스턴스인 skeleton 이 없어졌다는 것입니다. (삽질 요소 중 하나가 사라졌네요 ㅋㅋㅋ)

다음의 순서로 진행됩니다.

1. RMI 코드 작성

2. RMI 스텁 (stub) 생성

3. RMI 서버 실행

4. RMI 클라이언트 실행

1. RMI 코드 작성

코드는 이클립스 프로젝트 통째로 첨부하였으므로 이클립스에서 바로 import 해서 테스트 해보셔도 됩니다.

# RSInterface.java

package test.rmi;

import java.rmi.Remote;

import java.rmi.RemoteException;

/**

* RMI 인터페이스

*/

public interface RSInterface extends Remote { // 반드시 Remote  상속

    /**

     * 클라이언트에서 호출할 RMI 메소드. 메시지를 콘솔에 출력한다.

     * @param msg 프린트할 메시지

     * @throws RemoteException 반드시 RemoteException  던지도록 선언되어야 한다.

     */

    public void println(String msg) throws RemoteException;

}

# RSMain.java

package test.rmi;

import java.rmi.Naming;

import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;

/**

* RMI 서버 메인 클래스.

* RMI 서버 구현을 위해서는 UnicastRemoteObject  상속하면 가장 쉽게 만들  있다고 한다.

* 내가 만든 RSInterface  구현하는 것도 중요!

*  클래스로 RSMain.class  RSMain_Stub.class  만든다.

*/

public class RSMain extends UnicastRemoteObject implements RSInterface {

    private static final long serialVersionUID = 1L;

    private static final String BIND_NAME = "rs";

    /**

     * 메인 함수. 바인딩하고 무한 대기한다.

     */

    public static void main(String[] args) {

        System.out.println("[RMI-Server] START");

        try {

            Naming.rebind(BIND_NAME, new RSMain());

            while (true);

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            try { Naming.unbind(BIND_NAME); } catch (Exception e) {}

        }

        System.out.println("[RMI-Server] EXIT");

    }

    /**

     * UnicastRemoteObject  상속했다면 반드시

     * RemoteException  던지도록 생성자를 만들어 줘야 한다.

     * @throws RemoteException

     */

    public RSMain() throws RemoteException {

        super();

    }

    @Override

    public void println(String msg) throws RemoteException {

        // RMI 호출이 되면 메시지를 프린트한다.

        System.out.println("[RMI-SERVER] " + msg);

    }

}

 

# RCMain.java

package test.rmi;

import java.rmi.Naming;

/**

* 클라이언트 메인 클래스.

*/

public class RCMain {

    /**

     * 등록된 이름으로 RSInterface  가져오고 RMI 메소드 호출

     */

    public static void main(String[] args) {

        try {

            String url = "rmi://127.0.0.1/rs";

            RSInterface rs = (RSInterface)Naming.lookup(url);

            for (int i = 0; i < 10; i++) {

                rs.println(i + "번째 클라이언트 호출");

                Thread.sleep(1000);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

2. RMI 스텁 (stub) 생성

RMI 스텁 클래스 생성과 생성한 것을 클라이언트 프로그램의 classpath 디렉토리에 포함해야 합니다. 스텁 클래스 생성만 해놓고 복사를 안해서 삽질하게 되는 상황들이 종종 있었습니다. 개발 시간 및 스트레스를 증폭시킬 수 있는 이런 부분들은 스크립트나 IDE 툴을 이용해 자동화 시켜주는 것이 바람직합니다.

RMI 이클립스 플러그인도 있다고 하는데 이 프로젝트는 테스트를 위한 것이기에 기본적인 builder 의 기능을 설정해 사용해 봤습니다. 다음 설정은 이클립스에서 클래스 파일을 빌드할 때에 RMI 스텁 클래스를 생성하는 rmic.exe 를 같이 수행하도록 설정합니다.

첨부 된 파일을 Package Explorer 에서 마우스 우클릭 > Import 클릭 > General > Existing Projects into Workspace 클릭

이렇게 임포트를 하면 아래 처럼 RMIClient 와 RMIServer 프로젝트 2개가 생성될 것이다.

1) 우선 RMIServer 프로젝트의 설정을 열어 Builders 탭으로 갑니다. 거기서 New 버튼 클릭! 

2) RMIC 선택

 

3) RMIC 실행 설정

Location 은 설치된 JDK 에 rmic.exe 를 지정합니다. 중요한 것은 Argument 에서 –d 옵션인데 RMIClient 프로젝트의 classes 디렉토리로 생성되게 합니다.

 

 

4) 자동 빌드시에 같이 수행되도록 Build Options 탭에서 수정해 줍니다. 즉, Clean 해도 빌드가 되고 수정 후 저장을 해도 빌드가 된다.

설정 후 프로젝트를 클린하고 다시 빌드해보시면 RMIClient 프로젝트의 classes 디렉토리에 스텁 클래스가 들어가 있는 것을 볼 수 있습니다. 헷갈리에 RMIServer를 설정해서 RMIServer로 갔는데 그게 아니고 RMIClient로 가야한다.

위 정보가 본인의 환경하고 맞지 않으면 에러를 뱉어낼 것이다. import를 하지 않았으면 본인의 환경하고 맞춰야 한다.

또한 import하지 않았다면 RMIClient 프로젝트 Properties -> Java Build Path -> Projects 탭에가서 RMIServer를 추가해줘야 한다. 안그러면 RMIClient 프로젝트의 RCMain.java 에서 RSInterface에 빨간 밑줄이 그어지면서 못찾는다.

 

3. RMI 서버 실행

RMI 서버는 두가지의 실행 단계가 있습니다.

첫째는 rmiregistry 실행, 그 다음이 내가 만든 Java서버 프로그램 실행입니다.

1) rmiregistry 실행

 rmiregistry 를 실행할 때는 java 를 실행할 때와 마찬가지로 classpath 를 정확히 지정해 주고 실행을 해야 한다는 것입니다. 다만 인자의 설정은 조금 다릅니다. –J 를 앞에 붙여주어서 java 를 실행할 때의 파라미터를 지정을 해줍니다. (rmiregistry 안에서 java 를 실행할 것이라는 유추를 해볼 수 있습니다.)

D:\workspace\RMIServer>rmiregistry -J-classpath –Jclasses

rmiregistry 를 실행하면 위와 같이 가만이 커서만 깜빡이고 있습니다.

이 때 Ctrl + C 를 눌러서 끄면 안됩니다. 실행된 채로 내가 만든 서버를 실행해야 합니다.

2) Java 서버 실행

D:\workspace\RMIServer>java -classpath classes test.rmi.RSMain

[RMI-Server] START

위와 같이 서버가 실행되고 있는 것을 볼 수 있습니다.

4. RMI 클라이언트 실행

이제 클라이언트 프로그램만 실행하면 됩니다. 이클립스에서 RMIClient 프로젝트의 RCMain.java 로 가서 Ctrl + F11 을 눌러 실행합니다.

클라이언트 코드는 RMI 메쏘드만 호출하는 코드만 있으므로 이클립스에서 바로 실행을 해보았습니다. 정상적으로 동작한다면 위에서 실행한 Java 서버 를 띄운 콘솔에서 다음과 같은 메시지가 뜨는 것을 볼 수 있습니다.

RMITest.zip
0.01MB

D:\workspace\RMIServer>java -classpath classes test.rmi.RSMain

[RMI-Server] START

[RMI-SERVER] 0번째 클라이언트 호출

[RMI-SERVER] 1번째 클라이언트 호출

[RMI-SERVER] 2번째 클라이언트 호출

[RMI-SERVER] 3번째 클라이언트 호출

[RMI-SERVER] 4번째 클라이언트 호출

[RMI-SERVER] 5번째 클라이언트 호출

[RMI-SERVER] 6번째 클라이언트 호출

[RMI-SERVER] 7번째 클라이언트 호출

[RMI-SERVER] 8번째 클라이언트 호출

[RMI-SERVER] 9번째 클라이언트 호출

 

java.rmi.ConnectException: Connection refused to host: 1.1.1.1; nested exception is: 
java.net.ConnectException: Connection refused: connect

글이 길어져서 다음글에서는 위의 에러를 살펴보겠다.

https://selfdevelope.tistory.com/525

참고:

https://m.blog.naver.com/PostView.nhn?blogId=declin&logNo=50167490727&proxyReferer=https%3A%2F%2Fwww.google.com%2F

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함