상세 컨텐츠

본문 제목

네트워크 프로그래밍 - 리눅스에서의 파일 조작

Computer Science/Network

by 2021. 9. 24. 08:54

본문

반응형

저 수준 파일 입출력

ANSI의 표준 함수가 아닌, 운영체제가 제공하는 함수 기반의 파일 입출력이다. 표준이 아니기에 운영체제 별 호환성이 없다.

리눅스의 경우 소켓도 파일로 간주하므로 저 수준 파일 입출력 함수를 기반으로 소켓 기반의 데이터 송 수신이 가능하다.

 

file descriptor (파일 디스크립터)

운영체제가 만들 파일(및 소켓)을 구분하기 위한 숫자이다. 저 수준 파일 입출력 함수는 입출력을 목적으로 파일 디스크립터를 요구한다.

저 수준 파일 입출력 함수에게 소켓의 file descriptor를 전달하면, 소켓을 대상으로 입출력을 진행한다.

 

file descriptor의 경우 0,1,2 는 파일 생성 시 미리 지정되어있다.

 

파일 열기 / 닫기

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char* path, int flag); // 성공시 file descriptor, 실패 시 -1 반환
// path : 파일 이름을 나타내는 문자열의 주소 값 전달
// flag : 파일의 오픈 모드 정보 전달

 open명령어를 수행하면 file descriptor 값이 반환된다. 

 

flag에는 파일의 오픈 모드에 대해서 설정할 값이 들어가는데 이는 다음과 같다

#include <unistd.h>

int close(int fd); // 성공 시 0, 실패 시 -1 반환
// id : 닫고자 하는 파일이나 소켓의 파일 descriptor

open을 통해서 얻은 file descriptor를 통해 파일 입출력을 진행 

 

파일에 데이터 쓰기

#include <unistd.h>

ssize_t write(int fd, const void * buf, size_t nbytes); // 성공 시 전달한 바이트 수, 실패 시 -1 반환
// id : 데이터 전송대상을 나타내는 파일 디스크립터 전달
// buf : 전송할 데이터가 저장된 버퍼의 주소 값 전달
// nbytes : 전송할 데이터의 바이트 수 전달

 

int main(void)
{
	int fd;
	char buf[]="Let's go!\n";
	fd = open("data.txt", O_CREAT|O_WRONLY|O_TRUNC);
	if(fd==-1)
    	error_handling("open() error!");
	printf("file descriptor: %d \n", fd);
    
    if(write(fd, buf, sizeof(buf)) == -1)
    	error_handling("write() error!");
    close(fd);
    return 0;
}

위 코드를 보자.

파일에 적을 내용을 buf배열에 담고, open을 통해 data.txt의 file descriptor를 받은 후, 

write 함수를 통해 buf의 내용을 fd로 지정된 size만큼 복사한다. 

 

실행 결과를 확인하면 아래와 같다.

root@my_linux: /tcpip# gcc low_open.c -o lopen
root@my_ linux: /tcpip# ./lopen
file descriptor: 3
root@my_linux:/tcpip# cat data.txt
Let's go!
root@my_linux: /tcpip# 

파일에 저장된 데이터 읽기

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t nbytes); // 성공시 수신한 바이트 수, 실패 시 -1 반환
// fd : 데이터 수신 대상을 나타내는 파일 디스크립터 전달
// buf : 수신한 데이터를 저장할 버퍼의 주소 값 전달
// nbytes : 수신할 최대 바이트 수 전달
int main(void)
{
	int fd;
	char buf (BUF_SIZE];
    fd = open("data.txt", O_RDONLY);
    if(fd == -1)
    	error_handling("open() error!");
    printf("file descriptor: %d \n" , fd);
    
    if(read(fd, buf, sizeof(buf)) == -1)
    	error_handling("read() error!");
    printf("file data: %s", buf);
    close(fd);
    return 0;
}

위 코드를 보자.

open을 통해 파일을 열어 file descriptor를 받고, 받은 fd값을 이용해 read로 buf의 size만큼 읽어온다.

실패하지 않았다면, 읽어온 byte만큼 buf에 저장될 것이므로, buf를 출력하면 읽어온 내용을 볼 수 있다. 

 

실행 결과는 다음과 같다.

root@my_linux: / tcpip# gcc low_read.c - o lread
root@my_linux: / tcpip# ./lread
file descriptor : 3
file data : Let's go!
root@my_linux : / tcpip#

file descriptor와 socket

int main(void)

{
    int fdl, 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);
    printf("file descriptor 2: %d\n", fd2);
    printf("file descriptor 3: %d\n", fd3);
    close(fd1); close(fd2); close(fd3);
    return 0;
}

위 코드에서 socket과 파일을 열었을 때 모두 file discriptor 값이 할당된다. 즉 리눅스는 파일과 소켓을 동일하게 간주한다.

 

실행 결과는 아래와 같다.

root@my_linux: /tcpip# gcc fd_seri.c -o fds
root@my_linux:/tcpip# ./fds
file descriptor 1: 3
file descriptor 2: 4
file descriptor 3: 5
root@my_linux: /tcpip#

 

반응형

관련글 더보기