개발/JAVA

쉽게 배우는 Java 에코 서버와 클라이언트 만들기!

예니03 2025. 1. 24. 10:17
반응형

안녕하세요! 오늘은 Java로 에코 서버(Echo Server)와 클라이언트(Client)를 구현하는 방법을 알아보겠습니다. 에코 서버란 클라이언트가 보낸 메시지를 그대로 다시 돌려주는 간단한 서버입니다. 네트워크 프로그래밍을 배우기에 아주 좋은 예제랍니다. 그럼 하나씩 살펴볼까요? 😊

 

전체 구조

이번 예제는 다음과 같은 구조로 이루어져 있습니다:

  1. Server: 클라이언트의 요청을 기다리고, 연결된 클라이언트와 데이터를 주고받습니다.
  2. ServerThread: 여러 클라이언트와 동시에 통신하기 위해 각 연결을 별도의 스레드에서 처리합니다.
  3. Client: 서버에 메시지를 보내고, 서버로부터 응답을 받습니다.

 

코드 살펴보기

1️⃣ 에코 서버: Server.java

서버는 클라이언트의 요청을 받고, 각각의 연결을 스레드로 처리합니다.

public class Server {

    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        
        try {
            serverSocket = new ServerSocket(); // 서버 소켓 생성
            InetSocketAddress bindIp = new InetSocketAddress("0.0.0.0", 10000); // IP와 포트 설정
            serverSocket.bind(bindIp); // 소켓 바인딩
            
            System.out.println("<서버 시작>");
            System.out.println("[연결을 기다립니다.]");
            
            while (true) {
                Socket socket = serverSocket.accept(); // 클라이언트 연결 대기
                Thread thread = new ServerThread(socket); // 연결된 소켓을 새로운 스레드로 처리
                thread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                serverSocket.close(); // 서버 소켓 닫기
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟢 주요 개념

  • ServerSocket: 서버에서 클라이언트의 연결 요청을 수락하는 역할.
  • accept(): 클라이언트 연결 요청을 대기하다가, 요청이 오면 처리.
  • Thread: 여러 클라이언트가 동시에 접속해도 문제없이 처리할 수 있도록 각각의 요청을 독립적으로 처리.

 

2️⃣ 클라이언트 처리: ServerThread.java

각 클라이언트와의 통신은 ServerThread라는 별도의 스레드에서 처리합니다.

public class ServerThread extends Thread {
    private Socket socket; // 클라이언트와의 통신 소켓
    private String clientInfo;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InetSocketAddress socketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
            clientInfo = socketAddress.getAddress() + ":" + socketAddress.getPort();
            System.out.println("[클라이언트 " + clientInfo + "가 연결되었습니다.]");

            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));

            String message;
            while ((message = br.readLine()) != null) {
                System.out.println(clientInfo + ": " + message);
                bw.write("[Echo] " + message); // 에코 메시지 전송
                bw.newLine();
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

🟢 주요 개념

  • BufferedReader와 BufferedWriter: 클라이언트와 데이터를 주고받기 위한 입출력 스트림.
  • run(): 스레드 실행 시 호출되는 메서드로, 클라이언트와의 통신 로직을 처리.

3️⃣ 에코 클라이언트: Client.java

클라이언트는 서버에 연결하고, 메시지를 전송하며, 서버로부터 받은 응답을 출력합니다.

public class Client {

    public static void main(String[] args) {
        Socket socket = null;

        try {
            socket = new Socket();
            System.out.println("<클라이언트 시작>");
            System.out.println("[연결을 요청합니다.]");
            
            InetSocketAddress remote = new InetSocketAddress("192.168.0.49", 10000); // 서버 주소와 포트
            socket.connect(remote); // 서버와 연결
            System.out.println("[서버에 연결되었습니다.]");

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));

            Scanner scanner = new Scanner(System.in);

            while (true) {
                System.out.print("보낼 메시지: ");
                String msg = scanner.nextLine();

                if (msg.equals("/q")) {
                    System.out.println("접속을 종료합니다.");
                    break;
                }

                bw.write(msg);
                bw.newLine();
                bw.flush();

                String response = br.readLine();
                System.out.println("서버 응답: " + response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟢 주요 개념

  • Socket: 클라이언트가 서버와 통신하기 위해 사용하는 객체.
  • connect(): 서버에 연결 요청을 보냄.
  • Scanner: 사용자 입력을 받아 메시지를 전송.

실행 방법

  1. 서버 실행
    • Server를 먼저 실행합니다.
    • 서버는 클라이언트의 연결 요청을 대기합니다.
  2. 클라이언트 실행
    • Client를 실행합니다.
    • 메시지를 입력하면 서버로 전송되고, 서버가 이를 그대로 반환합니다.
  3. 종료
    • 클라이언트에서 /q를 입력하면 접속이 종료됩니다.

실행 결과

  1. 서버 시작:
    <서버 시작>
    [연결을 기다립니다.]
    
  2. 클라이언트 연결 및 메시지:
    <클라이언트 시작>
    [연결을 요청합니다.]
    [서버에 연결되었습니다.]
    보낼 메시지: 안녕하세요!
    서버 응답: [Echo] 안녕하세요!
    

마무리

이번 포스팅에서는 Java로 에코 서버와 클라이언트를 만드는 방법을 알아보았습니다. 기본적인 네트워크 통신 구조와 스레드 활용법을 배우기에 아주 적합한 예제죠! 여러분도 직접 코드를 실행해 보면서 네트워크 프로그래밍에 한 걸음 더 가까워져 보세요! 😊

반응형