[CS] 운영체제 및 네트워크 이론 정리 (자바 간단 채팅 구현)

박원준·2025년 6월 26일

💡 운영체제 및 네트워크 이론 정리 (자바 간단 채팅 구현)

📌 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

항목TCPUDP
연결 방식연결형 (3-way handshake)비연결형
신뢰성높음낮음
속도느림빠름
사용 예채팅, 파일 전송게임, 스트리밍

2.3 포트와 IP

  • IP: 장치 식별 주소
  • Port: 하나의 서버 내 여러 서비스 식별

2.4 보안 개요

  • SSL/TLS: 전송 계층 암호화
  • 자바에서는 SSLSocket 사용 가능

🔌 3. 소켓 통신 개념 및 흐름

3.1 소켓 정의

  • 네트워크 양 끝단의 연결 지점
  • 자바에서는 ServerSocket, Socket 클래스 사용

3.2 TCP 통신 절차

  1. 클라이언트: Socket 생성 → 서버에 연결 요청
  2. 서버: ServerSocket.accept() → 연결 수락 (3-way handshake)
  3. 데이터 송수신 (readLine, write)
  4. 연결 종료 요청 (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 기반 보안 통신, 웹소켓, 서버 아키텍처 개선 등

0개의 댓글