네트워크 프로그래밍 - 채널

chance·2020년 6월 8일
0

스트림 vs. 채널

  • 스트림
    • byte-based
    • 데이터를 바이트 단위로 읽거나 쓴다.
    • 읽기와 쓰기 중 하나만 할 수 있다.
  • 채널
    • block-based
    • 데이터를 버퍼에서 한번에 읽거나 쓴다.
    • 읽기와 쓰기를 같이 할 수 있다.

네가지 property

  • position
    • 읽거나 쓸 버퍼의 다음 위치
    • 0부터 세고, 최댓값은 버퍼의 크기
  • capacity
    • 버퍼가 저장할 수 있는 요소의 최대 개수
    • 버퍼가 만들어질 때 정해짐
  • limit
    • 접근 가능한 데이터의 끝
    • 이 지점을 읽거나, 쓰거나, 아니면 지나칠 수 없음
  • mark
    • 클라이언트가 지정하는 인덱스
    • mark method: mark가 현재 position으로 지정된다.
    • reset: position이 mark로 지정된다.
    • mark보다 position이 작게 설정된다면 mark는 버려진다.

limit과 capacity의 용도 차이

capacity는 버퍼 자체가 요소를 저장할 수 있는 최대 개수인 반면, limit은 쓴만큼만 읽어야 하기 때문에 읽기에서 쓰기로 전환할 때 position의 값을 저장하는 역할을 한다.

초기화

  • position은 0, mark는 undefined로 초기화
  • 각각의 요소는 0으로 초기화

methods

  • clearing
    • position을 0으로 옮기고 limit을 capacity로 옮긴다.
    • 버퍼를 쓸 준비를 한다.
  • rewinding
    • postion을 0으로 옮긴다.
    • 버퍼가 다시 읽히도록 해준다.
  • flipping
    • position을 0으로 옮기고 limit을 position이 있던 자리로 옮긴다.
    • write한 버퍼를 read하려고 준비할 때 사용한다.
  • remaining
    • position과 limit 사이의 elements의 수
    • hasRemaining도 있음

버퍼의 생성

새롭게 데이터를 넣을 때

  • 버퍼는 다음과 같이 생성한다.
ByteBuffer buffer1 = ByteBuffer.allocate(capacity);

capcity 크기의 메모리를 가진 array가 heap에 생성된다.
접근자인 buffer1 객체를 통하여 값을 읽거나 쓴다.

  • buffer는 java array 위에 구현된다. 이를 backing array라 부른다.
  • java array를 가져오고 싶다면 array() method를 호출한다.
  • array() method로 가져온 배열의 값을 변경하면 buffer에도 반영이 된다.

기존의 데이터를 이용할 때

  • wrap method: 기존에 있던 데이터를 이용하여 만들수도 있다.

Direct Allocation

  • backing array를 사용하지 않고 직접 메모리 접근

Read and Write

  • get
    • buffer에서 값을 읽어올 때 사용
    • 1바이트를 읽고 position을 1만큼 증가시킨다.
  • put
    • buffer에 값을 쓸 때 사용
    • 1바이트를 쓰고 position을 1만큼 증가시킨다.
  • bulk
    • Read operation에 해당
    • 인자로 byte array를 입력받아서 array를 모두 채우려고 한다.

버퍼의 에러

write 언더플로우

ByteBuffer buf = ByteBuffer.allocate(10);
buf.position(5);
buf.mark();
System.out.println("Position: " + buf.position() + ", limit:  " + buf.limit());

byte[] b = new byte[15];
for(int i=0; i<b.length; i++) {
	b[i] = (byte) i;
}

int size = buf.remaining();
if (b.length < size) {
	size = b.length;
}

System.out.println("Position: "  + buf.position() + ", limit: " + buf.limit());

buf.put(b, 0, size); //b의 0에서 size까지 읽어서 buf에 써라

System.out.println("Position: "  + buf.position() + ", limit: " + buf.limit());

for(int i=0; i< size; i++) {
	System.out.println("byte = " + buf.get());
}

buf의 limit(capaicity)를 넘어서서 데이터를 읽으려 시도하면 java.nio.BufferUnderflowException이 발생합니다.

read 언더플로우

ByteBuffer buf = ByteBuffer.allocate(10);
buf.put(((byte) 0)).put((byte) 1).put((byte) 2).put((byte) 3).put((byte) 4);
buf.mark();
buf.put(((byte) 5)).put((byte) 6).put((byte) 7).put((byte) 8).put((byte) 9);
buf.reset();

byte[] b = new byte[15];

int size = buf.remaining();
if (b.length < size) {
    size = b.length;
}
System.out.println("Position: " + buf.position() + ", limit: " + buf.limit());

buf.get(b, 0, size + 1); //b의 위치 0부터 size + 1까지 buf에서 읽어서 써라

System.out.println("Position: " + buf.position() + " limit: " + buf.limit());

for(int i=0; i < size; i++) {
    System.out.println("byte = " + b[i]);
}

buffer의 capacity를 넘어서서 읽기를 시도할 경우 java.nio.BufferUnderflowException이 발생합니다.

오버플로우

ByteBuffer buf = ByteBuffer.allocate(10);
buf.position(5);
buf.mark();
System.out.println("Position: " + buf.position() + ", limit:  " + buf.limit());

byte[] b = new byte[15];
for(int i=0; i<b.length; i++) {
	b[i] = (byte) i;
}

int size = buf.remaining();
if (b.length < size) {
	size = b.length;
}

System.out.println("Position: "  + buf.position() + ", limit: " + buf.limit());


buf.put(b, 0, size + 1); //b의 0에서 size + 1까지 읽어서 buf에 써라

System.out.println("Position: "  + buf.position() + ", limit: " + buf.limit());
buf.reset();


for(int i=0; i< size; i++) {
	System.out.println("byte = " + buf.get());
}

buf의 capacity를 넘어서서 byte를 쓰려고 하면 java.nio.BufferOverflowException이 발생합니다.

profile
프론트엔드와 알고리즘을 주로 다룹니다.

0개의 댓글