Recent Posts
Recent Comments
Link
06-30 12:53
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

NIO Buffer 본문

DEV&OPS/Java

NIO Buffer

ALEPH.GEM 2022. 5. 24. 11:03

direct buffer / non-direct buffer

direct buffer는 운영체제가 관리하는 메모리 공간을 사용하는 버퍼이고, non-direct buffer는 JVM이 관리하는 힙 메모리 공간을 이용하는 버퍼입니다.

direct buffer가 버퍼의 생성은 느리지만 버퍼의 크기가 더 크고 성능이 더 높습니다.

생성시 allocateDirect() 메소드로 생성하면 direct버퍼로 생성되고 allocate()메소드로 생성하면 non-direct로 생성됩니다.

 

Buffer

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;

public class BufferCapacityEx {

	public static void main(String[] args) {
		ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
		System.out.println("용량:"+byteBuffer.capacity() + "byte");
		CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer();
		System.out.println("용량:"+charBuffer.capacity() + "문자");
		IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer();
		System.out.println("용량:"+intBuffer.capacity() + "자리 정수");
	}

}

 

Big-endian / Little-endian

운쳥체제마다 이진수(byte)를 처리할 때 처리 순서가 다를 수 있습니다.

그래서 운영체제로 데이터를 보내거나 받을 때 처리 순서가 영향이 있을 수 있습니다.

이진수 배열 앞쪽 바이트 먼저 처리하는 것을 Big-endian이라고 하고 뒤쪽 바이트 먼저 처리하는 것을 Little-endian이라고 합니다.

자바의 JVM은 Big-endian으로 처리하지만 윈도우의 경우 Little-endian으로 처리합니다.

import java.nio.ByteOrder;

public class EndianEx {

	public static void main(String[] args) {
		System.out.println("운영체제:"+System.getProperty("os.name"));
		System.out.println("엔디안:"+ByteOrder.nativeOrder());
	}

}

direct 버퍼일 경우 운영체제의 native I/O를 사용하므로 운영체제의 기본 엔디안 순서로 JVM의 엔디안을 맞추는 것이 성능에 도움 됩니다.

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100).order(ByteOrder.nativeOrder());

 

기본 개념

position: 현재 읽거나 쓰려고 하는 위치입니다. 
인덱스 값이기 때문에 0부터 시작하며 limit보다 클 수 없습니다.
만약 position과 limit이 같아진다면 더이상 데이터를 읽거나 쓸 수 없습니다.
limit: position이 가질 수 있는 한계값 입니다.
capacity 값보다 클 수 없습니다.
최초로 버퍼를 만들었을 때는 capacity와 같은 값을 갖습니다.
capacity: 버퍼의 최대 데이터 개수(메모리 크기)입니다.
인덱스 값이 아니라 수량(크기) 입니다.
mark: reset시 돌아오는 위치를 지정하는 인덱스입니다.
반드시 position 이하 값으로 지정해줘야 합니다. 
postion이나 limit값이 mark 값 보다 작아지면 mark는 제거됩니다.
mark가 없는 상태에서 reset()하면 InvalidMarkException이 발생합니다. 
allocate(capacity): JVM의 힙영역에 할당합니다.
allocateDirect(capacity): 버퍼를 운영체제가 할당합니다. 운영체제를 이용하는 I/O작업이면서 할당과 해제가 잘 일어나지 않는 작업일 때 사용을 권고하고 있습니다.
포인터 반환 주요 method
int capacity(): 버퍼의 전체 크기를 반환 합니다.
int position(): 포인터의 시작 위치를 반환 합니다.
int limit(): 포인터의 마지막 위치를 반환합니다. 기본값은 capacity와 동일 합니다.
주요 method
clear(): position을 0 으로 설정하고 limit를 capacity값으로 설정하여 초기화 합니다.
flip(): position을 0 으로 설정하고 limit를 현재 버퍼에 저장된 내용의 마지막 위치로 이동합니다. read 후 write하기 전에 호출합니다.
hasRemaining(): position과 limit가 다르면 true, 같으면 false 반환합니다. 즉, 버퍼에 내용이 있는지 검사해서 내용이 있으면 true, 없으면 false를 반환합니다.
rewind(): position을 0으로 초기화 합니다. 이미 put한 것을 취소할 때 사용합니다.
wrap(byte[] array): 바이트 배열을 ByteBuffer로 변환합니다. capacity와 limit는 바이트배열과 같습니다.
compact(): position과 limit 사이 데이터를 버퍼 맨 앞쪽으로 이동시킵니다.  그 후 position을 이동한 데이터의 다음 위치로 이동하고 limit는 capacity값으로 변경시킵니다. 즉, buffer에 남은 데이터들을 앞으로 이동시키고 그 다음부터 데이터를 채울 수 있게 해줍니다.
put(byte): 버퍼에 byte 데이터를 입력합니다.
get(): 이전 get()을 이용해서 읽은 다음 부분부터 값을 읽습니다.

 

 

import java.nio.Buffer;
import java.nio.ByteBuffer;

public class BufferEx {

