1. 통신의 기본
컴퓨터는 크게 3가지 층으로 나뉜다. 1. Application (ex.프로그램 : 애가 데이터를 받으면 input, 주면 output) 2. OS 3. HW (ex.모니터)
* 어플리케이션에서 전송되는 데이터는 OS를 뚫고 모니터에 출력하게 된다. 어플리케이션은 파일 (추후 소켓이라고도 부름)을 통해 '바이트 스트림' 물길을 만들어서 OS로 데이터를 전송하게 된다. 스트림 : 끝이 없는 바이트의 흐름 / 저장공간 바이트 스트림 : 한바이트씩 저장되는 물길. 끝없이 늘어나기 때문에 백개, 천개, 오만개 보내도 됨.
스트림의 크기는 보내는 사람이 정의한다. (끝은 없다)
통신- 남의 컴퓨터의 하드에 들어가는 것 (자료 찾을라고)
이런 통신 코드에는 다 스레드가 있다는 걸 기억하자
2. 보조스트림 X, Buffer X (단순 바이트 스트림)
보조스트림 없이 파일에 바로 꽂는거 - 하나씩만 보내지겠지
코드
package ex15; import java.io.IOException; import java.io.InputStream; public class StreamEx01 { public static void main(String[] args) { InputStream input = System.in; try { int value = input.read(); System.out.println("받은 값 : " + value); } catch (IOException e) { throw new RuntimeException(e); } } }
이 코드에서는 InputStream을 사용하여 데이터를 읽고 있다. InputStream은 내부적으로 버퍼를 사용하지 않고 데이터를 한 바이트씩 읽는 스트림. 때문에 데이터를 버퍼에 저장하지 않고, 읽은 데이터는 즉시 처리(소멸)한다. 따라서 해당 코드는 버퍼가 사용되지 않은 것. 버퍼를 사용하려면 BufferedInputStream 등의 버퍼를 제공하는 클래스를 이용해야 한다. 이러한 클래스를 사용하면 한 번에 여러 바이트를 읽어들여 버퍼에 저장하고, 필요에 따라 버퍼에서 데이터를 읽어올 수 있다.
위의 코드처럼 키보드로 데이터를 input 받는다고 가정해보자. 콘솔 창에 아스키 코드 'A', 'A', 'B'를 입력하면 65가 흘러가서 바이트 스트림을 통해 HW에 출력 > 전송이 다 된 데이터 지워짐(팝처럼?)> 다시 65가 흘러가서 출력 > 지워짐을 반복 즉, '파일'이 데이터를 읽어서 'OS에서' 들고가는 것
//결과가 A값만 나왔다.
해당 코드는 버퍼가 없고, InputStream을 사용해서 1Byte씩 읽기 때문에, 뒤에 오는 B값은 Byte가 읽지 못한다. AB를 입력해도 A값만 출력하고, B는 바로 소멸하게 되는 것. 이런 현상을 해결하기 위해서 ‘보조스트림’이 필요하다.
2-2. 파일-소켓 이란?
바이트 데이터를 파일로 쓰면 (=컴퓨터에서 생성된 바이트 데이터를 하드 디스크나 다른 저장 매체에 저장하는 것) 그림에 있는 저런 파일이 만들어지는데, 이 파일은 데이터를 저장하는 매체로서 기능하고, 소켓이라는 매개체로 사용될 수도 있다. (파일 = 소켓 : 내가 만드는거 x)
* 단순히 데이터 저장만 하는 기능을 하면 : 파일
* 어플리케이션과 OS간의 데이터 교환을 위한 매개체로서 기능하면 : 소켓
[ 소켓 ]
응용 프로그램(APP)과 운영 체제(OS) 간의 데이터 교환을 위한 매개체로 사용된다. 응용 프로그램은 소켓을 통해 데이터를 보내고 받을 수 있으며, 운영 체제는 소켓을 통해 데이터를 전달하고 수신한다. 즉, 컴퓨터와 컴퓨터 사이에 데이터를 전달하는 역할 = 컴퓨터와 컴퓨터 사이에서 실시간으로 데이터를 교환하는 용도 = 어플리케이션과 OS를 이어주는 매개체
3. 보조스트림 O, Buffer X
보조스트림 (아직 버퍼x) 배열로 고정 크기를 만들고 나서 애를 그냥 쓰기
보조스트림을 사용하면 이제는 1byte씩 받는게 아니라 내가 크기를 지정해서 데이터를 받을 수 있게 된다. 보통 배열을 사용해서 보조스트림의 크기를 지정한다. > 배열로 지정했기에 크기가 고정된다.
데이터를 들고 옴 > 소비 > 처리 과정을 거치면 두번째 그림처럼 보조스트림 칸에서 (작업이 완료됐으니) 지워진다. 아까보단 더 많은 데이터를 처리할 수 있게 됐다! 그러나.....
채팅같은 경우는 보조스트림의 끝을 정할 수가 없다. (얼마나 칠지 모르기 때문) 데이터를 받을때 보조스트림의 크기를 50Byte로 제한해버리면 50Byte (칸 50개) 이상은 못받음 또한 보조스트림은 50칸이 있을때 3칸만 써도, 남은 칸을 돌려주지 않는다. 50칸을 계속 고정적으로 확보 해놓음. 즉, 적을 때마다 메모리를 낭비하게 된다. 통신이 일어날 때마다 낭비를 한다고!! 다 돈이다! > 못쓴다!!!
보조스트림의 크기를 4바이트로 할당하고 "hello"라는 문자열을 입력했을 경우
"hell"까지는 정상적으로 보조스트림으로 전달되지만,
나머지 "o"는 보조스트림의 크기를 초과하므로 데이터가 유실될 수 있다.
4. 보조스트림 O, Buffer O ✓
보조스트림 (버퍼로 만들기) 크기는 3바이트, 4바이트 상관x <애를 쓰게 될 것
만약, 보조스트림을 Buffer로 만들면 특이한 방식으로 작동함 내가 만약 hello를 적으면 hello가 칸에 들어가긴 하는데... 할당 된 보조스트림 칸이 4Byte이기 때문에 o는 칸에 들어오지 못한다. 이 o는 안버리고 애플리케이션이 가지고 있음. (물길이 차있어서 못 넘겨보내는 것) > 데이터를 바로 소멸시키지 않고 보관해준다는 말이다.
버퍼는 h를 소비하자 마자 이렇게, O를 바로 채워넣는다! 데이터를 계속 주면 무한대로 계속 들어오고, 처리하는 걸 반복하는 것. ex) 물길에 물고기 수천마리를 가둬놓고, 창고에 물고기를 4마리만 넣어둠. 내가 한마리를 먹음 3칸이잖아? 자리가 남잖아? 그럼 물길에서 물고기 하나 뽑아와서 다시 넣음. 이걸 반복 만약, 버퍼가 아니라 바이트 스트림이 파일에 직접적으로 꽂혀있다면 (= 버퍼가 없는 경우라면) 물고기 수천마리를 버리겠지..... (유실데이터)
애플리케이션에서 보조스트림(버퍼)을 만들어서 보조스트림에 데이터를 던지면
개발자의 일은 끝!
왜냐? 데이터를 던지면 os에서 다 구현이 되어있기 때문에 알아서 해줄 것
4-1. 버퍼와 버퍼링 이란?
HW에 감당하지 못할 데이터가 몰리게 되면 모니터에 출력되지 않는다. 이런 현상이 바로 버퍼링! (소비가 공급보다 빠를 때 생성! 소비가 공급보다 느릴 때 생성! (딱 안 맞을때 나옴)) 그런 데이터가 계속 쌓이고, 쌓이게 된다면 결국 데이터 소멸(유실)에 이른다. 이러한 상황을 방지하기 위해서 당장 처리하지 않는 데이터를 Buffer에 저장해야 한다. ex) 나는 새우깡을 3초에 1개씩 소비 할 수 있는데, 친구가 새우깡을 1초에 1개씩 줌. 난 3초에 1개씩만 새우깡을 소비하게 되니까 결국 2개를 (먹지 못하고) 소멸.
버퍼는 데이터를 일시적으로 저장하는 메모리 영역으로,
데이터를 처리하는 속도와 데이터가 도착하는 속도 간의 차이를 조절하기 위해 사용
4-2. 멀티스레드인 경우
보통 카톡 키고, 인터넷창도 키고, 게임도 하면서... 여러개를 띄워서 쓰잖아? (프로세스) 그럼 os는 어플리케이션1과 어플리케이션2를 둘 다 동시에 소비해야 하므로 무조건 멀티스레드로 처리하게 된다. (동시에 처리하기 위해선, 프로세스가 5개면 스레드 5개, 10개면 스레드 10개가 필요) 근데 멀티스레드에서 나오는 데이터들을 하나의 보조스트림에 보내버리면 데이터가 섞여버림. os가 그걸 막기 위해 각 프로세스마다 보조스트림을 다 들고 있음. (상단 이미지 참조)
4-3. 플러시(Flush)
버퍼가 완전히 차지 않으면 파일이 소비하지 않음 = 즉, os가 데이터를 가져가지 않음 (ex. 꽉 차지 않으면 수도꼭지를 틀지 않겠다) 난 다 안차도 바이트 스트림을 강제로 흘려보내고 싶은데 ㅠㅠ > 플러시 사용!! ★
[ 플러시 ]
플러시를 사용하면 소비하지 않겠다 이런거 없이 물길이 HW까지 다 열려서 바이트 스트림이 와라락 흐름
어플리케이션 ~ 파일까지 흐르는 스트림은 바이트 스트림
(이 보조스트림의 성격 - read, write 2개)
OS에서 흐르는 스트림은 패킷 스트림이라고 한다. (바이트 스트림이 절대 아니다!)
보라색 버퍼는 쓰기 버퍼(OutPutBuffer) 애플리케이션 입장에선 이 버퍼를 '쓰니까' (ex. System.out.print) 파일이나 소켓(과 같은 출력 대상)에 데이터를 쓰는 역할 주황색 버퍼는 읽기 버퍼(InPutBuffer) os입장에선 이 버퍼를 '읽으니까' (ex. System.in) 파일이나 소켓(과 같은 입력 대상)에서 데이터를 읽는 역할
out은 write랑 놀고, in은 read랑 놀고?
Share article