NIO 기반 입출력 및 네트워킹-1

Back-End/Java 2019. 4. 23. 15:33
728x90
반응형

- NIO(New Input/Output) -


새로운 입출력 방식이라는 뜻에서 java.nio 패키지가 포함되었는데, 자바 7로 버전업하면서 자바 IO와 NIO 사이의 일관성 없는 클래스 설계를 바로잡고,

비 동기 채널 등의 네트워크 지원을 대폭 강화한 NIO.2 API가 추가되었다.





- IO와 NIO의 차이점 -


I0와 NIO는 데이터를 입출력한다는 목적은 동일하지만, 방식에 있어서 크게 차이가 난다.
아래 표는 IO와 NIO의 차이점을 정리한 것이다.


 구분

 IO

NIO 

 입출력 방식

 스트림 방식 (입력과 출력이 구분되어 효율 떨어짐)

 채널방식 (양방향으로 입력과 출력이 가능)

 버퍼 방식

 넌버퍼(non-buffer) (속도 느림)

 버퍼(buffer) (속도 빠름)

 비동기 방식

 지원 안 함

지원

 블로킹 / 넌블로킹 방식

 블로킹 방식만 지원

 블로킹 / 넌블로킹 방식 모두 지원




- IO와 NIO의 선택 -


- NIO -


불특정 다수의 클라이언트 연결 또는 멀티 파일들을 넌블로킹이나 비동기로 처리할 수 있기 때문에 과도한 스레드 생성 방지 및 효과적 재사용 가능

또한 운영체제의 버퍼를 이용한 입출력이 가능하기 때문에 입출력 성능이 향상

NIO는 좀 더 다양한 파일의 속성 정보를 제공해주는 클래스와 인터페이스를  java.nio.file,  java.nio.file.attribute 패키지에서 제공하고 있다.




- IO -


대용량 데이터를 처리할 경우에는 IO가 더 유리.

NIO는 모든 작업에 버퍼를 무조건 사용하기 때문에 복잡하므로 받은즉시 처리하는 IO를 사용.

연결 클라이언트 수가 적고, 전송되는 데이터가 대용량이면서 순차적으로 처리될 필요성이 있을 경우에는 IO로 서버를 구현하는 것이 좋다.

파일 속성 정보를 읽기 위해 File 클래스만 제공




-경로 정의(Path)-


NIO의 API에서 파일의 경로를 지정하기 위해 Path를 사용한다.

Path 구현 객체를 얻기 위해서는 java.nio.file.Paths 클래스의 정적 메소드인 get() 메소드를 호출하면 된다.



  Path path = Paths.get (String first, String...more)


  Path path = Paths.get(URI uri);




-예제 및 출력결과-


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
package com.hs.chap19;
 
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
 
public class PathExample {
 
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("src/sec02/exam01_path/PathExample.java"); // get() 메소드를 사용해 파일을 가져온다.
        System.out.println("[파일명]" + path.getFileName()); // getFileName() 메소드를 사용해 파일의 이름을 가져와 출력한다.
        System.out.println("[부모 디렉터리명]: " + path.getParent().getFileName()); // getParent().getFileName()메소드를 사용해 부모
                                                                                // 디렉터리이름을 출력 한다.
        System.out.println("중첩 경로수 : " + path.getNameCount()); // 파일의 이름의 개수(파일의 이름의 개수는 경로의 수)를 출력한다.
 
        System.out.println();
        for (int i = 0; i < path.getNameCount(); i++) {
            System.out.println(path.getName(i)); // 파일의 경로를 하나씩 출력한다.
        }
 
        System.out.println();
        Iterator<Path> iterator = path.iterator(); // 반복자 메소드를 사용
        while (iterator.hasNext()) { // hasNext()메소드를 사용해 다음 디렉터리가 없을때 까지 출력
            Path temp = iterator.next();
            System.out.println(temp.getFileName()); //getFileName() 메소드를 사용해 얻은 디렉터리의 이름을 출력한다.
        }
    }
 
}
 
cs




- 파일 시스템 정보 (FileSystem) -


운영체제의 파일 시스템은 FileSystem 인터페이스를 통해서 접근할 수 있다.

FileSystem 구현 객체는 FileSystem의 정적 메소드인 getDefault( )로 얻을 수 있다.



  FileSystem fileSystem = FileSystems.getDefault( );

 


