티스토리 뷰

Skill/Programming - Network

20. 버퍼 (Buffer)

gyulee0220 2019. 9. 20. 21:21


1. 버퍼의 사용

  지난 시간에 Java NIO에서 버퍼가 어떻게 사용되는지와 더불어 Direct 버퍼와 Non-Direct Buffer에 대해서 알아보았다. 이번 시간에는 버퍼를 Java에서 어떻게 사용 할 수 있는지 알아보도록 하자. 


  버퍼는 Java.nio에 클래스로 소속되어 있다. 그리고 해당 클래스는 우리가 주고받게 될 데이터 양식을 기반으로 두고 있다. 상위에 Buffer 클래스가 존재하고 하위 서브클래스로 데이터 양식에 맞는 클래스가 존재하게 된다.



  버퍼 종류

  설명 

  ByteBuffer

  바이트 타입의 데이터를 받는 버퍼 

  CharBuffer

  문자열 타입의 데이터를 받는 버퍼 

  ShortBuffer

  Short형 타입의 데이터를 받는 버퍼 

  IntBuffer

  정수형 타입의 데이터를 받는 버퍼 

  LongBuffer

  Long형 타입의 데이터를 받는 버퍼 

  FloatBuffer

  Float형 타입의 데이터를 받는 버퍼 

  DoubleBuffer

  Double형 타입의 데이터를 받는 버퍼



  버퍼는 데이터 나열 이외에도 총 4가지의 중요한 정보를 관리한다. 버퍼의 타입에 관계없이 상위 버퍼에서 아래의 4가지 정볼르 가지게 된다. 


- 위치 (Position)


  버퍼에서 읽거나 쓸 다음 위치를 의미한다. 즉 버퍼를 읽을 때 시작되는 지점을 의미하고 시작점은 0이다.


public final int position()

public final Buffer position(int newPosition)


- 용량 (Capacity)


  버퍼가 보유할 수 있는 최대 요소의 수이다. 한번 설정이 되면 변경이 불가능 하기 떄문에 메소드를 통해 읽기만 가능하다.


public final int capacity()


- 한도 (Limit)


  버퍼에서 접근할 수 있는 데이터의 마지막을 의미한다. 위에 있는 용량이 한도보다 더 크다면 한도 뒤에 데이터를 읽거나 쓸 수 없으므로 실질적 사용 용량은 이 한도로 결정 된다.


public final int limit()

public final Buffer limit(int newLimit)


- 표시 (Mark)


  버퍼에서 클라이언트에 제한된 인덱스를 의미한다. 만약 위치가 표시 이하로 설정된다면 표시는 삭제된다.


public final Buffer mark() : 현재 위치에 표시를 설정

public final Buffer reset() : 호출하면 현재 위치가 표시된 위치로 설정





2. 버퍼 활용

  

- 버퍼 할당 (allocate)


  Java에서 버퍼를 선언하는 방법은 다음과 같다. 버퍼를 사용하려는 데이터 타입을 활용해 선언하면 된다. Byte Buffer를 사이즈 100으로 선언하게 된다면 아래와 같다.


ByteBuffer bytebuffer = ByteBuffer.allocate(100);


- 버퍼 접근 (array)


  버퍼의 접근은 배열 인자로 접근 가능하다. array() 메소드를 이용하게 되는데, 버퍼의 내부 데이터를 그대로 노출시킨다.


byte[] data1 = buffer1.array();


- 직접 할당 (allocateDirect)


  앞서 배운 Direct 버퍼를 생성하기 위해서는 allocateDirect 메소드를 사용해야 한다.


ByteBuffer buffer = ByteBuffer.allocateDirect(100);


- 랩핑 (Wrapping)


  이미 출력하고자 하는 데이터의 배열을 가지고 있다면 버퍼를 새로 만들고 채우는 것 보다 배열을 버퍼로 감싸면 된다.


byte[] data = "Some data".getBytes("UTF-8");

ByteBuffer buffer1 = ByteBuffer.wrap(data);


- 위치 및 한도 초기화 (clear)


  위치(Position)를 0으로 설정하고 한도(Limit)를 용량으로 설정하여 초기화 하는 기능을 수행한다.


public final Buffer clear()


- 위치 재설정 (rewind)


  위치(Position)를 0으로 설정하지만 한도는 변경하지 않는다.


public final Buffer rewind()


- 한도 재설정 (filp)


  한도를 현재 위치로 설정하고 위치는 0으로 초기와 한다.


public final Buffer flip()



3. 버퍼의 데이터 읽고 쓰기

- Put


  put() 메소드를 활용하여 버퍼에 데이터를 넣을 수 있다. 데이터를 넣는 방법은 2가지가 있는데, 버퍼 중 데이터를 넣는 위치를 지정하는 방법과 상대적 위치에 데이터를 넣는 2가지 방법이 있다.


  • 특정 위치에 데이터 넣기 : public abstract byte put(int index, byte a);
  • Position이 가르키고 있는 위치에 넣기 : public abstract byte put(byte a);


