java week 13


목차



스트림 (Stream) / 버퍼 (Buffer) / 채널 (채널) 기반의 I / O


I/O


자바 프로그램에서의 입력 작업(Input)과 출력 작업(Output)을 의미한다.

입출력의 간단한 예로 키보드로 텍스트를 입력하고, 모니터로 입력한 텍스트를 출력하는 것 이다.


스트림


스트림은 단일 방향으로 연속적으로 흘러가는 것을 말하는데 물이 높은 곳에서 낮은곳으로 흐르듯이 데이터는 출발지에서 나와 도착지로 흘러간다는 개념입니다.

물이 한 방향으로 흐르듯이 데이터 또한 단방향으로만 통신이 가능하며, 만약 입력과 출력을 동시에 처리학 싶다면 입력 스트림, 출력 스트림 각각 만들어서 처리해야한다.

그림1


버퍼


데이터를 전송하는 상호간의 장치에서 고속의 장치와 저속의 장치 간의 속도 차이로 인해 저속의 장치가 작업을 추리하는 동안 고속의 장치가 기다려야 하는 현상을 줄여주는 기술이다.

1바이트씩 읽는 것이 아니라 덩어리로 모아서 전단한다.

즉 버퍼는 각 장치나 프로세스가 상대방에 의해 정체되지 않고 잘 동작 할 수 있도록 해주는 기술이다.

컴퓨터에서 전송한 데이터가 버퍼에 저장되어 프린터는 컴퓨터로부터 직접 데이터를 받는 것이 아니라,

버퍼에서 데이터를 조금씩 읽어서 출력 하므로 컴퓨터는 버퍼로 전송작업을 마치고 프린터가 출력을 하는 동안 다른 작업을 할 수 있게 되는 것 이다.

캐시가 어떤 작업의 속도를 증진시키기 위해 존재하는데 비해, 버퍼는 개별 작업들 간의 협동을 지원 하기 위해 존재한다는 차이가 있다.


NIO(new IO)


네트워크 프로그램의 속도는 우리 컴퓨터의 CPU나 메모리에서 주고받는 속도에 비해 현저히 느리다, 속도는 컴퓨터 환경에서 느린데 오고가는 데이터의 양은 더 많은 양이 오고간다. CPU와 메모리 디스크야 하드웨어의 발전에 따라 속도가 점점 빨라진다. 듀얼코어, 쿼드코어가 생기고, SSD가 탄생하면서 더욱 빨라진다. 하지만 유무선 네트워크 환경 역시 발전하고 있지만 분명 한계는 존재한다. 그렇기에 기존의 IO 방식과는 다른 개념이 필요하다.

Stream 방식의 입출력은 병목현상에 매우 취약하다. 보내는 데이터가 Queue에 계속 쌓이다보면 앞의 데이터 처리가 되어야 뒤의 데이터가 처리 되는데, 처리 속도보다 데이터의 진입 속도가 더 빠르다면 병목현상이 지속되게 된다.

이러한 네트워크 환경에서의 문제점을 해결하기위해서 NIO 방식이 생겨났다.