	public static void main(String[] args) {
		ByteBuffer buffer = ByteBuffer.allocateDirect(7);	//7바이트 크기로 다이렉트 버퍼 생성
		printState(buffer);
		
		buffer.put((byte)10);
		buffer.put((byte)11);
		System.out.println("[2바이트 저장 이후]");
		printState(buffer);
		
		buffer.put((byte)12);
		buffer.put((byte)13);
		buffer.put((byte)14);
		System.out.println("[3바이트 저장 이후]");
		printState(buffer);
		
		//버퍼 데이터를 읽기 위해 position 0으로 이동
		buffer.flip();
		System.out.println("[flip() 실행 후]");
		printState(buffer);
		
		buffer.get(new byte[3]);
		System.out.println("[3바이트 읽은 후]");
		printState(buffer);
		
		//현재 position mark(표시)
		buffer.mark();
		System.out.println("-----현재 위치 mark-----");
		
		buffer.get(new byte[2]);
		System.out.println("[2바이트 읽은 후]");
		printState(buffer);
		
		//mark된 위치로 position 이동(reset)
		buffer.reset();
		System.out.println("[position을 mark 위치로 이동 후]");
		printState(buffer);
		
		//position을 0으로 이동
		buffer.rewind();
		System.out.println("[rewind() 실행 후]");
		printState(buffer);
		
		//모든 위치 속성값 초기화
		buffer.clear();
		System.out.println("[clear() 실행 후]");
		printState(buffer);
		
	}
	
	public static void printState(Buffer buffer) {
		System.out.print("position:"+buffer.position() + ",");
		System.out.print("limit:"+buffer.limit()+",");
		System.out.println("capacity:"+buffer.capacity());
	}

}

 

 

import java.nio.ByteBuffer;

public class CompactEx {

	public static void main(String[] args) {
		System.out.println("[7바이트 크기로 버퍼 생성]");
		ByteBuffer buffer = ByteBuffer.allocateDirect(7);
		buffer.put((byte)10);
		buffer.put((byte)11);
		buffer.put((byte)12);
		buffer.put((byte)13);
		buffer.put((byte)14);
		
		buffer.flip(); 		//데이터를 읽기 위해 위치(position)를 0으로 변경
		printState(buffer);
		
		buffer.get(new byte[3]);
		System.out.println("3바이트 읽음");
		
		buffer.compact(); 		//읽지 않은 데이터는 인덱스0부터 복사
		System.out.println("[compact()실행 후]");
		printState(buffer);
		
	}

	private static void printState(ByteBuffer buffer) {
		System.out.print(buffer.get(0) + ", ");
		System.out.print(buffer.get(1) + ", ");
		System.out.print(buffer.get(2) + ", ");
		System.out.print(buffer.get(3) + ", ");
		System.out.println(buffer.get(4));
		System.out.print("position:"+buffer.position() + ", ");
		System.out.print("limit:"+buffer.limit() + ", ");
		System.out.println("capacity:"+buffer.capacity());
	}

}

 

버퍼 변환

채널이 읽고 쓰는 버퍼는 ByteBuffer이기 때문에 문자열 또는 다른 타입의 버퍼로 변환해야 합니다.

반대로 문자열 등을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환해야 합니다.

 

ByteBuffer와 String 사이의 변환

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

public class ByteBufferToStringEx {

	public static void main(String[] args) {
		Charset charset = Charset.forName("UTF-8");
		
		//String -> ByteBuffer
		String data = "변환 테스트";
		ByteBuffer byteBuffer = charset.encode(data);
		
		//ByteBuffer -> String
		data = charset.decode(byteBuffer).toString();
		System.out.println(data);
	}

}

 

ByteBuffer와 IntBuffer 사이의 변환

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;

public class ByteBufferToIntBufferEx {
	public static void main(String[] args) throws Exception{
		//int[]->IntBuffer->ByteBuffer
		int[] writeData = {10, 20};
		IntBuffer writeIntBuffer = IntBuffer.wrap(writeData);
		ByteBuffer writeByteBuffer = ByteBuffer.allocate(writeIntBuffer.capacity()*4);
		for(int i = 0; i<writeIntBuffer.capacity(); i++) {
			writeByteBuffer.putInt(writeIntBuffer.get(i));
		}
		writeByteBuffer.flip();
		
		//ByteBuffer -> int[]
		ByteBuffer readByteBuffer = writeByteBuffer;
		IntBuffer readIntBuffer = readByteBuffer.asIntBuffer();
		int[] readData = new int[readIntBuffer.capacity()];
		readIntBuffer.get(readData);
		//원래 배열 복원
		System.out.println(Arrays.toString(readData));
	}
}

 

 

 

 

 

 

 

 

 

728x90

'DEV&OPS > Java' 카테고리의 다른 글

NIO TCP 서버/클라이언트 채팅 프로그램  (0) 2022.05.26
NIO 파일 채널  (0) 2022.05.25
NIO Path  (0) 2022.05.23
IO(블로킹)기반 자바 네트워킹  (0) 2022.05.20
입출력 보조 스트림  (0) 2022.05.19