InputStream.read()

제가 만든 서버에서 보낸 데이터가 xml 형식으로 바이트 스트림으로 해서 받는데 in.read로 받았더니 byte 버퍼에 태그단위로 하나씩 들어가는데다가 디버그해봤더니 in.read라는 문장은 받은 xml 태그 수만큼 반복해서 실행되고 전체 xml을 받아서 파싱하려고 Stringbuffer를 이용해서 append 하려고 했더니 이것도 안되네요..

안녕하세요 질문자님.
질문 내용을 제가 정확히 이해한 건진 모르겠습니다만 제가 이해한 내용은
보내는 서버 측에서는 예를 들어, "<tag>내용</tag>" 스타일의 문자열을  byte[]로 변환하여
outputStream.write( byte[] );
하고 있고 클라이언트 측에서는
[code]
while ((readLength = inputStream.read(input, 0, input.length)) != -1) {
    stringBuffer.append(  new String( input )  );
}
[/code]
과 같은 동작을 하고 있다고 생각이 됩니다.

질문 내용 중 ' in.read라는 문장은 받은 xml 태그 수만큼 반복해서 실행되고' 라고 하신 걸로 미루어보아 데이터는 정상적으로 들어오고 있다고 판단됩니다. 받은 데이터를 가공하는데 어려움을 느끼고 계신 건 아닌가 하는 생각이 드네요.

우선 작성하신 클라이언트 쪽이 서버에서 보내주는 xml을 다 받으면 while 문을 빠져 나오는지 확인해 보시고요. 정상적으로 while문을 빠져 나온다면 유입된 Data를 가공하는 문제가 될 거고 그렇지 않다면 서버 측의 outputStream 에 write() 하는 부분을 확인하셔서 stream을 확실히 끝내고 있는지 확인하셔야 할 것 같습니다.

참고로 제가 작성한 소스를 첨부합니다.
첨부한 소스는 xml 형식의 문서를 읽어들여 byte 형식으로 전송하여 수신부에서 file과 문자열로 복원하는 코드가 작성되어 있습니다. Server.java 와 Client.java 는 일반적인 자바 bolocked Socket 프로그래밍 방식을 취하고 있습니다. 실제로 확인해야 하는 부분은 ByteArrayTransporter 클래스에서 클라이언트에 Data를 전송하는 부분과 Client 클래스에서 java.nio.ByteBuffer 를 이용하여 입력 데이터를 처리하는 부분 정도가 될 거 같습니다.



ByteArrayTransporter.java
[code]
   byte[] byteArray = new byte[1024];
   int readLength = 0 ;
   while ( (readLength = fileInStream.read(byteArray, 0, byteArray.length) ) != -1 ){
     out.write(byteArray, 0, readLength);
   }
   out.flush();
   out.close();  //<-- 스트림 끝내기
[/code]

Client.java
[code]   ByteBuffer bBuffer = ByteBuffer.allocate(102400);
   while ((readLength = bin.read(input, 0, input.length)) != -1) {   //byteBuffer에 byte[] 쓰기
      bBuffer.put(input, 0 ,readLength );
   }   //ByteBuffer 에 담긴 문자열 확인
   System.out.println( new String(bBuffer.array() , "UTF-8" ) );
[/code]

2010/07/20 19:54 2010/07/20 19:54
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 다른 문제로 구글링하다가 들리게 되었는데, 좋은 글 잘 읽었습니다.

    이 페이지에 어울리지 않을지는 모르나 도움을 주실 수 있을 듯하여 한 가지 질문 드리겠습니다.

    <질문> 삼성 갤럭시폰에서 파일도 존재하고 파일크기도 0이 아닌데 FileNotFoundException 에러 나타나는 문제

    구글링해도 제 힘으로 답을 구하지 못해 도움을 청합니다.

    도와주시면 정말로 감사하겠습니다.

    1. 테스트 환경 : 폰(삼성 갤럭시S3/갤럭시S5), 파일(MMS에 첨부된 .vcf 주소록파일), 안드로이드 스튜디오 2.3, 윈7(64비트)

    아래 코드의 실행 결과 : "파일 OK/2025bytes/주소록 내용을 읽을 수 없습니다."

    즉 exists 메소드로 검사하면 파일도 존재하고, length 메소드로 검사하면 파일크기도 0이 아니므로 분명 존재하는 파일입니다.

    그런데 BufferedReader/readLine으로 파일을 읽으면 FileNotFoundException 에러를 내고 파일 내용을 읽지 못합니다.

    다른 테스트 폰 엘지 G2에서는 정상으로 읽힙니다.

    // 주소록 보이기
    public void viewVcf(final String filePath) {
    File vcfFile = new File(filePath);
    if (vcfFile.exists()) {
    long lFileSize = vcfFile.length();
    tv_unique.setText("파일 OK/"+lFileSize+"bytes");
    String content = readFile(filePath);
    if (content.isEmpty())
    tv_unique.setText(tv_unique.getText().toString()+"/주소록 내용을 읽을 수 없습니다.");
    else {
    ...
    }
    }
    else {
    report10sec("주소록 파일이 없습니다.");
    }
    }

    2. 테스트 환경 : 폰(삼성 갤럭시S3/갤럭시S5), 파일(MMS에 첨부된 .mp4 동영상파일), 안드로이드 스튜디오 2.3, 윈7(64비트)

    동영상 파일의 경우도 위와 비슷합니다.

    "파일 OK/707989bytes"

    // 비디오 재생
    public void viewVideo(final String filePath) {
    File videoFile = new File(filePath);
    if (videoFile.exists()) {
    long lFileSize = videoFile.length();
    tv_unique.setText("파일 OK/"+lFileSize+"bytes");
    ...
    }
    else {
    report10sec("비디오 파일이 없습니다.");
    }
    }

    위 ... 부분의 코드에 따라서 2가지 테스트 결과입니다.

    1) startActivity(intent);

    삼성폰, 엘지폰 모두 비디오가 재생되지 않습니다.

    (엘지) java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference

    (삼성) java.lang.NullPointerException

    2) videoView.start();

    엘지폰에서는 정상으로 재생이 됩니다.

    삼성폰에서는 에러 로그는 없으나, "재생할 수 없는 동영상입니다./확인" 메시지가 뜨고 비디오가 재생되지 않습니다.

    읽어 주셔서 고맙습니다.

  2. Blog Icon
    김병희

    <답변> 존재하고 크기도 있는 파일의 FileNotFoundException = 권한 없음/[Android] http://blog.daum.net/andro_java/1121

소켓통신 서버/클라이언트 통신시 IO Blocking 상태에 빠지는 코드와 해결 방안

2008/04/03 19:15

서비 JAVA , , ,

java study 질답 게시판에서 서버-클라이언트 소켓 통신을 하는데 통신이 단절된다는 내용의 질문을 접했습니다.

질문자의 원글 보기..


코드를 보면 지극히 정석으로 코드를 작성 해 둔 상황입니다. 하지만 동작 중 어느 순간이 되면 통신이 안되기 시작하죠..
이는 비단 자바뿐만 아니라 다른 언어로 소켓 프로그래밍을 하더라도 동일한 현상이 발생할 겁니다.
원 질문자께서 저 코드로 어디까지 다버깅을 해 보셨는지는 알지 못하겠지만, 제가 판단하건데 동작이 안되는 시점은
필시 클라언트와 서버 양쪽 모두 while((read = bin.read())!=-1) 문에서 서로의 데이터를 기다리느라
블록 상태에 빠졌기 때문일 겁니다.

왜? 무슨 이유로 read() 에서 블록 상태에 빠졌을까요?

원인은 서버 소켓 쓰레드인 ClientHandler 쪽에서 찾을수 있습니다. 
서버 소켓 코드를 보시면 클라이언트에서 받은 데이타를 읽는게 끝이나면 클라이언트로 데이타를 전송하는
절차적 구조로 작성 되어있는데요..

문제는 클라이언트로부터 데이터가 유입되는
[code]// received InputStream in = null;
BufferedInputStream bin = null;
in = m_socketClient.getInputStream();
bin = new BufferedInputStream(in);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read;
while ((read = bin.read()) != -1) {
    baos.write(read);
}
[/code]
부분이 원인입니다... 감이 좀 잡히시나요?

한번 API를 확인 해 볼 필요가 있겠군요. API를 보시면.. BufferedInputStream의 read() 메소드의 리턴 값을
the next byte of data, or -1 if the end of the stream is reached. 라고 명시하고 있습니다.
'스트림의 끝까지 갔을 때 -1을 리턴 한다'라고.. 데이터가 끝났을 때 -1이 아닙니다.

비록 클라이언트가 보낸 데이타는 끝이 났을지언정 클라이언트와 연결된 스트림은 끝나지 않았단 말이죠.
쉽게 말하자면 bin.read()에서 block된 상황은 BufferedInputStream인 bin이 끝나지 않았으며, 언젠간
새로운 데이터가 유입될테니 대기중이다.
는 얘기지요.

이제 좀 감이 잡히시죠? ^^
결국 지금의 코드 대로라면 서버 소켓은 클라이언트에서 보낼 데이터를 영원히(혹은 time out까지) 기다리고 있는거죠..

이제 원인을 알았으니 해결책에 대한 아이디어도 떠오르셨을거라 생각합니다.
이런 경우 클라이언트 소켓도 쓰레드로 돌려야 하지만 인풋 스트림과 아웃풋 스트림도 각각 쓰레드로 처리 해
주셔야 질문과 같이 blocking으로 어플리케이션이 멈춰있는 문제를 회피할 수 있습니다.
 

2008/04/03 19:15 2008/04/03 19:15
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. Blog Icon
    비밀방문자

    관리자만 볼 수 있는 댓글입니다.

  2. Blog Icon
    Nereid

    관리자님 안녕하세요. 프로그래밍중 안되는 부분이 있어
    해결책을 찾아보다가 여기서 같은 케이스를 찾게되서요.

    제가 초보라 해결책이 잘 떠오르지 않는데
    저런경우 인/아웃 스트림 쓰레드 처리에 대하여 좀 알려주실수 있을까요?

    부탁드립니다. 감사합니다.