이전에서는 서버가 단일 클라이언트의 요청만을 처리할 수 있었다면 이번엔 서버가 Thread를 이용하여 다중 클라이언트의 요청을 병행처리하는 과정이다.
먼저 서버를 실행한다.
package lesson01.exam02.multiserver;
import java.net.ServerSocket;
import java.net.Socket;
public class CalculatorServer {
private int port;
public CalculatorServer(int port) {//8888
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();
} catch (Throwable e) {
System.out.println("connection error!");
}
}
}
public static void main(String[] args) throws Exception {
CalculatorServer app = new CalculatorServer(8888);
app.service();
}
}
서버 실행시 클라이언트가 접속하면 이전 서버에서는 processRequest()라는 메소드를 호출하여 서버에서 직접 계산작업을 수행했지만 이번엔 CalculatorWorker라는 Thread를 상속받은 클래스를 통해서 계산작업을 수행한다.
package lesson01.exam02.multiserver;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
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() {//스레드를 시작시키면 부모 스레드의 흐름으로부터 분리되어 별개의 흐름으로 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.");
}
}
생성자에서는 클라이언트와 입출력을 위한 스트림 객체를 준비하고 다중 접속된 클라이언트를 구분하기 위해 클래스 변수 count를 증가시켜 스레드 고유번호로 할당한다.
스레드가 실행되면 부모 스레드의 실행 흐름과 분리된 별개의 흐름으로 run()메소드가 실행되어 연산 작업을 처리한다. 즉 부모 스레드와 자식 스레드가 병행으로 작업을 수행한다.
CalculatorFrame클래스에서 계산기를 실행시키면 CalculatorAgent클래스를 통해 서버와 통신하여 CalculatorServer클래스에서 실행한 서버에 접속하게 된다. CalculatorFrame클래스를 여러번 실행하여 다중 클라이언트를 접속시킨 뒤 각 클라이언트에서 계산작업을 수행하여도 잘 작동하는 것은, CalculatorServer클래스에서 자체적으로 계산작업을 하지 않고 Thread를 상속받은 CalculatorWorker클래스에서 계산작업을 수행하여 각 클라이언트가 독립된 스레드를 부여받아 계산 작업을 처리하게 된다.
하지만 현재 방법은 데스크톱 애플리케이션에 비해 복잡하고 데이터 통신을 위한 네트워크 프로그래밍과 다중 클라이언트 요청처리를 위한 스레드 프로그래밍도 해야 한다. 이 외에도 여러가지 다양한 애플리케이션 자원을 관리하기 위한 프로그래밍도 필요하다. 만약 이 부분이 자동화된다면 프로그래밍이 훨씬 쉬워질 것이다.
참고도서 : https://freelec.co.kr/book/1674/
[열혈강의] 자바 웹 개발 워크북
[열혈강의] 자바 웹 개발 워크북
freelec.co.kr
'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글
6. 웹 애플리케이션 아키텍처의 특징 (2) | 2022.01.01 |
---|---|
5. 클라이언트·서버 아키텍처의 진화 (0) | 2021.12.30 |
3. 클라이언트·서버 애플리케이션 (1) | 2021.12.28 |
2. 데스크톱 애플리케이션 실습 (0) | 2021.12.27 |
1. 실습 환경설정 : Github repositories에 연동 (0) | 2021.12.27 |
댓글