본문 바로가기
👨‍🏫Study/JAVA

[JAVA] 19. NIO 기반 입출력 및 네트워킹(2)

by 코푸는 개발자 2022. 4. 6.
728x90

1.버퍼

  • NIO에서는 데이터를 입출력하기 위해서 항상 버퍼를 사용한다.
    • 버퍼는 읽고 쓰기가 가능한 메모리 배열이다.

 

 

 

 

 

Buffer 종류

  • Buffer는 저장되는 데이터 타입에 따라 분류될 수 있고, 어떤 메모리를 사용하느냐에 따라서 다이렉트(Direct)와 넌다이렉트(NonDirect)로 분류 할 수 있다.

 

데이터 타입에 따른 버퍼

  • NIO버퍼는 저장되는 데이터 타입에 따라서 별도의 클래스로 제공
  • 버퍼 클래스들은 Buffer 추상 클래스를 모두 상속

 

 

 

 

 

  • 버퍼 클래스의 이름을 보면 어떤 데이터가 저장되는 버퍼 인지 쉽게 알 수 있다.
  • MappedByteBuffer는 ByteBuffer의 하위 클래스로 파일의 내용에 랜던하게 접하기 위해서 파일의 내용을 메모리와 맵핑 시킨 버퍼

넌다이렉트 다이렉트 버퍼

  • 버퍼가 사용하는 메모리의 위치에 따라 구분됨

 

넌다이렉트 버퍼 : JVM이 관리하는 힙 메모리 공간을 이용하는 버퍼

  • 버퍼 생성 시간 빠름, 버퍼 크기 작음, 입출력 성능 낮다.

 

다이렉트 버퍼 : 운영체제가 관리하는 메모리 공간을 이용하는 버퍼 

  • 버퍼 생성 시간 느림, 대용량 버퍼 생성 가능, 입출력 유리

 

Buffer 생성

  • 각 데이터 타입별로 넌다이렉트 버퍼를 생성하기 위해서는 각 Buffer클래스의 allocte()와 wrap 메소드를 호출하면 됨, 다이렉트 버퍼는 ByteBuffer의 allocateDirect()메소드를 호출하면 된다.

 

allocate() 메소드

  • 위에 언급했듯 allocate() 메소드는 JVM 힙 메모리에 넌다이렉트 버퍼를 생성한다.

 

리턴 타입     메소드(매개 변수)      설명
XXXBuffer  XXXBuffer.allocate(int capacity)  capacity개 만큼의 XXX(타입)값을 저장

 

ex. 최대 100개의 바이트를 저장하는 ByteBuffer 생성, 최대 100개의 문자를 저장하는 CharBuffer 생성 코드

ByteBuffer byteBuffer = ByteBuffer.allocate(100);
CharBuffer charBuffer = CharBuffer.allcate(100);

 

 

 

 

wrap() 메소드

  • 각 타입별 Buffer클래스는 모두 wrap()메소드를 가지고 있다.
  • wrap()메소드는 이미 생성되어 있는 자바 배열을 래핑해서 Buffer객체를 생성한다.
  • 자바 배열은 JVM힙 메모리에 생성되므로 wrap()은 넌다이렉트 버퍼를 생성한다.

 

ex. 길이 100인 byte[]를 ByteBuffer로 생성, 길이 100인 char[]를 이용하여 CharBuffer 생성

byte[] byteArray = new byte[100];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
char[] charArray = new char[100];
CharBuffer charBuffer = CharBuffer.wrap(charArray);
  • 배열의 일부 데이터만 가지고 Buffer 객체를 생성할 수도 있다.
byte[] byteArray = new byte[100];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray, 0, 50); //0인덱스부터 50개만 버퍼로 생성

 

allocateDirect() 메소드

  • 운영체제가 관리하는 메모리에 다이렉트 버퍼를 생성한다.
  • 이 메소드 각 타입별 Buffer 클래스에는 없고, ByteBuffer에만 제공된다.
    • 우선 ByteBuffer의 allocateDirect()메소드로 버퍼 생성한 다음 asXXXBuffer() 형태에 메소드를 붙여 해당 타입을 지정

 

ex.

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100); // 100개의 byte값 저장 가능
CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer(); // 50개의 char값 저장 가능 (byte*2 = char)
IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer();// 25개의 int값 저장 가능 (byte*4 =int)

 

