6-1.(4) 네트워킹_Java RMI

zhyun·2020년 10월 15일
0

HighJava

목록 보기
52/67

RMI 참고

RMI

  • Remote Method Invocation
  • 로컬 컴퓨터에서 원격 컴퓨터의 메서드를 호출하는 기술
  • 분산되어 존재하는 객체 간의 메시지 전송(메소드 호출 포함)을 가능하게 하는 프로토콜
  • RMI 자체는 분산개체 간의 통신을 구현하는 모든 프로토콜을 의미함

RMI 장점 (사용 이유)

  • 구현하기 쉽다
    • 네트워크 프로그래밍 => Socket 많이 사용
    • 소켓으로는 프로토콜을 구현하기가 매우 까다롭고 힘듦
    • Java RMISocket 통신 자체를 하부에 숨기고, 상위레벨에서 수행하여
      분산객체 간의 데이터 전송 메서드를 부르는 것과 같은 방법으로 구현
  • 신뢰성이 보장된다
    • 상위 레벨의 통신계층에서 수행하기 때문에! 통신에 대한 신뢰성이 확보되고 또한
      자바 자체에서 제공하는 라이브러리이기때문에!
  • java 플랫폼을 사용한다.
    • 자바의 RMI는 JVM에서 언제든 활용하고 사용 가능
    • 단, 동종의 Java가 아니면 제약이 따름 (ex. sun java와 gun java간의 RMI)

RMI 구조 및 과정

  • Server는 먼저 이름을 가지고 Registry에 bind를 해야함
  • Client원격 참조를 설정하기 위해 레지스터에서 서버 이름을 조회
  • stub은 파라미터를 skeleton에게 직렬화 하여 원격메소드를 호출하고 결과를 다시 직렬화하여 stub에게 돌려준다.

RMI 구성

Server

  • 공통 인터페이스
  • 구현클래스
  • 스켈레톤 클래스
  • 서버실행 클래스

Client

  • 공통 인터페이스
  • 스텁 클래스 : Client만을 의미하는 것
  • 클라이언트 실행 클래스

RMI 서버 작성 순서

1. 공통(원격)인터페이스 작성 (서버, 클라이언트 모두 갖고있어야댐)

  • Remote interface 상속(extends) 받아야 함 (interface가 interface에게 상속 받음- implement)
    • 로컬이 아닌 원격에서 사용할 수 있어야 한다는 것을 명시하기 위해
  • public interface이어야 함
  • 모든 Method(abstract)는 RemoteException 처리해야 함
    • 원격처리를 하다가 인터넷상에서 예외가 발생할 수 있기 때문에!
  • 매개변수 및 리턴 값들은 '직렬화'된 객체(String 객체 등 기본형) 직렬화(Serilizable 인터페이스 구현)로 만들어야 함

10-1.javaRMIServer / RemoteInterface.java

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;
	
}

10-1.javaRMIServer / FileInfoVO, TestVO

  • RMI에서 데이터 전달용으로 사용할 클래스
  • 이 VO 클래스들은 네트워크를 통해 전달되어야 하기 때문에 직렬화가 필요
  • 그래서 Serializable을 구현한 형태로 작성
    TestVO.java
public class TestVO implements Serializable{ //Marker interface (표시)
	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;
	}
}

FileInfoVO.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;
	}
	
}

2.구현 클래스 작성

  • interface를 구현(implements)하고 UnicastRemotObject를 상속(extends) 받아야 함
    • 이것을 상속받아야 RMI interface라고 인식함
    • UnicastRemotObject : 직렬화 가능 클래스
  • 무조건 public
  • 반드시 생성자 만들어야 하고, RemoteExceptionthrows해야함
    • 상속받은 UnicastRemoteObject 생성자에서 해당 예외가 발생하기때문에

10-1.javaRMIServer / RemoteServer.java

public class RemoteServer extends UnicastRemoteObject implements RemoteInterface{ 
	
