git허브 소스복사
데스크톱 애플리케이션
특징 .
pc에 설치한 후 실행 (알집프로그램, 알쇼)
사용자 화면출력, 업무관련 작업 실행, 데이터 처리를 모두 pc에서 실행
배포가 번거롭다 , 보안에 취약하다.
인사정보나 , 회계정보가 exe만 실행하면 실행파일 내부에 데이터가 있어서 database 접근이 가능하기 때문에 보안에 취약함
클라이언트 * 서버 애플리케이션
서버는 비즈니스 로직과 , 데이터로직을 처리하고
클라이언트는 프리젠테이션 로직만을 실행한다.
기존에는 클라이언트가 직접 dbms서버에 접근이 가능해서, id나 비밀번호 조회를 살 수 있었는데, 클라이언트 서버로 바뀌면 서버를 통해 dbms에 접근을해서 외부에 데이터 노출을 막을 수 있다.
실습 (클라이언트 서버 애플리케이션
CalculatorFrame -> calculatorServer 로 네트워크 통신을 했었는데
CalculatorFrame -> CalculatorAgent -> CalculatorServer로 3단계에 거쳐서 진행
public static void main(String[] args) throws Exception {
CalculatorServer app = new CalculatorServer(8888);
app.service();
}
//통신 포트번호 8888설정 후 계산기 서비스 실행
public void service() throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("CalculatorServer startup:");
Socket socket = null;
while(true) {
try {
System.out.println("waiting client...");
socket = serverSocket.accept();
System.out.println("connected to client.");
processRequest(socket);
System.out.println("closed client.");
} catch (Throwable e) {
System.out.println("connection error!");
}
}
}//new ServerSocket(port)에서 설정한 포트번호로 서버소켓 생성
//serverSocket.accept 클라이언트의 연결을 기다렸다가 연결이 이뤄지면 요청을 처리함.
//processRequest 클라이언트 소켓으로 부터 입출력을 위한 스트림 객체를 준비
private void processRequest(Socket socket) throws Exception {
Scanner in = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
String operator = null;
double a, b, r;
while(true) {
try {
operator = in.nextLine(); //입력한 연산자 받아오기
if (operator.equals("goodbye")) {
out.println("goodbye");
break;
} else {
a = Double.parseDouble(in.nextLine());//입력한 값 받아오기 1
b = Double.parseDouble(in.nextLine());//입력한 값 받아오기 2
r = 0;
switch (operator) {
case "+": r = a + b; break;
case "-": r = a - b; break;
case "*": r = a * b; break;
case "/":
if (b == 0) throw new Exception("0 으로 나눌 수 없습니다!");
r = a / b;
break;
default:
throw new Exception("해당 연산을 지원하지 않습니다!");
}
out.println("success");
out.println(r);
}
} catch (Exception err) {
out.println("failure");
out.println(err.getMessage());
}
}
try {out.close();} catch (Exception e) {}
try {in.close();} catch (Exception e) {}
try {socket.close();} catch (Exception e) {}
}
역할 -- 고객데이터를 받아서 전달 - 화면단에 뿌림
//버튼클릭 이벤트 처리 actionPerformed
@Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == equal) {
compute();
} else {
clearForm();
}
}//이벤트 객체를 조사해서 이벤트가 발생한 버튼이 무엇인지 알아냄
private void compute() {
double a = Double.parseDouble(operand1.getText());
double b = Double.parseDouble(operand2.getText());
double r = 0;
try {
r = calcAgent.compute(operator.getText(), a, b);
result.setText(Double.toString(r));
} catch (Exception err) {
JOptionPane.showMessageDialog(
null, err.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
소켓을 통해 입출력 할 수 있도록 스트림 객체를 준비
private void compute() {
double a = Double.parseDouble(operand1.getText());
double b = Double.parseDouble(operand2.getText());
double r = 0;
try {
r = calcAgent.compute(operator.getText(), a, b);
result.setText(Double.toString(r));
} catch (Exception err) {
JOptionPane.showMessageDialog(
null, err.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
// compute는 사용자가 입력한 연산자와 두개에 입력값을 서버에 전송합니다. success면 정상적으로 처리되었다는 것이므로 double형으로 바꿔서 리턴합니다.
위 클라이언트 - AGENT - SERVER구조에 문제점은 여러명에 클라이언트가 접속할 수 없다는 것이고
장점은 계속 업데이트하고 다운로드를 안해도 서버에있는 최신데이터는 통신을 통해 반영을 할 수 있다는 것이다.
다중 클라이언트 요청처리
위 방식은 하나에 서버에서 여러개 클라이언트의 요청을 처리하지 못해, 처리중의 경우 다른 사용자는 대기하는 형태이다. 이걸 한번 개선해보자.
이런 문제점을 해결하기 위해선 THREAD를 사용해야한다.
서버에 스레드가 요청을 대신 처리하고 응답을 할 수있도록 만들어야함
THREAD로 여러 동시요청을 서버에서 처리할 수 있다
public class CalculatorServer {
private int port;
public CalculatorServer(int port) {
this.port = port;
}
public void service() throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("CalculatorServer startup:");
Socket socket = null;
while(true) {
try {
socket = serverSocket.accept(); //여기까지 동일하지만
System.out.println("connected to client.");
new CalculatorWorker(socket).start(); //스레드인 calculator worker를 사용함
} catch (Throwable e) {
System.out.println("connection error!");
}
}
}
public static void main(String[] args) throws Exception {
CalculatorServer app = new CalculatorServer(8888);
app.service();
}
}
public class CalculatorWorker extends Thread {
static int count;
Socket socket;
Scanner in;
PrintStream out;
int workerId;
public CalculatorWorker(Socket socket) throws Exception {
workerId = ++count;
this.socket = socket;
in = new Scanner(socket.getInputStream());
out = new PrintStream(socket.getOutputStream());
}
@Override
public void run() {
System.out.println("[thread-" + workerId + "] processing the client request.");
String operator = null;
double a, b, r;
while(true) {
try {
operator = in.nextLine();
if (operator.equals("goodbye")) {
out.println("goodbye");
break;
} else {
a = Double.parseDouble(in.nextLine());
b = Double.parseDouble(in.nextLine());
r = 0;
switch (operator) {
case "+": r = a + b; break;
case "-": r = a - b; break;
case "*": r = a * b; break;
case "/":
if (b == 0) throw new Exception("0 으로 나눌 수 없습니다!");
r = a / b;
break;
default:
throw new Exception("해당 연산을 지원하지 않습니다!");
}
out.println("success");
out.println(r);
}
} catch (Exception err) {
out.println("failure");
out.println(err.getMessage());
}
}
try { out.close(); } catch (Exception e) {}
try { in.close(); } catch (Exception e) {}
try { socket.close(); } catch (Exception e) {}
System.out.println("[thread-" + workerId + "] closed client.");
}
}
반복되는 요청만 socket으로 받아서 처리함 기존에는, process가 사용자가 직접 종료하기 전까지는 끝나지 않았는데, 쓰레드가 서버가 생성함과 동시에 복제로 같은동작을 실행하는 worker를 계속 만들어내서 처리하도록 진행함.
장점 스레드 - 작업에 병행 처리가 가능 -> 동시작업
단점 - 스레드와 소켓을 사용해 코드가 복잡해짐