리턴 타입 

 매소드(매개 변수)

 설명

 Iterable<FileStore>

 getFileStores( )

 드라이버 정보를 가진 FileStore 객체들을 리턴

 Iterable<Path>

 getRootDirectories( )

 루트 디렉토리 정보를 가진 Path 객체들을 리턴

 String

 getSeparator( )

 디렉토리 구분자 리턴





-FileStore-


드라이버(컴퓨터 드라이버, ex- 그래픽드라이버 같은것) 를 표현한 객체.



리턴 타입

 메소드 (매개 변수)

 설명

 long

 getTotalSpace( )

 드라이버 전체 공간 크기 (바이트 단위) 리

 long

 getUnallocatedSpace( )

 할당되지 않은 공간 크기 (바이트 단위) 리턴

 long

 getUsableSpace( )

 사용 가능한 공간 크기,getUnallocatedSpace( ) 동일

 boolean

 isReadOnly( )

 읽기 전용 여부

 String

 name( )

 드라이버명 리턴

 String

 type( )

 파일 시스템 종류




-속성 이란?-


파일이나 디렉토리가 숨김인지, 디렉토리인지, 크기가 어떻게 되는지, 소유자가 누구인지에 대한 정보를 말한다.

다음은 java.nio.file.Files 클래스가 제공하는 정적 메소드들이다. (너무 많아서 전부다는 안쓰고 헷갈리는것들만 적음)



리턴 타입

 메소드(매개 변수)

설명 

Path

createDirectories( ) 

모든 부모 디렉토리 생성

Path 

createDirectory( ) 

경로의 마지막 디렉토리만 생성

FileStore 

getFileStore( ) 

파일이 위치한 FileStore(드라이브) 리턴

BufferedReader

newBufferedReader( ) 

텍스트 파일을 읽는 BufferedReader 리턴 

BufferedWriter 

newBufferedWriter( ) 

텍스트 파일에 쓰는 BufferedWriter 리턴 

boolean 

notExists( ) 

존재하지 않는지 여부 

String 

probeContentType( ) 

파일의 MIME 타입을 리턴 

byte[ ] 

readAllBytes( ) 

파일의 모든 바이트를 읽고 배열로 리턴 

List<String> 

readAllLines( )

텍스트 파일의 모든 라인을 읽고 리턴






-예제 및 출력결과-


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
package com.hs.chap19;
 
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
 
public class DirectoryExample {
 
    public static void main(String[] args) throws Exception {
        Path path1 = Paths.get("C:/Temp/dir/subdir"); // 디렉토리 주소를 얻는다.
        Path path2 = Paths.get("C:/Temp/file.txt"); // 파일 주소를 얻는다.
 
        if (Files.notExists(path1)) // notExists 메소드를 사용해 path1 경로에 디렉토리가 존재하지 않는지 확인
        {
            Files.createDirectories(path1); // 디렉토리의 경로가 존재하지 않으면 createDirectories() 메소드를 사용해 path1 경로에 디렉토리를 생성한다.
        }
 
        if (Files.notExists(path2)) // notExists 메소드를 사용해 path2 경로에 파일이 존재하지 않는지 확인
        {
            Files.createFile(path2); // 디렉토리의 경로가 존재하지 않으면 createDirectories() 메소드를 사용해 path2 경로에 파일을 생성한다.
        }
 
        Path path3 = Paths.get("C:/Temp"); // 폴더의 주소를 얻는다.
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path3); // newDirectoryStream() 메소들 사용해서 Temp
                                                                                    // 디렉토리 내용을 스트림으로 얻는다.
        for (Path path : directoryStream) // 얻는 스트림을 path에 하나씩 집어넣는다.
        {
            if (Files.isDirectory(path)) {
                System.out.println("[디렉토리]" + path.getFileName()); // 디렉토리의 이름을 순차적으로 출력한다.
            } else {
                System.out.println("[파일]" + path.getFileName() + "(크기: " + Files.size(path) + ")"); // 디렉토리의 이름을 출력한 후에
                                                                                                    // 파일의 크기를 출력한다.
            }
        }
    }
}
 
cs





- 와치 서비스(WatchService) -


자바7에서 처음 소개된 것으로 디렉토리 내부에서 파일 생성, 삭제, 수정 등의 내용 변화를 감시하는데 사용된다.

와치 서비스의 적용 예는 에디터에서 파일을 편집하고 있을 때, 에디터 바깥에서 파일 내용을 수정하게 되면 파일 내용이 변경됐으니

다시 불러올 것인지 묻는 대화상자를 띄우는 것이다.

