참고
RMI: Remote Method Invocation
- 로컬 컴퓨터에서 원격 컴퓨터의 메서드를 호출하는 기술
- 내부적으로는 TCP방식의 소켓프로그래밍이 돌고 있는 것
장점
- 구현하기 쉽다
- 신뢰성이 보장된다
- Java 플랫폼을 사용한다.
아키텍쳐
Stub (클라이언트)
- 실제 통신작업을 처리하는 객체
- 소켓 및 스트림과 관련된 작업을 처리
- 클라이언트에서 호출하려고 하는 메소드들을 가지고 있는 객체인척 하는 객체
Skeleton (서버)
- 소켓 연결을 통해 클라이언트 보조 객체(stub)에 보낸 요청을 받아서 실제 서비스 객체에 있는 메서드를 호출
- 서비스 객체로부터 리턴값을 받아서 포장해서 다시 클라이언트 보조 객체(Stub)로 보낸다.
방식
- 클라이언트가 서버의 registry에 등록된것을 lookup()으로 찾아낼 수 있다
- 찾아낸 정보를 가지고 클라이언트가 원격으로 호출할 수 있음 (멀리있는 시스템에서 실행된 결과를 받아낼 수 있음)
예제
TestVO.java
- RMI에서 데이터 전달용으로 사용할 클래스
- 네트워크로 객체를 보낼 것이기 때문에 직렬화가 필요
- Serializable을 구현해야함
- Serializable: Marker Interface (메서드 하나도 없고 단지 표식만 하는 역할
public class TestVO implements Serializable {
private String testId;
private int testNum;
public String getTestId() {
return testId;
}
public void setTestId(String testId) {
this.testId = testId;
}
public int getTestNum() {
return testNum;
}
public void setTestNum(int testNum) {
this.testNum = testNum;
}
}
FileInfo.java
public class FileInfoVO implements Serializable{
private String fileName;
private byte[] fileData;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public byte[] getFileData() {
return fileData;
}
public void setFileData(byte[] fileData) {
this.fileData = fileData;
}
}
RemoteInterface.java
- 서버와 클라이언트가 서로 가지고 있는 메서드를 공유할 목적으로 원격에서 호출할 수 있도록 만든 인터페이스
- 클라이언트 입장에서는 이 인터페이스만 알고 있으면 원격에 있는 객체를 가지고 오고싶을 때 인터페이스 방식대로 호출만 하면 되서 좋당
- 인터페이스 작성 규칙
- Remote 인터페이스 상속받아야함
- 이 인터페이스에 있는 메소드는 로컬이 아닌 원격에서 사용할 수 있어야 한다는 것을 명시하기 위해
- Remote: 마커 인터페이스
- 무조건 public
- 메소드는 무조건 RemoteException 처리
- 원격처리를 하다가 인터넷상에서 예외가 발생할 수 있기 때문
- 인터페이스 기반 코딩은 유지 보수가 좋다!
public interface RemoteInterface extends Remote {
public int doRemotePrint(String str) throws RemoteException;
public void doPrintList(List<String> list) throws RemoteException;
public void doPrintVO(TestVO vo) throws RemoteException;
public void setFiles(FileInfoVO[] fInfo) throws RemoteException;
}
RemoteServer.java
- 인터페이스 구현 클래스 작성 규칙
- 무조건 public
- RMI용 인터페이스를 구현해야함
- UnicastRemoteObject클래스 상속
- 해당 통신의 하부 구조를 일부 구현해주고 이 클래스가 객체의 직렬화를 만들어주기 때문
- RemoteException 처리를 위해 디폴트 생성자 정의
- 상속받은 UnicastRemoteObject 생성자에서 해당 예외가 발생하기때문
public class RemoteServer extends UnicastRemoteObject implements RemoteInterface {
protected RemoteServer() throws RemoteException {
super;
}
public static void main(String[] args) {
try {
RemoteInterface inf = new RemoteServer();
Registry reg = LocateRegistry.createRegistry(8888);
reg.rebind("server", inf);
System.out.println("RMI서버가 준비되었습니다");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public int doRemotePrint(String str) throws RemoteException {
int length = str.length();
System.out.println("클라이언트에서 보내온 메시지 : " + str);
System.out.println("출력 끝...");
}
@Override
public void doPrintList(List<String> list) throws RemoteException {
System.out.println("클라이언트에서 보낸 List값들...");
for (int i = 0; i < list.size(); i++) {
System.out.println((i+1)+"번째 : " + list.get(i));
}
System.out.println("List 출력 끝...");
}
@Override
public void doPrintVO(TestVO vo) throws RemoteException {
System.out.println("클라이언트에서 보내온 TestVO객체의 값 출력");
System.out.println("testId : " + vo.getTestId());
System.out.println("testNum : " + vo.getTestNum());
System.out.println("TestVO객체 출력 끝...");
}
}
@Override
public void setFiles(FileInfoVO[] fInfo) throws RemoteException {
FileOutputStream fos = null;
String dir = "d:/C_Lib/";
System.out.println("파일 저장 시작...");
for (int i = 0; i < fInfo.length; i++) {
try {
fos = new FileOutputStream(dir + fInfo[i].getFileName());
fos.write(fInfo[i].getFileDate());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("파일 저장 완료...");
}
RemoteClient.java
- RMI 규칙
- 클라이언트의 프로젝트에도 서버의 패키지와 같은 구조로 interface와 VO파일이 있어야 한다
- Client가 interface를 알아야 Server객체를 잘 이용할 수 있음
- 패키지명, 클래스명 전부 동일하게
public class RemoteClient {
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.getRegistry("127.0.0.1",8888);
RemoteInterface inf = (RemoteInterface) reg.lookup("server");
int a = inf.doRemotePrint("떤땡님 졸려요");
System.out.println("반환값=> " + a );
System.out.println("---------------------");
List<String> list = new ArrayList<String>();
list.add("꽉자바");
list.add("자바칩프라푸치노");
list.add("가나다라자바사");
inf.doPrintList(list);
System.out.println("List 호출 끝...");
System.out.println("---------------------");
TestVO vo = new TestVO();
vo.setTestId("담임땜");
vo.setTestNum(1004);
inf.doPrintVO(vo);
System.out.println("VO 출력 메서드 호출 끝...");
System.out.println("---------------------");
File[] files = new File[2];
files[0] = new File("d:/D_Other/Tulips.jpg");
files[1] = new File("d:/D_Other/out.txt");
FileInfoVO[] fInfo = new FileInfoVO[files.length];
FileInputStream fis = null;
for (int i = 0; i < files.length; i++) {
int len = (int) files[i].length();
fis = new FileInputStream(files[i]);
byte[] data = new byte[len];
fis.read(data);
fInfo[i] = new FileInfoVO();
fInfo[i].setFileName(files[i].getName());
fInfo[i].setFileData(data);
}
inf.setFiles(fInfo);
System.out.println("파일 전송 작업 끝...");
System.out.println("---------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
결과