1. 자바 네트워킹의 초기 시대
- 자바가 처음 등장했던 1995년부터 2002년까지, 네트워크 프로그래밍은 상당히 복잡한 작업이었습니다.
- C 언어의 소켓 라이브러리를 기반으로 한 초기 자바의 네트워킹 API(java.net)는 객체 지향적인 인터페이스를 제공했지만, 여전히 많은 상용구 코드(boilerplate code)가 필요했습니다.
1.1 전통적인 블로킹 I/O
- 초기 자바의 네트워킹 API는 시스템 소켓 라이브러리의 블로킹 함수만을 지원했습니다.
- 다음은 전통적인 블로킹 I/O를 사용한 서버 코드의 예시입니다:
전통적인 블로킹 I/O 예시
// 서버 소켓 생성 및 포트 바인딩
ServerSocket serverSocket = new ServerSocket(portNumber);
// 클라이언트 연결 대기
Socket clientSocket = serverSocket.accept();
// 입력 스트림 설정
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 출력 스트림 설정 (autoFlush = true)
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
// 클라이언트의 요청을 처리하는 메인 루프
while ((request = in.readLine()) != null) {
if ("Done".equals(request)) {
break;
}
response = processRequest(request);
out.println(response);
}
- 서버 소켓 초기화
ServerSocket
이 지정된 포트 번호에서 연결 요청을 수신하기 위해 생성됩니다.- 이 소켓은 서버의 진입점 역할을 합니다.
- 클라이언트 연결 수락
accept()
메서드는 새로운 클라이언트 연결이 들어올 때까지 블로킹됩니다.- 연결이 수립되면 클라이언트와의 통신을 위한 새로운
Socket
객체를 반환합니다. ServerSocket
은 다시 다른 연결 요청을 기다리는 상태로 돌아갑니다.
- 스트림 설정
BufferedReader
는 소켓의 입력 스트림에서 생성되어 텍스트를 읽습니다.PrintWriter
는 소켓의 출력 스 트림에서 생성되어 텍스트를 쓰고 자동으로 플러시합니다.- 두 스트림 모두 문자 기반으로 동작하며, 바이트-문자 변환을 자동으로 처리합니다.
- 요청 처리 루프
readLine()
메서드는 클라이언트로부터 개행문자(\n)나 캐리지 리턴(\r)으로 끝나는 문자열을 받을 때까지 블로킹됩니다.- "Done" 메시지를 받으면 루프를 종료합니다.
- 각 요청은
processRequest()
메서드로 처리되어 응답이 생성됩니다. - 응답은
println()
을 통해 클라이언트로 전송됩니다.
경고
이 구현의 가장 큰 제한사항은 한 번에 하나의 클라이언트만 처리할 수 있다는 점입니다. 새로운 클라이언트가 연결을 시도해도, 현재 처리 중인 클라이언트의 요청-응답 사이클이 완료될 때까지 대기해야 합니다. 따라서 여러 클라이언트를 동시에 처리하기 위해서는 각 클라이언트 연결마다 새로운 스레드를 할당해야 했습니다. 이는 다음 섹션에서 설명할 심각한 확장성 문제를 야기했습니다.