WatchService를 생성하려면 FileSystem의 newWatchService( ) 메소드를 호출하면 된다.



  WatchService watchService = FileSystems.getDefault( ).newWatchService( );

 




 

  * 와치 서비스 사용법 *



 1. FileSystem의 newWatchService( ) 메소드를 호출해서 WatchService 생성.


 2. 감시가 필요한 디렉토리의 path객체에서 register( ) 메소드로 WatchService 를 등록.


 3. 어떤 변화(생성, 삭제, 수정)를 감시할 것인지를 Standard WatchService 상수로 저장.


 4. 디렉토리에 WatchService 를 등록한 순간부터 WatchEvent가 발생하고, WatchService는 해당 이벤트 정보를 가진 와치키(WatchKey)를

생성하여 큐에 저장


 5. 프로그램이 무한 루프를 돌면서 WatchService의 take() 메소드를 호출하여 WatchKey가 큐에 들어올 때까지 대기하고 있다가 WatchKey가

    큐에 들어오면 WatchKey를 얻는다.


 6. WatchKey를 얻고나서 pollEvents( ) 메소드를 호출해서 WatchEvent 리스트를 얻어낸다.


 7. 프로그램은 WatchEvent 리스트에서 WatchEvent를 하나씩 꺼내어 이벤트의 종류와 Path 객체를 얻어낸 다음 적절히 처리하면 된다.




- JNI(Java Native Interface) -


자바 코드에서 C함수를 호출할 수 있도록 해주는 API이다.




- 버퍼 -


Buffer 종류

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


Buffer 클래스 allocate( ), wrap( )  메소드 = 넌다이렉트 버퍼 생성


ByteBuffer의 allocateDirect( ) 메소드 = 다이렉트 버퍼 생성 


구분

넌다이렉트 버퍼

다이렉트 버퍼 

사용하는 메모리 공간 

JVM의 힙 메모리 

 운영체제의 메모리

버퍼 생성 시간

버퍼의 생성이 빠르다.

 버퍼 생성이 느리다.

버퍼의 크기

 작다.

 크다.(큰 데이터를 처리할 때 유리)

입출력 성능

 낮다.

 높다. (입출력이 빈번할 때 유리)



- allocate( ) 메소드 -


JVM 힙 메모리에 넌다이렉트 버퍼를 생성하는 메소드.

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


  ByteBuffer byteBuffer = ByteBuffer.allocate(100);


  CharBuffer charBuffer = CharBuffer.allocate(100);




- wrap( ) 메소드 -


이미 생성되어 있는 자바 배열을 래핑해서 Buffer 객체를 생성한다.

자바 배열은 JVM 힙 메모리에 생성되므로 wrap( )은 넌다이렉트 버퍼를 생성한다.

다음은 길이가 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);





- allocateDirect( ) 메소드 -


JVM 힙 메모리 바깥쪽, 즉 운영체제가 관리하는 메모리에 다이렉트 버퍼를 생성한다.

Buffer 클래스에는 없고, ByteBuffer에서만 제공된다.


다음은 100개의 바이트(byte) 를 저장하는 다이렉트 ByteBuffer과 50개의 문자(char)를 저장하는 다이렉트 CharBuffer, 25개의 정수(int)를 저장하는

다이렉트 IntBuffer를 생성하는 코드이다. char은 2바이트 크기를 가지고, int는 4바이트 크기를 가지기 때문에 초기 다이렉트 ByteBuffer 생성 크기에

따라 저장 용량이 결정된다.



 

  ByteBuffer byteBuffer = ByteBuffer.allocateDriect(100);    //100개의 byte값 저장 (char은 2바이트 크기, int는 4바이트 크기를 가짐)


  CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer( );   //50개의 char값 저장 (100개의 char값을 변환한 값)


  IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer( );         //25개의 int값 저장 (50개의 char값을 변환한 값)





-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.hs.chap19;
 
import java.nio.ByteBuffer;
 
public class BufferSizeExample {
    public static void main(String[] args) {
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(200 * 1024 * 1024); // allocateDirect(버퍼크기 설정)버퍼 크기 설정해서
                                                                                // 다이렉트 버퍼 생성
        System.out.println(directBuffer);
        System.out.println("다이렉트 버퍼가 생성되었습니다.");
 
        ByteBuffer nonDirectBuffer = ByteBuffer.allocate(200 * 1024 * 1024); // allocate(버퍼크기 설정)버퍼 크기 설정해서 넌다이렉트 버퍼 생성
        System.out.println(nonDirectBuffer);
        System.out.println("넌다이렉트 버퍼가 생성되었습니다.");
 
    }
}
 
 
cs




