1. 예제코드에 사용된 개념
- 네트워크
- 소켓
- 스트림
- 직렬화와 역직렬화
- 스레드
2. 개념정리
1) 네트워크
- 컴퓨터나 장치 사이에서 데이터를 주고받는 과정
- 네트워크 통신을 통해 원격 위치에 있는 시스템 간 통신이 가능하다.
2) 소켓(Socket)
- 네트워크를 통해 데이터를 교환하기 위한 인터페이스다.
- 소켓을 통해 클라이언트와 서버가 양방향 통신을 할 수 있다.
- 소켓은 IP 주소와 포트 번호를 기반으로 통신한다.
3) 스트림
- 데이터의 입출력을 도와주는 중간 매개체다. 데이터의 흐름이나 통로로 보면 된다.
- 스트림은 단방향이므로 하나의 스트림으로 입출력을 동시에 수행할 수 없다.
4) 직렬화와 역직렬화
- 직렬화 : 데이터를 스트림에 전송가능 한 형태(바이트 단위, 일렬)로 변환하는 과정
- 역직렬화 : 스트림을 사용해 전달받은 직렬화된 데이터를 원래의 객체 형태로 변환하는 과정
5) 스레드
- 프로세스 내에서 나뉜 하나의 작업 단위. 하나의 프로세스에는 여러 스레드가 존재할 수 있다.
- 멀티스레드 : 각각의 스레드는 힙과 클래스(static)변수, 코드(메서드) 영역을 공유한다.
스레드의 제어흐름을 '동시'로 처리하여 CPU의 사용률과 응답성을 높이는 기술이다.
3. 멀티스레드 기반의 클라이언트-서버 애플리케이션 예제
import java.io.*; // 입출력 관련 클래스를 사용하기 위한 import
import java.net.*; // 네트워크 관련 클래스를 사용하기 위한 import
import java.util.concurrent.ExecutorService; // 스레드 풀 관리를 위한 클래스
import java.util.concurrent.Executors; // 스레드 풀 생성을 위한 클래스
public class Server {
public static void main(String[] args) throws IOException {
int port = 6666; // 서버가 사용할 포트 번호 설정
ServerSocket serverSocket = new ServerSocket(port); // 지정된 포트로 서버 소켓 생성
System.out.println("서버가 " + port + " 포트에서 시작되었습니다.");
ExecutorService executor = Executors.newFixedThreadPool(10);
// 최대 10개의 동시 작업을 처리할 수 있는 스레드 풀 생성
/*while(true)사용하는 이유*/
/*서버가 지속적으로 실행되면서 클라이언트의 연결 요청을
무한정 기다리고 수락하기 위해 while(true)를 사용한다.
이 무한루프는 서버가 실행 상태를 계속 유지하고 언제든지
클라이언트의 연결을 받을 준비가 되어있다는 의미다.*/
while (true) {
Socket socket = serverSocket.accept(); // 클라이언트의 연결 요청 대기
executor.execute(new ClientHandler(socket));
// 클라이언트 연결에 대한 처리를 ClientHandler 스레드에 위임
}
}
}
// 클라이언트 처리를 담당하는 Runnable 클래스
class ClientHandler implements Runnable {
private Socket socket; // 클라이언트와의 통신을 위한 소켓
//클라이언트-서버 간 연결이 되면 이 소켓을 통해 데이터 주고받음.
// 생성자에서 클라이언트 소켓을 받음
/*매개변수에 소켓 들어가는 이유*/
/*여러 클라이언트와 동시 연결을 처리하기 위해,
각 클라이언트 연결에 해당하는 소켓을 전달해야한다.
각 스레드는 자신만의 소켓 인스턴스를 갖고 독립적으로 작동한다.*/
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 데이터 입력 스트림 생성
DataInputStream dis = new DataInputStream(socket.getInputStream());
// 데이터 출력 스트림 생성
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
// 클라이언트로부터 메시지 읽기
String message = dis.readUTF();
System.out.println("받은 메시지: " + message);
// 클라이언트에 응답 보내기
dos.writeUTF("메시지 받았습니다: " + message);
// 스트림과 소켓 닫기
//닫지 않으면?
//1. 리소스 누수가 발생할 수 있다.--> 시스템 성능 저하, 안정성 문제 일으킴
//2. 버퍼에 남은 데이터가 손실될 수 있다.
//3. 소켓을 열어두면 불필요한 네트워크 자원 사용과 보험 위험을 야기할 수 있다.
dis.close();
dos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace(); // 예외 발생 시 스택 추적
}
}
}
import java.io.*; // 입출력 관련 클래스를 사용하기 위한 import
import java.net.*; // 네트워크 관련 클래스를 사용하기 위한 import
public class Client {
public static void main(String[] args) throws IOException {
// 서버의 주소와 포트 번호를 사용하여 소켓 생성
Socket socket = new Socket("localhost", 6666);
// 서버에 데이터를 보내기 위한 데이터 출력 스트림 생성
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
// 서버로부터 데이터를 받기 위한 데이터 입력 스트림 생성
DataInputStream dis = new DataInputStream(socket.getInputStream());
// 서버에 메시지 전송
dos.writeUTF("안녕하세요, 서버님!");
// 서버로부터 응답 메시지 수신
String response = dis.readUTF();
System.out.println("서버로부터 받은 응답: " + response);
// 스트림과 소켓 닫기
dis.close();
dos.close();
socket.close();
}
}
4. 오늘의 회고
문득 하루가 48시간이었음 좋겠다 생각이 들었는데, 하루가 48시간이라면, 기존 일정이 두배 앞당겨지겠지. 그럼 결국 나한테 주어진 시간은 똑같겠지? 결론적으로 나에게 주어진 절대적인 시간이 중요한게 아니라 시간을 어떻게 쪼개서 쓰냐가 중요한 것 이겠지..오늘 꿀잠자고 내일도 힘차게 달려봐야겠다. 오늘도 수고했다.
'프로그래밍 언어 > Java' 카테고리의 다른 글
[JSP] 게시판 만들기 | 2편.데이터베이스 설치 및 연동 (1) | 2024.02.07 |
---|---|
[JSP] 게시판 만들기 | 1편. 환경 설정 및 JSP 개념 이해 (0) | 2024.02.07 |
[과제] 경마게임 만들기 | Thread 활용 (1) | 2023.12.20 |
[Java] 제네릭(generic)의 개념과 활용법 (1) | 2023.12.19 |
[과제] UML에 맞게 싱글톤 패턴 적용하기 (0) | 2023.12.18 |