RMI를 사용하는 이유
이번 프로젝트에서는 클라이언트와 서버 간 통신을 위한 여러 가지 방법을 고민했는데, 리치 클라이언트 애플리케이션을 작업하고 있었기 때문에 최종적으로 RMI와 Java 소켓 간의 기술을 선택했는데, 그 중 RMI 유연성이 높지 않습니다. 클라이언트와 서버 모두 Java로 작성되어야 하지만 사용하기가 더 편리합니다. 반면에 Java 소켓은 더 유연하지만 서버와 클라이언트 간의 통신 프로토콜을 정의해야 합니다. 꽤 번거로운 일이었습니다. 여러 번 고민한 끝에 마침내 서버-클라이언트 통신을 위해 RMI를 선택했습니다.
파일 업로드 문제
java-rmi를 사용하는 과정에서 필연적으로 파일 업로드 문제에 직면하게 됩니다. 파일 스트림은 rmi로 전송될 수 없기 때문에(예를 들어 rmi의 메소드 매개변수는 FileInputStream 등이 될 수 없습니다) 절충안을 선택해야 합니다. 가장 좋은 방법은 먼저 FileInputStream을 사용하여 파일을 Byte 배열로 읽은 다음 이 Byte 배열을 RMI 메소드에 매개변수로 전달한 다음 Byte 배열을 서버 측의 outputStream으로 복원하는 것입니다. RMI를 통과함 파일을 전송하자
이는 전송된 데이터의 정확성을 확인할 수 없다는 단점도 있습니다.
아래 예를 들어 설명하겠습니다.
파일클라이언트
다음과 같이 코드 코드를 복사합니다.
패키지 rmiupload;
import java.io.BufferedInputStream;
java.io.파일 가져오기;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
공개 클래스 FileClient {
공개 FileClient() {
// TODO 자동 생성 생성자 스텁
}
공개 정적 무효 메인(String[] args) {
노력하다 {
FileDataService fileDataService = (FileDataService) Naming.lookup("rmi://localhost:9001/FileDataService");
fileDataService.upload("/Users/NeverDie/Documents/test.mp4", new FileClient().fileToByte("/Users/NeverDie/Music/test.mp4"));
} catch (MalformedURLException | RemoteException | NotBoundException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
}
}
//이 방법이 더 중요합니다. 이 방법을 통해 filename이라는 파일이 바이트 배열로 변환됩니다.
private byte[] fileToByte(문자열 파일 이름){
바이트[] b = null;
노력하다 {
파일 파일 = 새 파일(파일 이름);
b = 새 바이트[(int) file.length()];
BufferedInputStream은 = new BufferedInputStream(new FileInputStream(file));
is.read(b);
} 잡기(FileNotFoundException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
} 잡기(IOException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
}
b를 반환;
}
}
파일데이터서비스
패키지 rmiupload;
java.net.URL 가져오기;
import java.rmi.Remote;
import java.rmi.RemoteException;
공용 인터페이스 FileDataService는 Remote{를 확장합니다.
//여기서 파일 이름은 서버측에서 파일이 저장되는 주소여야 합니다.
public void upload(String filename, byte[] file)는 RemoteException을 발생시킵니다.
}
FileDataService_imp
다음과 같이 코드 코드를 복사합니다.
패키지 rmiupload;
import java.io.BufferedOutputStream;
java.io.파일 가져오기;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
java.net.URL 가져오기;
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
공용 클래스 FileDataService_imp는 UnicastRemoteObject를 확장하고 FileDataService를 구현합니다.
공개 FileDataService_imp()에서 RemoteException이 발생합니다.
}
@보수
public void upload(String filename, byte[] fileContent)에서 RemoteException이 발생합니다.
파일 파일 = 새 파일(파일 이름);
노력하다 {
if (!file.exists())
file.createNewFile();
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(파일));
os.write(파일컨텐트);
} 잡기(FileNotFoundException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
} 잡기(IOException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
}
;
}
파일서버
다음과 같이 코드 코드를 복사합니다.
패키지 rmiupload;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
공개 클래스 FileServer {
FileDataService fileDataService;
공개 파일서버() {
노력하다 {
fileDataService = 새로운 FileDataService_imp();
LocateRegistry.createRegistry(9001);
Naming.rebind("rmi://localhost:9001/FileDataService", fileDataService);
} 잡기(RemoteException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
}
}
/**
* @param 인수
*/
공개 정적 무효 메인(String[] args) {
새로운 파일서버();
}
}