- byte 해석 순서(ByteOrder) -


데이터를 처리할 때 바이트 처리 순서는 운영체제마다 차이가 있다. 이러한 차이는 데이터를 다른 운영체제로 보내거나 받을 때

영향을 미치기 때문에 데이터를 다루는 버퍼도 이를 고려해야 한다.

앞쪽 바이트부터 먼저 처리하는 것을 Big endian 이라고 하고, 뒤쪽 바이트부터 먼저 처리하는 것을 Little endian이라고 한다.




- Buffer의 위치 속성 -

-속성 크기 관계-


  0 <= mark <= position <= limit <= capacity 



속성 

설명 

 position

현재 읽거나 쓰는 위치값이다. 인덱스 값이기 때문에 0부터 시작하며, limit 보다 큰 값을 가질 수 없다.

만약 position과 limit의 값이 같아진다면 더 이상 데이터를 쓰거나 읽을 수 없다는 뜻이 된다.

 limit

버퍼에서 읽거나 쓸 수 있는 위치의 한계를 나타낸다. 이 값은 capacity보다 작거나 같은 값을 가진다.

최초에 버퍼를 만들었을 때는 capacity와 같은 값을 가진다,

 capacity

버퍼의 최대 데이터 개수(메모리 크기)를 나타낸다. 인덱스 값이 아니라 수량임을 주의하자.

 mark

reset( ) 메소드를 실행했을 때에 돌아오는 위치를 지정하는 인덱스로서 mark( ) 메소드로 지정할 수 있다.

주의할 점은 반드시 position 이하의 값으로 지정해주어야 한다.

position이나 limit의 값이 mark 값보다 작은 경우, mark는 자동 제거된다.

mark가 없는 상태에서 reset( ) 메소드를 호출하면 InvalidMarkException이 발생한다.


 

 

  flip( ) 메소드 : 현재 position의 위치를 limit로 설정하고, position을 0번 인덱스로 설정한다. 


  mark( ) 메소드 : 현재 position의 위치를 기억시켜 놓는다.


  reset( ) 메소드 : position을 mark가 있는 인덱스로 이동시킨다. (mark가 없는 상태에서 호출하면 예외발생-기억한 위치가 없기 때문)


  rewind( ) 메소드 : limit는 변하지 않지만 position은 0번 인덱스로 다시 설정된다.


  clear( ) 메소드 : Buffer의 세가지 속성이 초기화 된다. (limit는 capacity (버퍼의 최대 메모리값)으로, position은 0으로 설정되고, mark는 없어짐)

                       하지만 데이터는 삭제되지 않는다.


  compact( ) 메소드 : 현재 position에서 limit 사이의 데이터가 0번 인덱스로 복사 되고 현재 position은 복사된 데이터 다음 위치로 이동한다.

                            (예를들어 flip() 메소드 호출 후 세 개의 데이터를 읽고 다음과 같이 position이 3번 인덱스 위치에 있을 때 compact( )가

                             호출되면 3번 인덱스와 4번 인덱스의 데이터는 0번 인덱스와 1번 인덱스로 복사되고 position 2번 인덱스로 이동한다.)

                             주의할 점은 0번과 1번 인덱스를 제외한 나머지 인덱스의 데이터는 삭제되지 않고 남아있다.

                             compact( ) 메소드를 호출하는 이유는 읽지 않은 데이터 뒤에 새로운 데이터를 저장하기 위해서이다.                             



 

 

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

 

 

  put( ) 메소드 : 버퍼에 데이터를 저장하는 메소드

 

  get( ) 메소드 : 버퍼에 있는 데이터를 읽는 메소드

 

 

이 두가지 메소드는 상대적과 절대적으로 구분된다.

 

버퍼 내의 현재 위치 속성인 position에서 데이터를 읽고, 저장할 경우는 상대적이고, position과 상관없이 주어진 인덱스에서

데이터를 읽고, 저장할 경우는 절대적이다.

 

상대적 get( )과 put( ) 메소드를 호출하면 position의 값은 증가하지만, 절대적 get( )와 put( ) 메소드를 호출하면

position의 값은 증가되지 않는다.

 

만약 position 값이 limit 값까지 증가했는데도 상대적 get( )을 사용하면 BufferUnderflowException 예외가 발생하고,

put( ) 메소드를 사용하면 BufferOverflowException 예외가 발생한다.

 