NIO의 키워드 3가지


  1. 채널

    서버와 클라이언트간의 통신수단을 나타낸다. 혹은 프로그램 컴포넌트와 같이 읽기나 쓰기등 한 개 이상의 뚜렷한 입출력 작업을 수행할 수 있는 개방된 연결을 나타낸다.

    • 읽기 쓰기를 동시에 할 수 있다.
    • Buffer 클래스를 사용하므로 데이터형에 맞는, 전용 메모리 공간을 가지고 있다. 즉 채널은 버퍼에 저장된 데이터를 출력하고, 입력된 데이터를 버퍼에 저장한다.
    • 블로킹된 스레드를 깨우거나 중단시킬수 있다.
  2. 버퍼
    • IO 에서는 출력 스트림이 1바이트를 쓰면 입력 스트림이 1바이트를 읽는다.
    • NIO 에서는 버퍼를 사용해서 복수 개의 바이트를 한꺼번에 입력받고 출력하는 것이 빠른 성능을 낸다.
  3. Selector

    IO는 블로킹(blocking)이 된다. 입력 스트림의 read() 메소드를 호출하면 데이터가 입력되기 전까지 스레드는 블로킹(대기 상태)됩니다.

    스레드가 블로킹되면 다른 일을 할 수 없고 블로킹을 빠져나오기 위해 인터럽트도 할 수 없다.

    반대로 논블록킹은 입출력 작업 시 스레드가 블로킹되지 않는 것을 말한다.

    NIO는 블록킹의 특성과 넌블록킹의 성질 두가지 모두 가지고 있다.

    NIO의 넌블로킹은 입출력 작업 준비가 완료된 채널만 선택해서 작업 스레드가 처리하기 때문에 작업 스레드가 블로킹되지 않는다.

    작업 준비가 완료되었다는 뜻은 지금 바로 읽고 쓸 수 있는 상태를 말한다.

    셀렉터는 복수 개의 채널 중에서 준비 완료된 채널을 선택하는 방법을 제공해준다.

    IO와 NIO


    |구분|IO|NIO| |—|—|—| |입출력 방식|스트림 방식|채널 방식| 버퍼 방식|넌버퍼|버퍼| 비동기 방식|지원 안함|지원| 블로킹 방식|블로킹 방식만 지원|블로킹 / 넌블로킹 모두 지원|


InputStream과 OutputStream


InputStream


바이트 기반 입력 스트림의 최상위 클래스로 추상클래스 이다.

모든 바이트 기반 입력 스트림은 이 클래스를 상속받아서 만들어진다.

InputStream 클래스에는 바이트 기반 입력 스트림이 기본적으로 가져야 할 메소드들이 정의 되어 있다.

메소드내용
read()입력 스트림으로부터 1바이트를 읽고 읽은 바이트를 리턴한다.
read(byte[] b)입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열b에 저장하고 실제로 읽은 바이트 수를 리턴한다.
read(byte[] b, int off, int len)입력 스트림으로부터 len개의 바이트만큼 읽고 매개값으로 주어진 바이트 배열 b[off]부터 len개까지 저장한다. 그리고 실제로 읽은 바이트 수인 len개를 리턴한다. 만약 len개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴한다.
close()사용한 시스템 자원을 반납하고 입력스트림을 닫는다.

OutputStream


바이트 기반 출력 스트림의 최상위 클래스로 추상클래스이다.

모든 바이트 기반 출력 스트림 클래스는 이 클래스를 상속받아서 만들어진다.

OutputStream 클래스에는 모든 바이트 기반 출력 스트림이 기본적으로 가져야할 메소드가 정의되어 있다. |메소드|설명| |—|—| void close()|OutputStream을 닫는다| void flush()|버퍼에 남아있는 출력 스트림을 출력한다| void write(byte[] b)|버퍼의 내용을 출력한다| void write(byte[] b, int off, int len)|b배열 안에 있는 시작 off부터 len만큼 출력한다| abstract void write(int b)|정수 b의 하위 1바이트를 출력한다|


바이트와 ​​문자 스트림


스트림은 바이트 단위로 핸들하는 것을 기본으로 한다.

문자 스트림이든 바이트 스트림이든, 둘 다 모두 처음엔 바이트로 받아들인다.

해당 스트림이 알아서 처리를 해준다.

각각의 스트림의 역할은 가공하는 방법과 장치가 다를 뿐 자료의 입출력을 도아주는 중간 매개체로서의 역할은 동일하다.

각 장치에 맞는 스트림을 이용하는 것이 프로그래머가 하는 일.


바이트 스크림


데이터를 바이트 단위로 주고받는다. 이미지, 동영상 등을 송수신할 때 주로 사용한다.

