네트워크 프로그래밍은 종종 복잡하고 신비로운 기술로 인식되지만, 사실 그 핵심은 간단한 통신 메커니즘에 기반합니다. 이 글에서는 자바에서 제공하는 네트워크 기능을 중심으로 기본적인 TCP 기반 통신 구현 방법을 알아봅니다.
우선 명확히 해야 할 점은, 웹 개발과 네트워크 프로그래밍은 동일하지 않다는 것입니다. 웹은 네트워크 통신의 한 형태일 뿐이며, 네트워크 프로그래밍은 더 넓은 범위를 포함합니다. 특히 게임이나 실시간 채팅 애플리케이션 등에서 사용되는 소켓 기반 통신이 대표적입니다.
기본적으로 네트워크 통신은 두 가지 주요 프로토콜인 TCP와 UDP를 기반으로 합니다.
- TCP는 연결 지향형 프로토콜로, 데이터 전송의 신뢰성을 보장하며, 오류 감지 및 재전송 기능을 갖춥니다. 마치 '질문-답변' 방식처럼 정확한 순서로 데이터가 도착합니다.
- UDP는 비연결 지향형으로, 속도 우선이며 패킷 손실을 허용합니다. 마치 대화기처럼 즉각적인 전달이 가능하지만, 데이터 손실 가능성 있음.
게임 개발에서는 실시간성과 낮은 지연을 위해 종종 UDP를 선택하지만, 정확성 요구 시에는 TCP가 적합합니다.
다음은 간단한 서버-클라이언트 구조의 자바 예제입니다.
1. 단일 클라이언트 연결을 위한 서버 코드
public class SimpleServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(888)) {
System.out.println("서버가 포트 888번에서 대기 중...");
Socket clientSocket = serverSocket.accept();
System.out.println("클라이언트가 연결되었습니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
이 코드는 특정 포트(예: 888)에서 클라이언트 접속을 기다립니다. accept() 메서드는 클라이언트 연결이 올 때까지 블로킹됩니다.
2. 클라이언트 코드
public class SimpleClient {
public static void main(String[] args) {
try (Socket socket = new Socket("127.0.0.1", 888)) {
System.out.println("서버에 연결되었습니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
이 코드는 로컬 머신의 888번 포트에 연결하려 시도합니다. 127.0.0.1은 자기 자신을 의미하는 내부 주소입니다.
3. 다중 클라이언트 처리 및 데이터 전송 예제
단일 연결만 처리하는 위 예제는 제한적이므로, 여러 클라이언트를 동시에 수용하기 위해 스레드를 사용하는 방식이 필요합니다.
public class MultiClientServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(999)) {
System.out.println("멀티 클라이언트 서버 시작 (포트 999)");
while (true) {
Socket client = serverSocket.accept();
System.out.println("새로운 클라이언트 연결: " + client.getInetAddress());
// 각 클라이언트 요청을 별도 스레드로 처리
new Thread(() -> {
try (InputStream input = client.getInputStream();
DataInputStream dataInput = new DataInputStream(input)) {
String message = dataInput.readUTF();
System.out.println("수신 메시지: " + message);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 클라이언트에서 메시지 전송 예제
public class MessageSenderClient {
public static void main(String[] args) {
try (Socket socket = new Socket("127.0.0.1", 999);
OutputStream output = socket.getOutputStream();
DataOutputStream dataOutput = new DataOutputStream(output)) {
Thread.sleep(3000); // 잠시 대기 후 전송
dataOutput.writeUTF("안녕하세요, 서버!");
System.out.println("메시지 전송 완료");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
이 예제에서는 스레드를 이용해 동시 연결을 처리하며, 각 클라이언트마다 독립된 작업 스레드를 생성하여 안정적인 멀티클라이언트 지원을 구현합니다. 또한 DataInputStream과 DataOutputStream을 통해 문자열 형식의 데이터를 안정적으로 읽고 쓸 수 있습니다.
이러한 기초 개념들은 네트워크 프로그래밍의 핵심이며, 이후 메시지 큐, 비동기 처리, 보안 통신 등을 확장할 수 있는 기반이 됩니다.