통신에 필요한 개념정리(Socket, Buffer, Stream, Pipe)

프로필

2016. 10. 19. 11:05

이웃추가

간단한 클라이언트와  HTTP 서버를 만들어 보기 전에, 필요한 몇 가지 개념들을 정리하겠다.

※ Socket

우선 소켓은 여러 개념에 적용이 되며,
여기서 설명하려는 소켓은 네트워크 통신에 사용되는 소켓임을 밝힌다.
소켓을 먼저 설명하기 전에 IPC에 대해 간단히 알고가자.

IPC(Inter Process Communication) 
말 그대로 프로세스간의 통신을 의미한다.
IPC에는 internal이냐 external이냐라고 나뉠 수 있다.

internal IPC에 사용되는 방법에는
Shared memory
Semaphore
Mutex
세가지 방법이 존재한다.
이러한 방법론들은 프로세스간의 통신 뿐만 아니란 쓰레드의 통신에서도 활용된다.
이 부분은 OS에서 차근차근히 공부하기로 하고,

External IPC
에서 필요한 개념이 바로 Socket이다.

Socket이란 
위키피디아의 정의는 이렇다.

네트워크 소켓(network socket)은 컴퓨터 네트워크를 경유하는 프로세스 간 통신의 종착점이다. 오늘날 컴퓨터 간 통신의 대부분은 인터넷 프로토콜을 기반으로 하고 있으므로, 대부분의 네트워크 소켓은 인터넷 소켓이다. 네트워크 통신을 위한 프로그램들은 소켓을 생성하고, 이 소켓을 통해서 서로 데이터를 교환한다. 

즉 물리적으로 구성되어 있는 네트워크 위에서
서로 다른 컴퓨터의 프로세스간의 통신을 위한 종점이라고 생각하면 된다.
소켓은 OS가 제공 및 관리하며,
우리가 흔히 사용하는 이더넷으로 구축된 네트워크가 물리적으로 구축된 연결이라면
소켓은 소프트웨어적으로 구축된 통신을 위한 입구/출구 정도로 생각하면 된다.

일상생활에 비유하면,
서로 다른 지역에 있는 에서
그곳에 거주하는 사람
대화를 하기 위해선
물리적으로 구성되어 있는 전화선 위에서
전화기라는 소켓이 필요한 것이다.
그리고 이렇게 만들어진 전화기는 전화번호와 통화하고자 하는 람의 이름을 통해서
내가 원하는 상대와 통화를 할 수 있다.

이걸 소켓과 비교하자면

집 = 컴퓨터
집에 거주하는 사람 = 프로세스
대화 = 음성데이터를 주고 받는 행위
전화선 = 구축되어 있는 네트워크
전화기 = 소켓
전화번호 + 사람이름 = IP주소 + Port번호

라고 비교할 수 있을 것 같다.

그러나 우리가 만드려는 HTTP 통신서버에서는 Socket을 다룰 필요가 없다.
라이브러리에 socket과 관련된 제어를 알아서 해주기 때문이다.






※ Bytestream

 Stream의 사전적 의미는 '개울, 시내, 줄기, 늘어선 줄' 이며, 한 마디로 정리하면 Byte의 흐름이다. 
입력과 출력에서 이동하는 byte의 흐름들을 통틀어서 일컫는 말이라고 생각하면 쉬울 것 같다.

스트림의 구분
- 콘솔의 입력/출력
- 파일의 읽기/쓰기
- 서버 클라이언트간의 요청과 응답
  (TCP/IP에서의 소켓을 이용한 데이터 통신)

스트림의 종류
- 읽기 스트림
- 쓰기 스트림

node.js에선 
파일 시스템과 관련된 fs모듈을 이용해서
읽기를 담당하는 readable stream과
쓰기를 담당하는 writable stream을 클래스를 생성하고
이 객체들을 활용하여 Stream을 제어 및 관리할 수 있다. (읽거나 혹은 쓰거나)







※ Buffer

 위키피디아의 정의는 이렇다.
컴퓨팅에서, 버퍼(buffer, 문화어: 완충기억기)는 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역이다. 버퍼링(buffering)이란 버퍼를 활용하는 방식 또는 버퍼를 채우는 동작을 말한다. 다른 말로 '큐(Queue)'라고도 표현한다.
버퍼는 컴퓨터 안의 프로세스 사이에서 데이터를 이동시킬 때 사용된다. 보통 데이터는 키보드와 같은 입력 장치로부터 받거나 프린터와 같은 출력 장치로 내보낼 때 버퍼 안에 저장된다. 

일상생활에 비유하면,
만석 시 출발하는 버스라고 생각하면 될 것 같다.
사람들은 줄을 서서 버스에 타게되고
버스의 좌석이 모두 찼을 때,
버스는 출발하게 된다.

이처럼 버퍼에는
바이너리 데이터들이 차게되고 버퍼가 가득차면
목적지로 데이터를 보내게 된다.

바이트들을 읽어 들이는 단위는 File System에 기술되어 있다.
그리고 이 Byte의 크기단위가 Block이 된다.
즉 Block가 데이터 처리의 단위가 된다.






※ Pipe

 파이프를 설명하려면 운영체제에 대해 알아야 한다.
OS의 기본 4대 역할은, 프로세스/메모리/파일시스템/디바이스 관리라고 볼 수 있을 것 이다.
그리고 하드웨어의 입/출력을(디바이스) 관리할 때 버퍼를 사용하게 된다.

우선 기본적으로 클라이언트와 서버간의 요청과 응답의 과정을 보면


1. 클라이언트로부터 요청이 들어온다.
2. 해당 요청데이터를 리눅스 커널이 맞이하여 자신의 Read Buffer에 저장한다.
3. 포트번호를 통해 해당 프로세스의 read socket으로 전달한다. (Data 이벤트 발생)
4. 요청을 통해 프로세스가 클라이언트가 요청한 자원을 Read Stream을 생성해 읽어온다.
5. 읽어온 파일의 내용을 write socket에서 write stream을 통해 커널의 Send buffer에 작성한다.

한가지 알아 두어야 할 것은,
커널의 read buffer와 send buffer은 모든 프로세스가 공유하는 자언이라는 사실이다.
즉 kernel OS가 프로세스들에게 버퍼의 접근/사용 권한을 할당한다.
이말은 결국 Write stream 또한 OS의 자원이다. (다 쓰고 나면 반납해야 한다.)


응답을 작성하는 과정에서 사용되는 스트림들

위의 설명처럼
파일과 관련된 스트림은 크게 3개다


1. 파일의 내용을 읽어오는 Read Stream
2. 읽어온 파일의 내용을 소켓에 작성하는 Write Stream
3. 소켓이 갖고 있는 내용을 커널의 Send buffer에 작성하는 Write stream

1의 경우, 우리가 직접 생성해줘야 하는 부분이며,
2번과 3번의 경우에는 기존에 생성이 자동으로 이루어지는 스트림이다.

그리고 우리가 직접적으로 관심을 가져야 하는 부분은 1,2번 스트림이다.
3번은 시스템 단의 작업이기 때문인 듯 하다.

2번 스트림의 경우 자동으로 생성이 되긴 하지만,
2가지 스트림을 모두 동시에 관리하기란 어려운일이다.
그리고 이러한 문제점을 해결하기 위해 등장한 개념이 바로 Pipe다.
말그대로 이 스트림들을 하나의 Pipe로 묶어 관리해줌으로써 편리함을 제공한다.

여기서 말하는 스트림의 편리한 관리란,
Kernel의 Send buffer가 가득 찼을 경우, 
write메소드의 결과 값이 false(쓰기작업을 수행 할 수 없다는 의미)가 발생하고
drain이벤트가 발생할 때 까지 읽기스트림으로부터 내용을 읽어오는 작업을 중단해야 한다.
이러한 제어 작업들을 읽기스트림과 쓰기스트림을 하나의 파이프로 묶어줌으로써 자동으로 관리가 가능하다.

stream.createRedableStream.pipe(destination, [option])을 이용해서 생성한다.

이짜젠
이짜젠

저만의 메모장입니다! 글이 이해하시기 힘들 경우 언제든지 말씀해주세요