본문 바로가기

Study/TCP/IP

파일 조작하기

리눅스(유닉스 계열 운영 체제)는 모든것(콘솔, 소켓, 파일 등등)을 파일로 간주한다. 소켓 또한 예외가 아니며 우리가 생성하는 소켓도 사용하는 방식이나 내부적으로 처리되는 방식이 파일과 상당 부분 유사하기 때문에 파일 입, 출력 함수를 소켓 입, 출력에 사용할 수 있다.

저 수준 파일 입출력(Low-Level File Access)
Low -Level의 의미는 단순히 "시스템이 직접 제공해 주는~" 이라는 의미를 지닌다. 즉 ANSI표준 C에서 정의된 함수들은 아니라는 뜻이다.

파일 디스크립터(File Descriptor)
파일 디스크립터란 시스템으로부터 할당받은 파일이나 소켓을 대표하는 정수를 의미한다. 또한 표준 입력과 표준 출력도 파일 디스크립터로 표현이 되는데 이들은 프로그램이 시작되자마자 기본적으로 열리고, 종료 시에 자동적으로 닫히게 된다. 기본적으로 열리는 파일 디스크립터는 아래와 같다.
파일 디스크립터  대상
0                        표준 입력 : Standard Input
1                        표준 출력 : Standard Output
2                        표준 에러 출력 : Standard Error
소켓을 생성 할 때나, 파일을 생성할 때나 위의 기본적으로 열리게 되는 파일 디스크립터를 제외하고 생성 순서대로 디스크립터가 넘버링 된다. (3부터)

File 열기
읽거나 쓰기 위해 파일을 여는 함수를 소개한다. 두개의 인자를 받는데, 하나는 열고자 하는 파일의 경로를 포함한 이름이고, 또 하나는 열게 되는 파일의 모드(mode)이다.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int open(const char *path, int flag);
성공시 파일 디스크립터, 실패시 -1 리턴

- path : 파일의 경로를 포함한 이름을 나타내는 문자열의 포인터.
- flag : 파일 오픈 모드.
flag에 인자를 넘겨줄 수 있는 값과 그 의미는 다음과 같으며 하나 이상의 모드를 Bitwise-OR('|')로 묶어서 인자로 전달 가능하다.
MODE                  의미
O_CREAT             필요한 경우 파일을 생성한다.
O_TRUNC             존재하던 데이터를 모두 삭제한다.
O_APPEND           존재하던 데이터 보존하고 뒤에 이어서 저장한다.
O_RDONLY           읽기 전용 모드로 파일을 연다.
O_WRONLY           쓰기 전용 모드로 파일을 연다.
O_RDWR               읽기 쓰기 겸용 모드로 파일을 연다.

File 닫기
파일은 사용 후 반드시 닫아줘야 한다. close 함수를 호출 하면서 닫고자 하는 파일의 디스크립터를 인자로 전달 하면 된다. 여기서 중요한 사실은 리눅스에서 소켓도 파일로 취급하기 때문에, 생성된 소켓을 닫아 줄 때에도 close 함수를 사용한다는 것이다.
#include <unistd.h>

int close(int fildes);
성공 시 0, 실패시 -1 리턴

데이터 쓰기
write 함수는 파일에 데이터를 출력(전송)하는 함수이다. 리눅스에서 파일과 소켓을 동일하게 취급하므로 소켓을 통해서 다른 호스트에게 데이터를 전송할 때에도 물론 사용할 수 있다. 여기서 두번째 인자는 전송할 데이터를 가지고 있는 버퍼의 포인터이며 세번째 인자는 전송할 데이터의 바이트 수를 나타낸다.
#include <unistd.h>

ssize_t write(int fildes, const void *buf, size_t nbytes)
성공 시 전달 한 바이트 수, 실패시 -1 리턴

여기서 size_t는 unsigned int로 정의되어 있고 ssize_t의 경우 s가 하나 더 붙어 signed int를 의미한다. 이러한 자료형을두고 고전적인(primitive) 자료형이라 한다. 일반적으로 (sys/type.h> 헤더에 선언되어 있는데, 사실 C의 typedef 선언을 통해서 정의되어 있다. 따라서 새로운 자료형이 아닌 기본 자료형을 가지고, 이름만 바꾸어 정의해 놓은 것이다. 이러한 자료형을 사용하는 이유는 다음과 같다.

지금은 int가 32비트라고 말한다. 보편적으로 사용되는 운영체제와 컴퓨터가 32비트 이기 때문이다. 최근 64비트 시스템이 도입되면서 일부 시스템에서는 int의 비트수가 32비트가 아니다. 즉, 시스템에 따라서 자료형의 표현 방식이 틀려지므로, 내가 구현한 프로그램을 다른 시스템에서 실행시키기 위해서는 코드의 수정 자체가 불가피하다는 것을 의미한다.

그러나 4바이트의 자료형이 필요한 곳에, 지금처럼 헤더파일에 unsigned int를 size_t로 정의해 놓고 쓰게 되면, 나중에 시스템이 변경되어서 unsigned int가 더 이상 4바이트를 나타내지 않게 되었을 때, 4바이트를 나타내는 다른 적절한 자료형을 가지고 헤더파일에 선언되어 있는 size_t를 재정의 해 주기만 하면 된다. 즉, 소스 코드는 바꾸지 않고 컴파일만 다시 한번 하면 새로운 시스템에서 잘 돌아 가게 된다는 것이다. 결과적으로 확장성을 고려하여 위와 같은 방법을 따르는 것이 좋다.

데이터 읽기
read 함수는 데이터를 입력(수신) 받는 함수이다. 함수내 인자의 의미는 write 함수와 같다.
#include <unistd.h>

ssize_t read(int fildes, void * buf, size_t nbytes);
성공 시 수신 한 바이트 수(단 EOF 만나면 0), 실패시 -1 리턴

'Study > TCP/IP' 카테고리의 다른 글

주소 정보의 표현  (0) 2008.02.16
Port란,  (0) 2008.02.16
Internet Address  (0) 2008.02.16
소켓의 생성과 프로토콜의 설정  (0) 2008.02.16
네트워크 프로그래밍의 이해  (0) 2008.02.16