- Get


  get() 메서드를 활용하면 버퍼에서 데이터를 읽어온다. put과 마찬가지로 인덱스를 지정해서 데이터를 읽어 올 수 있고, 위치가 가르키고 있는 현재 위치를 가져올 수도 있다.


  • 특정 위치에 데이터 넣기 : public abstract byte get(int index, byte a);
  • Position이 가르키고 있는 위치에 넣기 : public abstract byte get(byte a);


  아래는 데이터를 넣고 읽는 방법의 예시 코드이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.nio.ByteBuffer;
 
public class Solution {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        // size 100 Buffer
        ByteBuffer bytebuffer = ByteBuffer.allocate(100); 
        
        System.out.println("Position : " + bytebuffer.position());
        System.out.println("Limit : " + bytebuffer.limit());
        System.out.println("Capacity : " + bytebuffer.capacity());
        
        // Position 0에 Mark
        bytebuffer.mark();
        
        // 순서대로 데이터를 넣는다. (인덱스 미지정)
        bytebuffer.put((byte'a').put((byte'd').put((byte'r').put((byte'i').put((byte'a').put((byte'n');
        
        // Position reset
        bytebuffer.reset();
        
        // 순서대로 데이터를 출력. (인덱스 미지정)
        System.out.println("Relative Get : " + (char) bytebuffer.get() + (char) bytebuffer.get() + (char) bytebuffer.get()
        + (char) bytebuffer.get() + (char) bytebuffer.get() + (char) bytebuffer.get());
        
        // Position 0에 Mark
        bytebuffer.mark();
        
        // 순서대로 데이터를 넣는다. (인덱스 미지정)
        bytebuffer.put(7, (byte'a').put(10, (byte'd').put(12, (byte'r')
                    .put(1, (byte'i').put(6, (byte'a').put(22, (byte'n');
        
        // Position reset
        bytebuffer.reset();
        
        // 순서대로 데이터를 출력. (인덱스 미지정)
        System.out.println("Absolute Get : " + (char) bytebuffer.get(7+ (char) bytebuffer.get(10+ (char) bytebuffer.get(12)
        + (char) bytebuffer.get(1+ (char) bytebuffer.get(6+ (char) bytebuffer.get(22));
 
 
 
    }
 
}
 
cs




4. 버퍼를 이용한 에코 서버

  앞에서 Buffer를 사용하지 않고 Java IO 방식으로 구현 했던 에코 서버를 Java NIO방식으로 구현 가능하다. ByteBuffer를 이용한 에코 서버는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
 
public class Solution {
 
    // Port Number 지정
    public static int DEFAULT_PORT = 7;
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        int port;
        
        // 포트 연결 시도
        try {
            port = Integer.parseInt(args[0]);
        } catch (RuntimeException ex) {
            port = DEFAULT_PORT;
        }
        
        // 포트 연결 대기중
        System.out.println("Listening for connections on Port" + port);
        
        // 서버 소켓 채널
        ServerSocketChannel serverChannel;
        Selector selector;
        
        // 서버 소켓 연결
        try {
                        // Open
            serverChannel = ServerSocketChannel.open();
            ServerSocket serversocket = serverChannel.socket();
            InetSocketAddress address = new InetSocketAddress(port);
            
            // Bind
            serversocket.bind(address);
            serverChannel.configureBlocking(false);
            selector = Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
                        
        } catch (IOException ex) {
            ex.printStackTrace();
            return;
        }
        
        // 연결
        while(true) {
            try {
                selector.select();
            } catch (IOException ex) {
                ex.printStackTrace();
                break;
            }
            
            Set<SelectionKey> readyKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            
            while(iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                
                // Client
                try {
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = 
                                (ServerSocketChannel) key.channel();
                        SocketChannel client = server.accept();
                        System.out.println("Accepted connection from + " + client);
                        client.configureBlocking(false);
                        SelectionKey clientKey = client.register(
                                selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        clientKey.attach(buffer);
                        
                    }
                    if (key.isReadable()){
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        client.read(output);
                    }
                    if (key.isWritable()){
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        output.flip();
                        client.write(output);
                        output.compact();
                    }
                    
                } catch(IOException ex) {
                    key.cancel();
                    
                    // Close
                    try {
                        key.channel().close();
                    } catch(IOException ex2) {}
                }
            }
        }
        
    }
 
}
 
cs



출처 : Java Network Programming - 엘리엇 러스트 해럴드 (O'REILLY)

'Skill > Programming - Network' 카테고리의 다른 글

22. UDP (User Datagram Protocol)  (0) 2019.10.04
21. 채널 (Channel)  (0) 2019.09.27
19. Java NIO (New IO)  (0) 2019.09.11
18. 서버 소켓 프로그래밍 예제 (Java)  (0) 2019.09.08
17. 네트워크 보안 및 보안 소켓  (0) 2019.08.31
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함