관리 메뉴

드럼치는 프로그래머

[TCP/IP] 자바 소켓 기다리는 read와 기다리지않는 read 본문

★─Programing/☆─TCP IP

[TCP/IP] 자바 소켓 기다리는 read와 기다리지않는 read

드럼치는한동이 2013. 6. 14. 09:50

스트림으로부터 데이터를 읽으려고 스트림의 read 메소드를 호출했을 때 read 메소드가

어떻게 반응하는지 정확하게 알아야 합니다.

왜냐하면 read 메소드가 여러분의 예상과는 다르게 반응할 때도 있기 때문입니다.

물론 read 메소드를 호출할 때 예상과는 다르게 read 메소드가 반응한다고 해서

read 메소드의 잘못은 아닙니다. 그것은 read 메소드를 호출하는 스트림의 종류에 따라서

read 메소드의 결과가 달라질 수 있기 때문입니다.

자바에는 크게 두가지의 read()가 있습니다. 기다리는 read()와 기다리지 않는 read()

부르는데,기다리는 read()를 느린 read() 라고도 부릅니다.

스트림이 파일등으로부터 만들어진 것 이라면 read()는 기다리지 않는 read()입니다.

반면 네트워크로 부터 만들어진 스트림에서 하는 read()는 모두 기다리는 read()입니다.

기다리는 read()와 기다리지 않는 read()의 차이는 아주 중요합니다.

기다리지 않는 read()는 호출하자마자 결과값을 얻을 수 있습니다. 스트림에 읽을 데이터가

없다고 해도 데이터가 없다는 표시로 많이 사용되는 -1을 돌려줍니다.

하지만 기다리는 read()는 스트림에 데이타가 존재해서 읽을 수 있을때 까지 기다립니다.

, 기다리는 read()의 경우는 호출한 다음 언제까지 기다리고 있을 수 있다는 것입니다.

심지어는 영원히 기다릴 수 있습니다. , 느린 read()를 호출한 후에 프로그램이 멈춰 설

수도 있습니다.

 

읽기 할때 반드시 알아야 하는 것

 

스트림에서 기다리는 read()를 하는 경우 시간이 하염없이 지나도 read()가 리턴되지

않고 계속 기다리고 있을 수 있는데(물론 마냥 기다리는 경우는 스트림에 읽을 데이터가 없는 경우입니다.) 상용 프로그램등에서는 반드시 이런 경우를 대비해야 합니다.

일반적으로 타이머 기능을 두는 것이 가장 간명합니다. 스트림으로부터 데이타를 읽기

위해서 무한정 기다리기만 할 수 없기 때문입니다.

만약 무한정 기다리고 있는 상황이라면, 프로그램 사용자의 눈으로 보기에는 흔히

"프로그램이 먹통이다"라고 부르는 상태가 되겠지요. 이것이 read()할때 알아야하는 첫째 상식입니다.

 

read()를 하는 경우 스트림으로부터 얼마나 많은 데이터를 읽어낼까요?

             InputStream is = s.getInputStream();

             int bytes = is.read(byte[] b, 0, 300);

예제 6 - 5

예제 6 - 5처럼 스트림에 public int read(byte[] b, inf offset, int len) 메소드를 호출했다면

그 결과는 어떻게 될까하는 문제입니다.

예제 6 - 5와 같이 스트림에 read(byte[] b, 0, 300)을 호출한 프로그래머의 생각은 스트림

is로 부터(바이트 스트림이겠지요. 메소드의 형태를 보니…) 데이터 300 바이트를 읽어서,

byte배열 b 0번째 위치부터 저장하려고 하는 것 같습니다.

그러나 예제 6 - 5는 프로그래머의 생각대로 될때도 있고, 그렇지 않을 때도 있습니다.

 

InputStream read() 메소드가 스트림으로부터 데이터를 읽은 후에 리턴했을 때,read()

-1을 리턴할 때가 있는데, 이 경우는 스트림의 끝에 도착한 경우입니다.

더 읽을 데이터가 없다는 뜻입니다. 그외에 read()가 양의 정수값을 리턴했을 때

read()는 스트림으로부터 양의 정수값 만큼의 데이터를 읽었다는 뜻입니다.

적어도  read()는 스트림으로부터 어떤 양의 정수값만큼의 데이터를 읽었다는 것을

확신할 수 있습니다.

다만 예제 6 - 5의 경우는 프로그래머는 300바이트를 읽고 싶어하지만 정말 300바이트를

읽어냈는지는 자신있게 말할 수 없습니다.

즉 리턴값은 300을 포함한 값, 그러니까 항상 300이하의 값을 가지게 됩니다.

 

InputStream이 만약 기다리지 않는 read()를 하는 파일등으로부터 만들어진 것이라면

리턴값은 일반적으로 인자로 주어진 값과 같은 값의 데이터를 읽습니다.

, 예제 6 - 5의 경우는 300 바이트의 정보를 항상 읽어 낸다는 뜻입니다.