Buffer의 위치 속성

  • Buffer의 사용 방법을 알기 위해서 Buffer의 위치 속성 개념과 위치 속성이 언제 변경되는지 알고 있어야 한다.

 

Buffer 사용을 위한 관련 속성

 

 

 

 위 속성의 관계는 0 <= mark <= position <= limit <= capacity 이다

  • 위 속성들은 생성된 버퍼에 데이터를 쓰거나 읽을 때 변화는 버퍼의 위치들을 말한다.

 

position : 데이터를 쓸때 버퍼의 배열에 데이터를 하나씩 넣는다. position은 데이터를 넣을 위치를 알려준다.

limit : 데이터를 쓰기 상태에서 flip()메소드를 호출하면 읽기 상태로 변경되는데 이때 position이 있던 위치가 limit이 되어 자리를 표시

capacity : 버퍼 생성 시 지정해준 버퍼의 크기

mark : mark() 메소드로 mark를 정해주면 reset()메소드를 사용하면 mark가 있던 자리로 position이 온다.

 

 

Buffer 메소드

 

Buffer 공통 메소드

 

  • 각 타입별 버퍼 클래스는 Buffer 추상 클래스를 상속한다. Buffer 추상 클래스는 모든 버퍼가 공통적으로 가져야 할 메소드들이 정의됨

 

 

 

 

 

데이터를 읽고 저장하는 메소드

  • 버퍼에 데이터를 저장하는 메소드는 put()이고, 데이터를 읽는 메소드는 get()이다
  • 이 메소드는 Buffer 추상 클래스에 없고, 각 타입별 하위 Buffer 클래스가 가지고 있다.
  • get()과 put() 메소드는 상대적(Relative)과 절대적(Absolute)으로 구분된다.

 

상대적 get()과 put()메소드 : posotion을 이용해서 데이터를 읽고 저장

절대적 get()과 put()메소드 : 버퍼 배열의 인덱스를 가지고 데이터를 읽고 저장

 

※각 타입 버터 클래스별로 get(), put()있는다. 종류는 많으나 같은 내용이 반복되므로 생략한다.

 

Buffer 변환

  • 체널이 데이터를 읽고 쓰는 버퍼는 모두 ByteBuffer이다.
    • 채널을 통해 읽은 데이터를 복원하려면 ByteBuffer를 문자열 또는 다른 타입 버퍼(CharBuffer, ShortBuffer 등등)으로 변환해야 함
  • 반대로 문자열 또는 다른 타입 버퍼의 내용을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환 해야 한다.

 

ByteBuffer <-> String

 

  • 채널을 통해 문자열을 파일이나 네트워크로 전송하려면 특정 문자셋으로 인코딩해서 ByteBuffer로 변환해야함
    • 파일과 네트워크 운영체제로 주고 받기 때문에
  • 이를 위해 java.nio.charset.Charset 객체가 필요

 

Charset 생성 방법 2가지

Charset charset = Charset.forName("UTF-8"); //매개값으로 주어진 문자셋
Charset charset = Charset.defaultCharset(); //운영체제가 사용하는 디폴트 문자셋
  • Charset을 이용하여 문자열을 ByteBuffer로 변환하려면 encode()메소드 호출한다.
String data = "...";
ByteBuffer byteBuffer = charset.encode(data);
  • 반대로 파일이나 네트워크로부터 읽은 ByteBuffer가 특정 문자셋으로 인코딩되어 있을 경우. 해당 문자셋으로 디코딩해야 문자열 복원할 수 있다.
  • Charset은 ByteBuffer를 디코딩해서 CharBuffer로 변환시키는 decode()메소드 제공
ByteBuffer byteBuffer = ...;
String data = charset.decode(byteBuffer).toString();

 

 

 

ByteBuffer <-> IntBuffer

 

 

  • IntBuffer는 한 배열당 4개의 byte를 저장할 수 있다. 그래서 IntBuffer -> ByteBuffer 할때 ByteBuffer의 크기를 capacity()*4한다.
  • ByteBuffer -> IntBuffer의 경우 ByteBuffer의 asIntBuffer()메소드를 사용하여 IntBuffer로 변환해준다. 
    • ByteBuffer <-> IntBuffer 간의 변환만 이해하면 나머지 ShortBuffer,LongBuffer 등이 쉽게 예측할 수 있다. 
728x90

댓글