반응형
안녕하세요! 오늘은 Java로 에코 서버(Echo Server)와 클라이언트(Client)를 구현하는 방법을 알아보겠습니다. 에코 서버란 클라이언트가 보낸 메시지를 그대로 다시 돌려주는 간단한 서버입니다. 네트워크 프로그래밍을 배우기에 아주 좋은 예제랍니다. 그럼 하나씩 살펴볼까요? 😊
전체 구조
이번 예제는 다음과 같은 구조로 이루어져 있습니다:
- Server: 클라이언트의 요청을 기다리고, 연결된 클라이언트와 데이터를 주고받습니다.
- ServerThread: 여러 클라이언트와 동시에 통신하기 위해 각 연결을 별도의 스레드에서 처리합니다.
- 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: 사용자 입력을 받아 메시지를 전송.
실행 방법
- 서버 실행
- Server를 먼저 실행합니다.
- 서버는 클라이언트의 연결 요청을 대기합니다.
- 클라이언트 실행
- Client를 실행합니다.
- 메시지를 입력하면 서버로 전송되고, 서버가 이를 그대로 반환합니다.
- 종료
- 클라이언트에서 /q를 입력하면 접속이 종료됩니다.
실행 결과
- 서버 시작:
<서버 시작> [연결을 기다립니다.]
- 클라이언트 연결 및 메시지:
<클라이언트 시작> [연결을 요청합니다.] [서버에 연결되었습니다.] 보낼 메시지: 안녕하세요! 서버 응답: [Echo] 안녕하세요!
마무리
이번 포스팅에서는 Java로 에코 서버와 클라이언트를 만드는 방법을 알아보았습니다. 기본적인 네트워크 통신 구조와 스레드 활용법을 배우기에 아주 적합한 예제죠! 여러분도 직접 코드를 실행해 보면서 네트워크 프로그래밍에 한 걸음 더 가까워져 보세요! 😊

반응형
'개발 > JAVA' 카테고리의 다른 글
Java로 네트워크 프로그래밍 시작하기: InetAddress를 활용한 IP 주소 확인 (4) | 2025.01.28 |
---|---|
Java로 배우는 간단한 TCP 소켓 프로그래밍 예제 (4) | 2025.01.27 |
JDBC와 오라클 데이터베이스: 자바로 간단한 작가 관리 애플리케이션 만들기 (4) | 2025.01.23 |
Java 입출력과 텍스트 처리: Scanner와 StringTokenizer 활용하기 (18) | 2025.01.22 |
Java 입출력 스트림 이해하기: Buffered 및 CharStream 사용 예제 (11) | 2025.01.21 |