(물론 파일의 끝부분이라면, 즉 읽을 수 있는 정보가 300 바이트 이하라면, 읽을 수

있는 정보 전부를 읽게되고, 리턴값은 읽을 수 있는 정보의 길이입니다.)

하지만 InputStream이 기다리는 read()를 하는 네트워크로부터 만들어진 것이라면

리턴값은 일반적으로 인자로 주언진 값보다 작은 값은 데이터를 읽습니다.

물론 네트워크 사정에 따라서 좀 달라지긴 하지만 말입니다.

 

 네트워크로부터 만들어진 스트림에서 read()를 한 경우와 파일등으로부터 만들어진 스트림

에서 read()를 한 경우가 조금 다르게 반응하는 것은 TCP라는 네트워크 레이어의 특성

때문입니다.

네트워크로 만들어진 스트림으로부터 읽고,쓰기를 할때는 TCP의 도움을 받게 되는데

TCP는 최선을 다해서 네트워크로부터 데이터를 읽어오게 되는데, 프로그램에서 네트워크

스트림에 read()를 하게 되면 당시 TCP버퍼에 도착해있는 바이트 바로 바로 read()에게

돌려줍니다. 즉 스트림에 read()를 호출하게 되면, read()를 호출한 당시 TCP버퍼에

도착해 있는 데이터를 읽게 된다는 뜻입니다.

이런 이유로 read()할때 읽고 싶은 바이트의 수를 지정하지만, read()를 해서 얼마의 바이트

를 읽어 내는지는 아무도 모릅니다. 다만 얼마나 읽었는지는 read()의 리턴값으로 정확하게

알수 있습니다. 이것이 읽기할 때 반드시 알아야할 두번째입니다.

 

 예제 6 - 5의 경우가 만약 네트워크로부터 만들어진 InputStream으로부터 읽고 있는

것이라면, 300바이트를 읽을 수도 있고, 28바이트를 읽을 수도 있고, 212 바이트를 읽을

수도 있습니다. 정확히 얼마를 읽을 지는 알 수 없지만 리턴값 bytes를 살펴보면

정확히 몇 바이트를 읽었는지는 알 수 있습니다.

대부분 네트워크 스트림에 read()를 호출했을 때 다양한 결과를 나타내는 이유는

네트워크 상황에 따라 다르게 되므로, 한번 원하는 대로 300바이트를 읽는다고 해서

모든 경우에 300 바이트를 읽어낼 수 있는 것은 아닙니다.

 

여기서 필자가 꼭 전하고 싶은 메시지는 스트림으로부터 데이터를 읽고자 read()한 경우

읽어낸 데이터와 메소드의 인자로 지정한 읽고 싶은 데이터의 양이 일치하지 않는다는

것입니다.

 이런 이유로 필자는 느린 read()인 경우 정확한 public int read()메소드를 선호합니다.

다른 특별한 이유는 없습니다. 항상 일정하게 한 바이트 읽는 것을 보장할 뿐만 아니라

가장 간명한 형태의 메소드이기 때문에 즐겨 사용합니다.

 

OutputStream InputStream과 마찬가지로 기본적으로 기다리는 write()와 기다리지 않는

write()가 있습니다. 하지만 write()의 경우 현실적으로 기다리는 경우가 거의 없는데

거기에는 이유가 있습니다.

read()와는 달리 write() TCP 버퍼까지만 write()하게 되면 바로 린터하기 때문입니다.

즉 데이터가 상대방 네트워크의 끝까지 도착했는지 여부는 전혀 알 수 없습니다.

데이터가 상대방 네트워크의 끝까지 도착했는지 관리해주고 보내주는 친구는 TCP이기

때문이지요. 보통 컴퓨터의 TCP 버퍼는 최소한 2 16제곱 만큼은 됩니다.

TCP 버퍼가 꽉 차기 전까지는 write()는 성공하게 되고, CP 버퍼가 가득차서

더 이상 공간이 없어질 만큼 네트워크 반대쪽에서 read()를 늦게 하지는 않기 때문입니다.

하지만 TCP 버퍼가 가득 찰때 까지 네트워크 반대편에서 read()를 하지 않거나

실제로 read()를 하더라도 굉장히 느리게 한다면 write() TCP버퍼에 데이터를 넣을 수

없기 때문에 한참동안이나 기다려야 하는 경우가 생길 수 있습니다.

스트림으로부터 기다리는 read(), write()를 해야하는 경우는 여기서 언급한 두가지 상식은

반드시 이해하고 사용해야 합니다.

 

[출처] http://scarlett.tistory.com/entry/%EC%9E%90%EB%B0%94%EA%B3%B5%EB%B6%80-6%EA%B8%B0%EB%8B%A4%EB%A6%AC%EB%8A%94-%EC%9D%BD%EA%B8%B0%EC%99%80-%EA%B8%B0%EB%8B%A4%EB%A6%AC%EC%A7%80%EC%95%8A%EB%8A%94-%EC%9D%BD%EA%B8%B0

Comments