들어가기
이번엔 클라이언트의 HTTP 메세지를 받아 서버측에서 출력해보자.
HTTP 메세지를 BufferedReader로 출력하는 과정에서 Body를 출력하지 못하고 무한정 대기하는 상황이 있었다.
정리를 해보자.
클라이언트 HTTP 요청
클라이언트는 저번과 같이 http://localhost:8081 에서 기동중이다.
아래는 클라이언트 코드다.
shot() {
var dataToSend = 'Hello Server';
fetch('http://localhost:8080', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: dataToSend,
})
.then(function (response) {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text();
})
.then(function (data) {
console.log('전송 성공:', data);
})
.catch(function (error) {
console.error('전송 실패:', error);
});
},
아래는 저번에 만든 클래스에 출력하는 코드를 작성했다.
public class WebServer {
private static final Logger logger = LoggerFactory.getLogger(WebServer.class);
private static final int DEFAULT_PORT = 8080;
private static final int BACK_LOG = 5;
private static BufferedReader bufferedReader = null;
private static ServerSocket serverSocket = null;
public static void main(String[] args) throws IOException {
try{
serverSocket = new ServerSocket();
serverSocket.bind(
new InetSocketAddress(serverSocket.getInetAddress(),DEFAULT_PORT)
,BACK_LOG
);
logger.info("WebServer Started {} port",DEFAULT_PORT);
Socket client = serverSocket.accept();
logger.info("{} : success",client.getInetAddress());
// 클라이언트가 보낸 HTTP 메세지를 InputStream으로 받아오기
InputStream inputStream = client.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
// HTTP 메세지 출력
String line = "";
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
inputStream.close();
client.close();
}catch (IOException e){
logger.error(e.getMessage());
}finally {
serverSocket.close();
}
}
'잘 되겠지'라며 호기롭게 요청을 보냈으나...
클라이언트에서 전송한 body(Hello Server)는 출력되지 않았다.
HTTP 요청 메세지의 startline, header, empty-line 까지만 출력됐다.
우선 InputStream, InputStreamReader, BufferedReader 에대한 이해가 부족하다고 생각해서 3가지를 공부해봤다.
InputStream
InputStream은 자바에서 바이트 단위로 데이터를 읽어들이는 통로다. 스트림이라는 단어의 뜻과 같이 데이터가 단방향으로 흐르게된다.
추상클래스로 이루어져있으며 바이트 기반 입력 클래스의 최상위 추상 클래스다.
바이트 단위로 읽어서 읽은 바이트를 반환하고 읽을 데이터가 없다면 -1을 반환한다.
소켓의 수신버퍼에서 데이터를 가져와 바이트 단위로 하나씩 읽는다.
InputStreamReader
InputStreamReader는 InputStream을 이용해 바이트 단위로 읽은 데이터를 char형으로 변형시키고 배열 형태로 반환받을 수 있다.
BufferedReader
InputStreamReader의 문자를 문자열 형태로 쌓아서 한 번에 출력할 수 있게해준다.
문제 분석
그런데 참 이상하게도 서버에서 empty-line을 찍고 무한대기하다가 클라이언트의 접속을 끊어 버리면 body가 출력된다.
bufferedReader의 readline()을 조금 뜯어 보기로했다.
메서드 설명을 보면 라인피드(\n) , 캐리지 리턴(\r), 혹은 케리지 리턴 바로 뒤에 라인피드(CRLF, \r\n)를 만나면 한 줄의 끝이라고 판단한다고 한다.
메세지를 분석해보면 empty line은 CRLF이고, 메세지 바디의 끝엔 CRLF가 없다.
그렇기 때문에 바디를 읽어도 CRLF를 만나지 못해서 무한 대기를 했던 것이다.
클라이언트의 연결이 종료되면 EOF가 전송돼 끝나게된다.
그럼 EOF를 InputStream에 전송되게끔 해야하나?
절대 안된다.
서버가 EOF를 수신했다는 것은 TCP 연결을 해제했다는 것이고 응답을 내려주기 전에 EOF를 수신받으면
적절한 응답을 클라이언트에게 보낼 수 없다.
서버로부터 응답을 받은 뒤 클라이언트가 EOF를 보내야한다.
즉 InputSream의 EOF가 오기까지 기다리고 HTTP 메세지를 파싱해서는 안되고
HTTP헤더의 Content-Length 정보를 가지고 body를 가져와야한다.
다음 포스팅에선 HTTP를 파싱해보자.
'Java' 카테고리의 다른 글
[우아한 테크 세미나] 우아한 객체지향 - 1 (0) | 2024.04.08 |
---|---|
[WAS를 만들어보자 (3)] HttpMessageBody 추출하기 (1) | 2024.03.23 |
[WAS를 만들어보자 (1)] 자바로 TCP/IP 통신하기 (0) | 2024.03.23 |
Faker를 이용한 테스트데이터 만들기 + 데이터 30만건 밀어넣기 (0) | 2023.07.16 |
Exception Performance Cost (0) | 2023.07.05 |