그림과 동영상도 바이트들로 이루어져 있고, 텍스트도 바이트로 이루어져 있다. 그리고 zip이나 jar같은 압축 파일도 일단은 바이트로 되어있다.

대표적인 바이트 스트림은 InputStream과 OutputStream

Input, OutputSteam의 하위 클래스들도 전부 바이트 단위로 주고받으며,원시 바이트를 그대로 주고 받겠다는 의미를 담고 있다.

AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream, BufferedInputStream, DataInputStream 등등

전부 끝에 InputStream과 OutputStream이라는 단어를 달고 있다.


문자 스트림


text 데이터를 입출력하는데 사용하는 스트림이다. HTML 문서, 텍스트 파일을 송수신할 때 주로 사용한다.

바이트를 2바이트 또는 1바이트로 묶어서 사용한다. 2바이트와 1바이트의 차이는 문자 인코딩에 따라서 다르게 묶여진다. (재해석후 유니코드로 바뀌는데 이 차이)

Reader / Writer : 문자 기반 스트림의 최고 조상

Writer, BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter, FileWriter, PipedWriter, PrintWriter, StringWriter, BufferedReader, LineNumberReader, CharArrayReader, InputStreamReader, FileReader, FilterReader, PushbackReader, PipedReader, StringReader … 전부 끝에 Reader와 Writer가 붙어있다.


표준 스트림 (System.in, System.out, System.err)


System class

자바에서 미리 정의해둔 표준 입출력 클래스 표준 입출력은 콘솔 화면에 입출력된다고 해서 콘솔 입출력이라고도 함

System.in: 표준 입력용 스트림 System.out: 표준 출력용 스트림 System.err: 표준 오류 출력 스트림

in, out, err 는 모두 정적 변수이기 때문에 System 클래스를 생성하지 않고도 사용 가능


표준 입출력 Method


|메소드 |설명| |—|—| |System.in.read()|키보드로 입력된 값을 읽어들임, 더 이상 읽어들일 수 없으면 -1 리턴| |System.out.write()|( )안에 입력된 값을 화면(콘솔)에 출력 컴퓨터가 숫자로 저장하고 있는 것을 사람이 읽을 수 있는 문자로 디코딩해서 출력| |System.out.flush()|출력은 버퍼에 일정 용량 이상이 쌓여야 가능한데, 버퍼를 비워서 바로 출력하도록 하는 메소드 데이터를 일정 용량 쌓아두었다가 출력하는 이유는 입출력 성능 향상을 위함|


파일 읽기 쓰기


  1. 파일 객체를 만든다.(File, FileReader/FileWriter, BufferReader/BufferWriter)

  2. 해당 파일을 열어 읽는다. (+읽은 내용 알아서 처리, 어디다 쓰든지 문자열을 바꾸던지 뭐 맘대로 하면 된다.)

  3. 파일을 닫는다.


파일 읽기


    //파일 객체 생성
    File file = new File("./Sample.txt");
    
    //입력 스트림 생성
    FileReader filereader = new FileReader(file);
    
    //입력 버퍼 생성
    BufferedReader bufReader = new BufferedReader(filereader);
    
    String line = "";
    while((line = bufReader.readLine()) != null){
        System.out.println(line);
    }
    
    //.readLine()은 끝에 개행문자를 읽지 않는다.            
    bufReader.close();


파일 쓰기


    //파일 객체 생성
    File file = new File("./Writer.txt");
    
    //쓰기 스트림 생성
    FileWriter fileWriter = new FileWriter(file);
    
    //쓰기 버퍼 생성
    BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

    if(file.isFile() && file.canWrite()){
        //쓰기
        bufferedWriter.write("문자열 추가1");
        //개행문자쓰기
        bufferedWriter.newLine();
        bufferedWriter.write("문자열 추가2");
        
        bufferedWriter.close();
    }

출처







© 2019.04. by salmon2

Powered by theorydb