💡 운영체제 및 네트워크 이론 정리 (자바 간단 채팅 구현)
📌 1. 운영체제 (Operating System) 이론
운영체제는 사용자와 하드웨어 사이에서 자원을 관리하고 프로그램 실행을 조정하는 시스템 소프트웨어입니다. 서버 개발에서는 프로세스, 스레드, 입출력 처리 방식이 성능에 직접적인 영향을 미칩니다.
1.1 프로세스 (Process)
- 정의: 실행 중인 프로그램 인스턴스. 메모리에 적재되어 CPU에 의해 실행 가능한 상태가 된 프로그램.
- 구성 요소
- 텍스트 영역 (Code): 실행할 명령어
- 데이터 영역 (Data): 전역 변수, 정적 변수
- 힙 (Heap): 동적으로 할당된 메모리 공간 (
new, malloc)
- 스택 (Stack): 함수 호출 시 생성되는 지역 변수와 리턴 주소
▶ 프로세스 상태 전이
| 상태 | 설명 |
|---|
| 생성(New) | 프로세스 생성 중 |
| 준비(Ready) | CPU 할당을 기다림 |
| 실행(Running) | 명령어 수행 중 |
| 대기(Waiting) | I/O 등 이벤트 대기 중 |
| 종료(Terminated) | 수행 완료 |
💡 readLine() 호출 시 프로세스는 Waiting 상태로 전이됨 → 입출력이 끝나야 Ready로 복귀
▶ 컨텍스트 스위칭 (Context Switching)
- 하나의 프로세스에서 다른 프로세스로 CPU 제어권이 넘어갈 때 발생
- OS는 PCB(Process Control Block)에 현재 프로세스 상태 저장 후, 새 프로세스를 복원
- 비용 발생: 메모리 접근, 캐시 미스 등
▶ PCB (Process Control Block)
운영체제가 각 프로세스를 관리하기 위한 핵심 자료 구조
- 프로세스 ID (PID)
- 상태 정보 (Ready, Waiting 등)
- 프로그램 카운터 (현재 실행 중인 명령어 주소)
- 레지스터 상태
- 메모리 관리 정보 (페이지 테이블 등)
- 입출력 정보 (열린 파일 목록 등)
▶ 프로세스 간 통신 (IPC)
- 파이프(Pipe), 공유 메모리(Shared Memory), 메시지 큐(Message Queue) 등
- 채팅 서버는 클라이언트-서버 구조 → IPC보다는 소켓 통신 활용
▶ 멀티프로세싱 vs 멀티스레딩
| 항목 | 멀티프로세싱 | 멀티스레딩 |
|---|
| 자원 공유 | 메모리 공유 안 함 | 같은 프로세스 내 메모리 공유 |
| 안정성 | 하나 죽어도 다른 프로세스는 영향 없음 | 하나의 스레드 오류 시 전체 영향 가능 |
| 속도 | 컨텍스트 스위칭 비용 큼 | 상대적으로 빠름 |
| 예시 | 웹 브라우저의 탭마다 별도 프로세스 | 채팅 서버에서 클라이언트별 스레드 처리 |
1.2 스레드 (Thread)
- 정의: 프로세스 내부에서 실행 흐름의 최소 단위
- 특징
- 같은 프로세스 내 스레드는 메모리 자원(힙 등)을 공유
- 컨텍스트 스위칭 비용이 낮고 병렬 처리에 유리
- 예시
ChatServer: 각 클라이언트마다 ClientHandler 스레드 생성
ChatClientGUI: 메시지 수신을 별도 스레드에서 처리 → UI 프리징 방지
1.3 스레드 동기화
- 스레드 간 충돌 방지를 위해
synchronized, Lock, ConcurrentHashMap 등의 방법 사용
- 공유 자원 보호 및 동시성 보장
1.4 입출력 모델
- Blocking I/O: 스레드가 I/O 작업 완료 시까지 대기
- Non-blocking I/O (NIO): Selector 기반으로 여러 채널을 하나의 스레드가 효율적으로 관리
🌐 2. 네트워크 및 소켓 이론
2.1 OSI 7계층 심화
| 계층 | 이름 (영문) | 주요 역할 및 개발자 관점 |
|---|
| 7 | 응용 (Application) | 사용자와 맞닿음, HTTP/FTP |
| 6 | 표현 (Presentation) | 인코딩/디코딩, 암호화 |
| 5 | 세션 (Session) | 연결 관리, 동기화 |
| 4 | 전송 (Transport) | TCP/UDP, 오류 복구 |
| 3 | 네트워크 (Network) | IP 주소, 라우팅 |
| 2 | 데이터 링크 (Data Link) | MAC 주소, 프레임 전송 |
| 1 | 물리 (Physical) | 전기 신호, 케이블 등 |
💬 채팅 서버는 4~7계층 중심으로 동작
2.2 TCP vs UDP
| 항목 | TCP | UDP |
|---|
| 연결 방식 | 연결형 (3-way handshake) | 비연결형 |
| 신뢰성 | 높음 | 낮음 |
| 속도 | 느림 | 빠름 |
| 사용 예 | 채팅, 파일 전송 | 게임, 스트리밍 |
2.3 포트와 IP
- IP: 장치 식별 주소
- Port: 하나의 서버 내 여러 서비스 식별
2.4 보안 개요
- SSL/TLS: 전송 계층 암호화
- 자바에서는
SSLSocket 사용 가능
🔌 3. 소켓 통신 개념 및 흐름
3.1 소켓 정의
- 네트워크 양 끝단의 연결 지점
- 자바에서는
ServerSocket, Socket 클래스 사용
3.2 TCP 통신 절차
- 클라이언트:
Socket 생성 → 서버에 연결 요청
- 서버:
ServerSocket.accept() → 연결 수락 (3-way handshake)
- 데이터 송수신 (
readLine, write)
- 연결 종료 요청 (4-way handshake)
3.3 코드 흐름 예시
ServerSocket serverSocket = new ServerSocket(12345);
Socket clientSocket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
Socket socket = new Socket("localhost", 12345);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
🤖 4. 채팅 서버 코드
| 코드 요소 | 연결된 CS 개념 |
|---|
new Thread(...).start() | 멀티스레딩, 운영체제 스케줄링 |
BufferedReader.readLine() | Blocking I/O, Waiting 상태 |
Socket socket = new Socket(...) | TCP 연결 수립, 3-way handshake |
ConcurrentHashMap.newKeySet() | 스레드 안전 자료구조, 동기화 |
SwingUtilities.invokeLater(...) | GUI 이벤트 디스패치 스레드 처리 |
ChatServer.java
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
// 멀티스레드 기반 채팅 서버 클래스
public class ChatServer {
// 서버 포트 설정 (클라이언트는 이 포트로 연결 요청)
private static final int PORT = 12345;
// 최대 5명까지 클라이언트 허용
private static final int MAX_CLIENTS = 5;
// 연결된 클라이언트들을 저장하는 동기화된 Set
// ConcurrentHashMap.newKeySet()는 스레드 안전(thread-safe)한 자료구조를 제공
private static Set<ClientHandler> clients = ConcurrentHashMap.newKeySet();
public static void main(String[] args) {
System.out.println("💬 Chat Server is running...");
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
// ServerSocket: OS에서 제공하는 네트워크 API를 이용한 소켓 바인딩
// 포트 12345에서 클라이언트 연결 요청을 수신
while (true) {
if (clients.size() < MAX_CLIENTS) {
// 클라이언트 접속 요청 수락 (blocking call)
Socket socket = serverSocket.accept(); // => 커널에 accept 시스템 콜
// 클라이언트마다 별도의 스레드를 생성해 통신 처리
ClientHandler handler = new ClientHandler(socket);
clients.add(handler);
handler.start(); // Thread의 run()을 실행, 사용자 정의 스레드
} else if(clients.size() >= MAX_CLIENTS + 1){
System.out.println("⚠️ 최대 접속 인원을 초과했습니다.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 모든 클라이언트에게 메시지를 전송 (브로드캐스트)
public static void broadcast(String message, ClientHandler sender) {
for (ClientHandler client : clients) {
if (client != sender) { // 보낸 사람 제외
client.sendMessage(message);
}
}
}
// 클라이언트 연결 종료 시 목록에서 제거
public static void removeClient(ClientHandler client) {
clients.remove(client);
}
}
ChatClientGUI.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
/**
* Swing 기반 GUI 채팅 클라이언트
* - 스레드: 서버 메시지 수신은 별도 스레드에서 처리 (UI 프리징 방지)
* - 이벤트 기반: 버튼 클릭 또는 Enter 키로 메시지 전송 (ActionListener 사용)
*/
public class ChatClientGUI extends JFrame {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private JTextArea messageArea; // 메시지를 표시할 텍스트 영역 (출력 전용)
private JTextField inputField; // 메시지를 입력할 텍스트 필드
private JButton sendButton; // 메시지 전송 버튼
private String nickname;
public ChatClientGUI(String serverAddress, int serverPort) {
// JFrame 기본 설정
setTitle("💬 채팅 클라이언트");
setSize(400, 500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null); // 화면 중앙에 배치
// 메시지 출력 영역 설정
messageArea = new JTextArea();
messageArea.setEditable(false); // 사용자 편집 불가
messageArea.setLineWrap(true); // 줄 바꿈 자동
// 스크롤 가능한 메시지 영역
JScrollPane scrollPane = new JScrollPane(messageArea);
// 입력 필드와 전송 버튼 구성
inputField = new JTextField();
sendButton = new JButton("전송");
// 하단 패널 구성 (입력 필드 + 버튼)
JPanel bottomPanel = new JPanel(new BorderLayout());
bottomPanel.add(inputField, BorderLayout.CENTER);
bottomPanel.add(sendButton, BorderLayout.EAST);
// 메인 프레임에 추가
add(scrollPane, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.SOUTH);
// 버튼 클릭 또는 Enter 키 입력 시 메시지 전송 이벤트 연결
sendButton.addActionListener(e -> sendMessage());
inputField.addActionListener(e -> sendMessage());
// 서버 연결 시도
connectToServer(serverAddress, serverPort);
}
/**
* 서버에 소켓 연결 및 메시지 수신 스레드 시작
*/
private void connectToServer(String address, int port) {
try {
// 서버와 TCP 연결 (클라이언트 소켓 생성)
socket = new Socket(address, port);
// 입출력 스트림 설정
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// 사용자에게 닉네임 입력 받기
nickname = JOptionPane.showInputDialog(this, "닉네임을 입력하세요:");
if (nickname == null || nickname.trim().isEmpty()) {
nickname = "Anonymous";
}
out.println(nickname); // 서버로 닉네임 전송
// 별도 스레드에서 서버 메시지 수신 처리
new Thread(() -> {
String message;
try {
while ((message = in.readLine()) != null) {
// 메시지를 UI 쓰레드(Swing의 이벤트 디스패치 스레드)에서 출력
appendMessage(message);
}
} catch (IOException e) {
appendMessage("❌ 서버 연결이 종료되었습니다.");
}
}).start();
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "서버에 연결할 수 없습니다.");
System.exit(0);
}
}
/**
* 메시지를 서버에 전송하고, 본인의 메시지를 채팅창에 표시
*/
private void sendMessage() {
String msg = inputField.getText();
if (!msg.trim().isEmpty()) {
out.println(msg); // 서버로 메시지 전송
appendMessage("나: " + msg); // 자신의 메시지를 로컬 채팅창에 출력
inputField.setText(""); // 입력창 비우기
}
}
/**
* UI에 메시지 추가 (스레드 안전)
*/
private void appendMessage(String message) {
// Swing은 UI 업데이트를 이벤트 디스패치 스레드(EDT)에서 해야 함
SwingUtilities.invokeLater(() -> {
messageArea.append(message + "\n");
});
}
public static void main(String[] args) {
// Swing은 UI를 이벤트 디스패치 스레드에서 실행해야 안정적
SwingUtilities.invokeLater(() -> {
ChatClientGUI client = new ChatClientGUI("localhost", 12345);
client.setVisible(true);
});
}
}
ClientHandler.java
import java.io.*;
import java.net.*;
// 각 클라이언트와 통신을 담당하는 별도 스레드 클래스
public class ClientHandler extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
// InputStream을 이용해 클라이언트 메시지 수신
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// OutputStream을 이용해 클라이언트에게 메시지 전송
out = new PrintWriter(socket.getOutputStream(), true);
// 환영 메시지 전송
out.println("👋 채팅에 오신 것을 환영합니다!");
// 사용자 이름 수신
String name = in.readLine();
// 접속 알림을 모든 사용자에게 전송
ChatServer.broadcast("🔔 " + name + "님이 입장했습니다.", this);
// 사용자 메시지 수신 및 브로드캐스트 반복
String message;
while ((message = in.readLine()) != null) {
System.out.println("📨 " + name + ": " + message);
ChatServer.broadcast("💬 " + name + ": " + message, this);
}
} catch (IOException e) {
System.out.println("❌ 클라이언트 연결 종료");
} finally {
// 연결 종료 및 자원 정리
ChatServer.removeClient(this);
try {
socket.close(); // 소켓 닫기 → TCP 연결 종료
} catch (IOException e) {}
}
}
// 클라이언트에게 메시지 전송
public void sendMessage(String message) {
out.println(message);
}
}
📚 결론 및 확장 방향
- 운영체제와 네트워크 지식은 실전 코드와 연결하면 훨씬 빠르고 정확하게 체화 가능
- 향후 확장: NIO, SSL 기반 보안 통신, 웹소켓, 서버 아키텍처 개선 등