상대적 메소드와 절대적 메소드를 쉽게 구분하는 방법은 index 매개 변수가 없으면 상대적이고, index 매개 변수가 있으면 절대적이다.

 

 

 

- 버퍼 예외의 종류 -

 

주로 버퍼가 다 찼을 때 데이터를 저장하려는 경우와 버퍼에서 더 이상 읽어올 데이터가 없을 때 데이터를 읽으려는 경우에 예외가 발생

 

 예외

 설명

 BufferOverflowException

 position이 limit에 도달했을 때 put( )을 호출하면 발생

 BufferUnderflowException

 position이 limit에 도달했을 때 get( )을 호출하면 발생

 InvalidMarkException

 mark가 없는 상태에서 reset( ) 메소드를 호출하면 발생

 ReadOnlyBufferException

읽기 전용 버퍼에서 put( ) 또는 compact( ) 메소드를 호출하면 발생

 

 

-예제 및 출력 결과-

 

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
package com.hs.chap19;
 
import java.nio.ByteBuffer;
 
public class BufferExample {
    public static void main(String[] args) {
        System.out.println("[7바이트 크기로 버퍼 생성]");
        ByteBuffer buffer = ByteBuffer.allocateDirect(7); // allocateDirect()메소드로 7바이트 크기로 다이렉트 버퍼 생성
        printState(buffer); // 버퍼에 인덱스를 매개값으로 받는 메소드 선언
 
        buffer.put((byte10); // put()메소드를 사용해 버퍼에 10바이트를 저장, 0번 인덱스에 값 저장
        buffer.put((byte11); // put()메소드를 사용해 버퍼에 11바이트를 저장, 1번 인덱스에 값 저장
        System.out.println("[2바이트 저장후]");
        printState(buffer); // 인덱스 매개변수가 있으므로 상대적 저장임
 
        buffer.put((byte12); // put() 메소드를 사용해 버퍼에 12바이트를 저장, 2번 인덱스에 값 저장
        buffer.put((byte13); // put() 메소드를 사용해 버퍼에 13바이트를 저장, 3번 인덱스에 값 저장
        buffer.put((byte14); // put() 메소드를 사용해 버퍼에 14바이트를 저장, 4번 인덱스에 값 저장
        System.out.println("[3바이트 저장후]");
        printState(buffer); // 인덱스 매개변수가 있으므로 상대적 저장
 
        buffer.flip(); // flip()메소드를 사용해 현재 position의 위치를 limit로 설정하고, position을 0번 인덱스로 설정
        System.out.println("[flip() 실행후]");
        printState(buffer); // 출력값 position : 0, limit: 5 으로 예상됨
 
        buffer.get(new byte[3]); // get() 메소드를 사용해 3바이트를 읽음
        System.out.println("[3바이트를 읽은 후]");
        printState(buffer); // 상대적 읽기
 
        buffer.mark(); // 현재 position위치를 저장해놓음 (현재 3번 인덱스)
        System.out.println("--------[현재 위치를 마크 해놓음]---------");
 
        buffer.get(new byte[2]); // get()메소드를 사용해 2바이트를 읽음 (현재위치 5번 인덱스)
        System.out.println("[2바이트 읽은후]");
        printState(buffer);
 
        buffer.reset(); // reset() 메소드를 사용해 mark위치로 position을 옮김 (현재 3번 인덱스)
        System.out.println("-----[position을 마크 위치로 옮김]");
        printState(buffer);
 
        buffer.rewind(); // rewind()메소드를 사용하여 position을 0번으로 이동 (현재 0번 인덱스)
        System.out.println("[rewind() 실행후]");
        printState(buffer);
 
        buffer.clear(); // clear() 메소드를 사용해 위치 값을 초기화 (position,limit,capacity 즉 처음 버퍼 만들었을 때로 돌아감)
        System.out.println("[clear() 실행후]");
        printState(buffer);
    }
 
    private static void printState(ByteBuffer buffer) { // 버퍼의 인덱스를 매개값으로 받아 position, limit, capacity를 출력하는 메소드
        System.out.println("position: " + buffer.position() + ", ");
        System.out.println("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity() + ", ");
    }
}
 
cs

 

 

 

 

- Buffer 변환 -

 

채널이 데이터를 읽고 쓰는 버퍼는 모두 ByteBuffer이다.

그렇기 때문에 채널을 통해 읽은 데이터를 복원하려면 ByteBuffer를 문자열 또는 다른 타입 버퍼( CharBuffer, IntBuffer, LongBuffer 등 )

반대로 문자열 또는 다른 타입의 버퍼의 내용을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환해야 한다.

 

 

  ByteBuffer <-- String 변환

 

  채널을 통해 문자열을 파일이나 네트워크로 전송하려면 특정문자셋(UTF-8, EUC-KR)으로 인코딩해서 ByteBuffer로 변환해야 한다.

  문자셋을 표현하는 java.nio.charset.Charset 객체는 두 가지 방법으로 얻을 수 있다.

 

 

  Charset charset = Charset.forName("UTF-8");   //매개값으로 주어진 문자셋

 

  Charset charset = Charset.defaultCharset( );    //운영체제가 사용하는 디폴트 문자셋

 

 

  Charset를 이용해서 문자열을 ByteBuffer로 변환하려면 다음과 같이 encode( ) 메소드를 호출 하면 된다.

 

  String data = ...;

 

  ByteBuffer byteBuffer = charset.encode(data);

 

 

 

 

  ByteBuffer --> String 변환

 

  반대로 파일이나 네트워크로부터 읽은 ByteBuffer이 특정 문자셋으로 인코딩되어 있을 경우, 해당 문자셋으로 디코딩해야만

  문자열로 복원할 수 있다.

  Charset는 ByteBuffer를 디코딩해서 CharBuffer로 변환시키는 decode( ) 메소드를 제공하고 있기 때문에 쉽게 문자열로 복원할 수 있다.

 

 

  ByteBuffer byteBuffer = ...;

 

  String data = charset.decode(byteBuffer).toString( );

 

 

 

-예제 및 출력결과-

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.hs.chap19;
 
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
 
public class ByteBufferToStringExample {
    public static void main(String[] args) {
        Charset charset = Charset.forName("UTF-8"); // forName() 메소드의 매개값인 문자셋으로 Charset 객체를 얻는다.
 
        // 문자열 -> 인코딩 -> ByteBuffer
        String data = "안녕하세요";
        ByteBuffer byteBuffer = charset.encode(data); // charset의 encode메소드로 data에 있는 문자열을 변환한다.
 
        // ByteBuffer -> 디코딩 -> CharBuffer -> 문자열
        data = charset.decode(byteBuffer).toString();// decode 메소드 사용 byteBuffer을 String문자열로 변환한다.
        System.out.println("문자열 복원: " + data);
 
    }
 
}
 
 
cs

 

 

 

 

 

 

  ByteBuffer <-- IntBuffer 변환

 

  int[ ] 배열을 생성하고 이것을 파일이나 네트워크로 출력하기 위해서는 int[ ] 배열 또는 IntBuffer로 부터

  ByteBuffer을 생성해야 한다.

  int 타입은 4byte 크기를 가지므로 int[] 배열 크기 또는 IntBuffer의 capacity보다 4배큰 capacity를 가진 ByteBuffer을 생성하고, 

  ByteBuffer의 putInt() 메소드로 정수값을 하나씩 저장하면 된다.

  다음은 int[] 배열을 IntBuffer로 래핑하고, 4배 큰 ByteBuffer를 생성한 후 정수값을 저장한다.

  주의할 점은 putInt() 메소드는 position을 이동시키기 때문에 모두 저장한 후에는 position을 0으로 되돌려 놓는 flip() 메소드를 호출해야 한다.

 

  int[ ] data = new int[ ] {10,20};

  IntBuffer intBuffer = IntBuffer.wrap(data);

  ByteBuffer byteBuffer = ByteBuffer.allocate(intBuffer.capacity()*4);

  for(int i = 0; i < intBuffer.capacity( ); i++) {

  byteBuffer.putInt(intBuffer.get(i);

  }

  byteBuffer.flip( );

 

 

 

 

 

  ByteBuffer --> IntBuffer 변환

 

  반대로 파일이나 네트워크로부터 입력된 ByteBuffer에 4바이트씩 연속된 int 데이터가 저장되어 있을 경우,

  int[ ] 배열로 복원이 가능하다.

  ByteBuffer의 asIntBuffer( ) 메소드로 IntBuffer를 얻고, IntBuffer의 capacity와 동일한 크기의 int[ ] 배열을 생성한다.

  그리고 IntBuffer의 get( ) 메소드로 int값들을 배열에 저장하면 된다.

 

  ByteBuffer byteBuffer = ...;

  IntBuffer intBuffer = byteBuffer.asIntBuffer( );

  int[ ] data = new int [intBuffer.capacity( )];

  intBuffer.get(data);

 

 

728x90
반응형
: