2011년 6월 10일 금요일

ByteBuffer 사용

참고: http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html

네트워크 통신 등의 버퍼를 사용하는 작업에는 ByteBuffer 클래스를 많이 사용한다.
byte 배열을 사용하는 경우 잦은 GC(garbage collection) 가 성능에 영향을 줄 수 있다.

목차
1)ByteBuffer instance 생성: allocate, allocateDirect
2)ByteBuffer 초기화: clear
3)ByteBuffer 사용 가능 크기 변경: limit
4)ByteBuffer 쓰기: put*
5)ByteBuffer 읽기: get*
6)ByteBuffer 읽기/쓰기 작업 병행시: flip
7)ByteBuffer 에 남은 데이터 처리: compact


1)ByteBuffer instance 생성: allocate, allocateDirect
ByteBuffer 클래스에는 allocate 함수와 allocateDirect 함수가 있다.
두 함수 모두 static 이고 int 값을 파라미터로 전달받으며 버퍼의 크기를 지정한다.

예)
ByteBuffer buffer = ByteBuffer.allocate(1024);
allocateDirect 함수의 경우 사용하는 플랫폼(안드로이드 등의 모바일 플랫폼) 에 따라 정상적으로 동작하지 않는 경우도 있으므로 반드시 플랫폼이 지원을 하는지 테스트해 보고 사용하길 권한다.

2)ByteBuffer 초기화: clear
clear 함수는 해당 ByteBuffer instance 가 생성되었을 때 상태로 만들어 준다.

3)ByteBuffer 사용 가능 크기 변경: limit
limit(int newLimit) 함수를 사용하여 쓰기 가능 영역 크기를 조정할 수 있다.
파라미터로 넘기는 값이 allocate 로 지정한 capacity 를 초과할 수 없다.

예)
ByteBuffer buffer = ByteBuffer.allocate(1024);

buffer.limit(); // 1024 를 리턴

buffer.limit(50);

//buffer.limit(2048); // 허용되지 않음

buffer.limit(); // 50 를 리턴

4)ByteBuffer 쓰기: put*
put* 함수들을 이용해서 데이터를 ByteBuffer 에 넣어준다.

예)
ByteBuffer buffer = ByteBuffer.allocate(1024);

buffer.put((byte)0x01);

buffer.put(new byte[] {0x02, 0x03});

buffer.put((byte)0x04);
결과:
buffer.array() => [0x01, 0x02, 0x03, 0x0x04, ...]

5)ByteBuffer 읽기: get*
get* 함수들을 이용해서 원하는 데이터 크기만큼 데이터를 읽을 수 있다.
get* 함수를 이용해서 데이터를 읽으면 다음 get* 함수 이용시 이전 get* 함수를 이용해 읽은 다음 부분부터 값을 읽는다.

예)
ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});

byte a = buffer.get();

byte b = buffer.get();

byte[] c = new byte[2];

buffer.get(c);

byte d = buffer.get();

결과:
a => 0x01
b => 0x02
c => [0x03, 0x04]
d => 0x05

6)ByteBuffer 읽기/쓰기 작업 병행시: flip
put* 함수를 이용해서 값을 쓰거나 SocketChannel 의 read 함수로 ByteBuffer instance 에 값을 쓰고 나서 데이터를 읽기 전에 ByteBuffer 클래스의 flip 함수를 한번 호출해야 한다.

예) http://download.oracle.com/javase/6/docs/api/java/nio/Buffer.html#flip()
buf.put(magic); // Prepend header

in.read(buf); // Read data into rest of buffer

buf.flip(); // Flip buffer

out.write(buf); // Write header + data to channel
buf.put(magic) 과 in.read(buf) 모두 buf 에 데이터를 쓴다.
데이터를 쓰고 나서 buf.flip() 으로 모드 변환을 해주고
out.write(buf) 로 buf 의 내용을 읽는다.

7)ByteBuffer 에 남은 데이터 처리: compact
socket 통신의 경우 buffer 에 읽을 수 있는 양이 찰 때까지 read 함수로 buffer 를 채우고 buffer 가 충분히 차면 의미있는 범위까지 데이터를 읽고 buffer 의 남은 부분을 유지하고 남은 부분 다음부터 데이터를 채우는 작업을 반복하는 경우가 있다.

이때 데이터의 남은 부분을 buffer 의 앞으로 이동시키고 남은 부분 다음부터 쓸 수 있게 하는 함수가 compact 함수이다.

예)
ByteBuffer buffer = ByteBuffer.allocate(1024);

byte[] data = new byte[16];

buffer.clear(); // ByteBuffer 초기화

int len = 0;

while ((len = in.read(buffer)) > 0) { // ByteBuffer 쓰기

 buffer.flip(); // 쓰기 작업이 끝나고 읽기 작업을 위해 flip

 while (buffer.remaining() > 16) { // remaining 함수는 읽을 수 있는 크기를 리턴

  buffer.get(data); // 16 바이트만큼 데이터를 읽음
  
  // data 처리

 }

 buffer.compact(); // 남은 부분을 ByteBuffer 맨 앞으로 이동 

 // 다음 read 작업에서 남은 부분 다음부터 채움
}

댓글 없음:

댓글 쓰기