본문 바로가기

Stream을 사용하는 이유

 

 

프로그램은 일반적으로 누군가로부터 정보를 전달 하거거나, 누군가에게 정보를 전달받는 작업의 연속이다.

정보를 주고받기 위해 Computer는 수많은 입출력(I/O) 장치와 연결된다.

 

Keyboard, Mouse, Monitor, Printer 등 각각 입출력 장치 종류나 각각의 제조사들마다

입출력 방법을 별도로 만드는 것은 불가능하다.

마치 C++ 언어가 CPU 제조사마다 다른것과 같다.

마치 애플이 8핀, 12핀 단자 독자 규격을 사용하는 것과 같다.

 

Stream은 여러 입출력 장치들 간에 데이터를 읽고 쓰는 통일된 방법이다.

입출력 장치의 종류에 상관없이, OS와 Stream 표준으로 입출력 기기간에 데이터를 주고받는다.

장치가 달라지더라도, 항상 똑같은 절차를 통해 데이터를 읽고 쓰는 스트림을 이용한다.

 

Network Socket의 데이터 송/수신 방법도 모니터, 프린터, 파일 입출력의 stream과 동일한 방식을 사용한다.

그래서 데이터 송/수신 함수의 이름이 read, write 인 경우가 있다.

윈도우 소켓(WinSock)은 send, recv(receive) 를 사용한다.

 

Stream 개념 이해

 

  • 끊기지 않고 연속적인 "데이터의 흐름"을 추상화 한 용어
    영어 뜻으로는, '흐르는 시냇물' 등을 의미한다.
    가상 연결 통로에서 바이너리 데이터가 흐르는 모습이다.

 

  • 입출력 스트림 예시

키보드 - 스트림 - 프로그램(메모리) - 스트림 - 모니터

 

C언어 표준 입출력 stream

  • C언어, <stdio.h> 표준 입출력을 사용한다.
    printf( ) stdout stream 
    scanf( ) stdin stream 

 

C++ 표준 입출력 stream

  • C++, <iostream> 표준 입출력을 사용한다.
    cin 객체
    cout 객체

 

C언어 File stream

  • 파일 입출력은, 표준 스트림 처럼 자동으로 Stream이 생성되지 않는다.
    File* 라는 Stream을 의도적으로 생성시켜 사용한다.
    그래서 의도적으로 생성한 stream 은 필수로 닫아줘야 한다.
  • File* fp = fopen("파일.txt", "w");
    fp 라는 file stream 을 생성해 사용한다.
    fprintf( fp , "Hello world\n");
    fscanf( fp , " ");

 

소켓 stream

  • Network 통신에서 Socket 으로 통신하면 socket stream 이 자동 생성된다.
  • Socket 꼭! 닫아야 하는 이유는 stream을 닫아야 하기 때문이다.
  • Database 도 마찬가지의 경우다. 

 

Qt Stream

  • Qt i/o 공식문서 링크
  • Qt 네트워크에서 파일 송수신 방법
    • QDataStream 을 socket에 연결한다. (로컬 파일 연결 작업과 동일) 
    • QByteArray 타입의 데이터를 stream으 주고 받으면 된다.

 

Stream의 물리적 위치 (Linux)

 

 

  • 리눅스의 경우, 최상위 경로에서 dev 디렉토리에서 확인 가능하다.
    운영체제가 만들어 놓은 stdin, stdout, stderr 스트림을 확인 할 수 있다.

 

  • 위 이미지에서 오른쪽 0, 1, 2 번호는 파일 디스크립터(File Descriptor)이다.

 

  • File* 를 사용하면 File Stream이 생성되고 File Descriptor를 부여받는다.
    • 아래 stream 모두  File Descriptor 라는 고유 정수 식별값을 부여받는다.
      1. 입력
      2. 출력
      3. 파일
      4. 프린터
      5. 모니터
      6. 데이터베이스
      7. 소켓
      8. ...

 

  • Socket 이 연결되면 Socket Stream이 생성되고 File Descriptor 를 부여받는다.
    즉, 서버에서 연결된 Socket 들을 fd(File Descripor) 정수값으로 로 식별하여 Client를 구분 할 수 있다.

 

  • Qt 에서 Socket의 File Descriptor 확인 방법
    • socket->socketDescriptor()
    • QString::number(socket->socketDescriptor())
    • File Descriptor 값은 stream에 부여되는 중복되지 않는 고유한 정수값으로 Socket 의 식별이 가능하다.

 

파일 디스크립터(File Descriptor)

 

리눅스에서 파일 디스크립터(File Descriptor)란, C 언어와 같은 프로그래밍 언어가 아닌 시스템(OS)에 의해 관리되는 일련의 정수값으로 파일 또는 소켓의 식별에 사용된다.

*윈도우는 파일과 소켓을 구분한다.

 

 

▶ UNIX에서 파일을 새로 열면(open) int 타입의 정수를 리턴하는데, 이는 파일 기술자 테이블(file descriptor table) 의 index 번호다.
▶ 예를들어, 2개의 파일을 open하면 파일 기술자는 3과 4가 배정되며, 소켓을 생성하면 파일 기술자와 똑같은 기능과 역할을 하는 소켓 기술자(socket descriptor)가 리턴된다. 즉, 파일과 소켓이 기술자 테이블을 공유한다.

 

 

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>

int main(void)
{	
	int fd1, fd2, fd3;
	fd1=socket(PF_INET, SOCK_STREAM, 0);
	fd2=open("test.dat", O_CREAT|O_WRONLY|O_TRUNC);
	fd3=socket(PF_INET, SOCK_DGRAM, 0);
	
	printf("file descriptor 1: %d\n", fd1); // file descriptor 1: 3
	printf("file descriptor 2: %d\n", fd2); // file descriptor 1: 4
	printf("file descriptor 3: %d\n", fd3); // file descriptor 1: 5
    
	// 3 부터 시작하는 이유는 0,1,2 는 표준 입출력에 할당되었기 때문이다.
	
	close(fd1);
	close(fd2);
	close(fd3);
	return 0;
}

 

 

Buffer(버퍼)

 

1. Buffer(버퍼)란?

 

Buffer(버퍼)는 데이터가 임시 저장되는 메모리의 물리적 공간을 상징하는 용어

 

 

2. Buffer(버퍼)를 사용하는 이유

 

 

: 버퍼는 여러 입출력 장치간에 데이터를 읽고 쓰는 동안 발생하는 속도 차이를 해결한다.

 

 

3. Buffer(버퍼)를 사용한 화면 출력 예시

 

 

입력 버퍼: 사용자와 저장장치의 입력은 프로그램의 연산 속도보다 훨씬 느리다.

프로그램이 이러한 입력의 완료를 대기하는 것은 낭비가 심하다.

 

출력 버퍼: 모니터의 출력은 프로그램의 연산 속도보다 매우 느리다.

프로그램이 모니터 출력 완료를 대기하는 것 또한 엄청난 낭비다.

*예) 컴퓨터 게임에서 화면 주사율 딜레이 및 수직 동기화 백버퍼, 프론트 버퍼 이슈가 발생하는 경우

 

: 프로그램의 연산 속도는 입력과 출력 시간에 비해 빠르다.

그래서 입력 버퍼와 출력 버퍼를 사용해 딜레이 문제를 보완한다.

BasicLike

어? 나 프로그래밍 좋아하네?