	//객체 생성 시점에도 문제가 발생할 수 있다 => RemoteServer클래스에 빨간줄 뜸
	//생성자에도 throws 해주면 빨간줄 사라짐
	protected RemoteServer() throws RemoteException {
		super();
	}
	
	public static void main(String[] args) {
		try {
			//1.구현한 RMI용 객체를 클라이언트에서 사용할 수 있도록
			//RMI서버 (Registry)에 등록한다.
			
			//1-1.RMI용 인터페이스를 구현한 원격객체 생성하기
			RemoteInterface inf = new RemoteServer();
			
			//2. 구현한 객체를 클라이언트에서 찾을 수 있도록
			//   'Registry객체'를 생성해서 등록한다.
			
			//2-1. 포트번호를 지정하여 Registry객체 생성(기본포트값 : 1099)
			Registry reg = LocateRegistry.createRegistry(8888);
			
			//3. Registry서버에 제공하는 객체 등록
			//형식) Registry객체변수.rebind("객체의 Alias", 원격객체) Alias:식별자
			// Client는 서버에서 '원격 객체'를 바인딩 시키기 위해서 식별자(Alias) 이용해서 Client에서 lookup함
			reg.rebind("server", inf);
			
			System.out.println("RMI서버가 준비되었습니다.");
			
		} catch (RemoteException ex) {
			ex.printStackTrace();
		}
	}
	
	@Override
	public int doRemotePrint(String str) throws RemoteException {
		int length = str.length();
		System.out.println("클라이언트에서 보내온 메시지 : "+str);
		System.out.println("출력 끝...");
		return length;
	}

	@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());
				
				//클라이언트에서 전달한 파일데이터(byte[])를 서버측에 저장한다.
				fos.write(fInfo[i].getFileData());
				fos.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		System.out.println("파일 저장 완료...");
	}
}

RMI Client 작성하기

RMI Servr에 있는 method를 가지고 와서 실행함

1. 서버의 파일 가져오기

  • 서버에서 만든 파일중
    10-1.javaRMIServer / RemoteInterface.java
    10-1.javaRMIServer / FileInfoVO, TestVO >> 이 파일들을
    10-2.javaRMIClient에 복사함

2. 서버에 기동중인 객체 얻어오기

  • Naming Class의 Lookup Method를 사용하여 서버의 객체 복사본 얻어옴

3. 해당 객체를 통해 Method 사용하기

  • 해당 객체에 존재하는 인터페이스에서 구현된 Method를 원격 지역에서 실행

10-2.JavaRMIClient / RemoteClient.java

public class RemoteClient {
	public static void main(String[] args) {
		try {
			//1.등록된 서버를 찾기 위해 Registry객체를 생성한 후
			//  사용할 객체를 불러온다. => 서버측의 IP와 포트번호, 디폴트시 localhost RMI 기본 포트: 1099
			Registry reg = LocateRegistry.getRegistry("192.168.45.2", 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("광주");
			list.add("인천");
			inf.doPrintList(list);
			System.out.println("List호출 끝");
			System.out.println("------------------------------------------");
			
			TestVO vo = new TestVO();
			vo.setTestId("dditMan");
			vo.setTestNum(123456);
			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/Tulips.jpg");
			
			FileInfoVO[] fInfo = new FileInfoVO[files.length];
			//2개의 파일을 읽어서 byte[]에 담아서 서버측 메서드에 전달하면 된다.
			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);// 파일 내용을 읽어서 byte배열에 저장
				
				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 ex) {
			ex.printStackTrace();
		}
	}
}

RMI registry 대체 클래스

Registry interface

  • Field : REGISTRY_PORT : 특정 포트를 Mapping 시킬 수 있음
  • Method 종류 : interface이므로 실행은 불가능
    • bind(String, Remote), list(), lookup(), rebind(String,Remote), unbind(String)

LocateRegistry class

  • 모두 static method임 : 실행 가능
  • Method 종류 :
    • createRegistry(int) : 포트를 등록(수정)함
    • getRegistry(), getRegistry(int), getRegistry(String), getRegistry(String, int)
profile
HI :)

0개의 댓글