티스토리 뷰

자바의 InputStream과 OutputStream은 외부와의 I/O를 위한 인터페이스를 제공한다.

이번에 다룰 주제인 InputStream은 I/O 중 I에 대한 부분을 다룬다.

 

대표적인 3가지 사용처는 콘솔과의 표준 입출력, 파일 입출력, 소켓과의 입출력이다.

 

오늘 다룰 내용은 자바 ee 어플리케이션에서의 inputstream이다.

 

자바 ee 서버에서는 HttpServletRequest가 존재하고,

이 클래스를 통해 inputstream을 가져올 수 있게 되어 있다. 

그렇기 때문에, request.getInputStream(); 과 같은 형태로 서블릿 내에서 inputstream을 받아와 사용할 수 있는 것이다.

톰캣에서는 CoyoteInputStream이란 이름으로 단일한 형태의 inputstream을 제공하고,

제우스의 경우에는 요청의 종류에 따라 WjpInputStream, Ajp13InputStream, ServletInputStreamImpl을 제공한다.

 

오늘 다룰 내용은 inputstream 에서 제공하는 available 메서드 사용 시에 주의할 점에 대한 내용이다

 

다시 한번 말하지만, inputstream은 외부와 통신하기 위한 인터페이스이다.

자바는 C언어와 다달리,  OS의 민감한 부분에 직접적으로 접근하는 부분은 jdk에서 제공한 수단으로만 접근하도록 어지간한건 다 막아놨다. 

FileInputstream , SocketInputStream 등 외부에서 무언가 읽기 위해선 InputStream을 거치도록 해놨다. 

 

SocketInpustream에선 read 메서드를 호출하면, 인자에 따라 1byte를 읽거나, 정해진 양만큼 읽어오려고 시도한다.

어플리케이션에서 요구한 양보다 버퍼에 있는 양이 더 적으면, 그냥 적은걸 던져준다.

-1을 리턴하는 경우는 연결이 끊겼을 때이고, 단순히 읽을 것이 없는 경우엔 0을 던저주는 것이 아니라, blocking 된다.

 

따라서 만약에 어플리케이션이 구현이 잘못되었다면, 

 

1. read 메서드에 의해 blocking이 될 수 있다.

2. 읽어오고자 하는 데이터를 전부다 읽어오지 못할 수도 있다. 

 

위와 같은 위험성을 제거하기 위해, inputstream은 available 메서드를 제공한다. 

available 메서드는 지금 읽어올 수 있는 데이터의 양을 제공해주는 메서드이다.

이 메서드의 이름만 봐서는 현재 읽을 수 있는 값을 정확하게 돌려줄 수 있을 것 같다.

그러나 결코 그렇지 않기 때문에 사용에 많은 주의가 필요하다.

 

엄밀히 말하면, 이 메서드는 "현재 확실히 읽을 수 있는 버퍼의 크기"를 제공한다.

java doc에 가보면, "estimated value"를 제공한다고 되어 있다.

 

예를 들어 SocketInputstream의 경우 실제 구현한 코드에 아래처럼 주석이 달려있다.

/**
     * Returns the number of bytes that can be read without blocking.
     * @return the number of immediately available bytes
     */
    public int available() throws IOException {
        return impl.available();
    }

 

이는 이 메서드가 call 되는 시점과, 버퍼가 차오르는 시점이 다를수 있기 때문이다.

available 메서드로 얼마나 읽을 수 있을지 확인하고 있는 그 시점에도, 

새로운 데이터는 버퍼에 계속 쌓이고 있기 때문이다. (블로킹을 하지 않기 때문이다)

 

이 때문에, available 메서드가 사람들이 이해한대로 동작하지 않는 경우가 자주 있다.

예를 들어, available이 0일 때, 루프를 탈출하도록 작성했다고 가정한다.

 

InputStream is = socket.getInputStream();

while(is.available()>0) {}

 

이렇게 되면 대부분의 상황에서, 루프를 탄출할 가능성이 크다. 

클라이언트에서 데이터를 보내도, 해당 루프를 아예 돌지 않을 수 있다는 것이다.

socket에서 연결을 맺고, inputstream에서 available을 호출하는 시점엔 데이터가 없다가,

로프를 탈출한 이후에 버퍼에 데이터가 들어올 수도 있다. 

 

물론 이 부분도 구현하기 나름이라서 InputStream 을 상속받아 구현한 사람이 어떻게 만드느냐에 따라 다르다.

어찌되었든 그로 인해 다른 문제가 생길수도 있기 때문에 문서를 보거나 구현을 확인한 후에 사용하도록 하자.

 

결론은 아래와 같다.

 

자바 어플리케이션 내에서 inputstream으로 값을 읽어올 때에는, 위의 상황을 고려하지 않으면,

데이터를 끝까지 읽지 못하거나, blocking이 될 위험성이 있다. 

그렇기 때문에 자바 어플리케이션 내에서 inputstream으로 값을 읽어올 때에는 주의가 필요하다.

 

이 문제 때문에 일반적인 프로토콜에서는 헤더에 메시지의 길이를 포함시키거나(Http content-length) EOF(http header의 경우 \r\n 으로 body와 구분하도록 해놨다)를 따로 지정하도록 되어 있다. 구현에 참고하도록 하자.

'개발 > 아파치와 톰캣' 카테고리의 다른 글

Tomcat HTTP location Header 이슈  (1) 2019.08.18
ajp 요청 헤더2  (0) 2019.01.20
AJP 요청 헤더  (0) 2018.10.27
ajp 프로토콜 설정  (0) 2018.07.08
아파치 간단 빌드  (0) 2018.07.06
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함