본문 바로가기

CS지식/컴퓨터네트워크

소켓 프로그래밍 과제 2번

문제 2번 ) client에서 파일 이름을 서버에 보내면 서버에서 파일에 접근해 파일의 내용을 읽어서 다시 클라이언트에게 리턴해주어라. 또한, 서버에 파일을 쓰는 것도 구현하여라.

<구현 방법>

  fcntl.h 라이브러리를 include하여서 파일 입출력을 수행할 수 있도록 만들었다.

 만약 클라이언트가 rd를 입력하면 파일을 읽는 동작을 수행한다. fopen을 통해서 파일을 읽기 권한으로 오픈하고, 내용을 buf에 저장한 후 클라이언트에게 전송한다.

클라이언트가 wrt를 입력하면 파일을 쓰는 동작을 수행한다.  fopen을 "w" 옵션으로 열면 파일이 생성된다. 그 후에 클라이언트에게서 입력받은 파일 내용을 생성한 파일에 쓰고 파일을 닫는다.

<서버 코드>

//sol2_server.c

#include <stdio.h> //표준 입출력 라이브러리
#include <stdlib.h> //문자열 관련 함수 사용 위한 라이브러리
#include <string.h> //memset 등 사용
#include <unistd.h> // exit,sockaddr_in 등 사용
#include <fcntl.h> // 파일 입출력 관련 라이브러리
#include <sys/socket.h> // 소켓 관련 라이브러리
#include <arpa/inet.h> //아파넷 관련 라이브러리

#define MAX 500

// 에러 발생 시 내용 출력하는 에러 핸들러
void error_handling(char * message){
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

void main(){
    int ss, sa;
    struct sockaddr_in ssa;

// ssa 구조체 초기화 후 통신타입, ip주소, 포트번호 지정
    memset(&ssa, 0, sizeof(ssa));
    ssa.sin_family = AF_INET;
    ssa.sin_addr.s_addr = htonl(INADDR_ANY); // 어떤 주소이든 상관없다.
    ssa.sin_port = htons(11234); // 포트번호 11234

// tcp 통신 소켓 생성
    ss = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(ss == -1) // 소켓 생성 실패할 경우 에러 메세지 출력
        error_handling("socket error");

//소켓과 서버 주소를 연결한다. 실패할 경우 에러 메세지 출력
    if(bind(ss,(struct sockaddr *) &ssa, sizeof(ssa))==-1){
        error_handling("bind error");
    }
   
// 서버는 무한루프를 돌면서 클라이언트의 요청이 있을 때 마다 서비스해준다.

    while(1){
   
    // 소켓을 사용가능한 상태로 활성화. 소켓 대기열의 크기는 10.
        if(listen(ss,10)==-1){
            error_handling("listen error");
        }

//클라이언트로부터 요청이 오면 연결을 수락한다.
        sa = accept(ss,0,0);
       
        if(sa ==-1){
            error_handling("accept error");
        }

        //클라이언트에서 명령어를 수신받아 command 배열에 저장한다.
        char command[7];
        recv(sa, command, MAX, 0);
       
        //rd를 입력했을 때, 파일 읽기 수행
        if(!strcmp(command, "rd\n")){
            char filename[20],buf[MAX];
            FILE *file;
            filename[0] ='\0'; //filename 초기화
            memset( buf, 0, sizeof(buf)); //buf 초기화

            //클라이언트 측에서 읽을 파일 이름을 filename에 전송받는다.
            recv(sa,filename,20,0);

            // 입력받은 파일을 읽기 권한으로 열기
            file = fopen(filename,"r");

            //만약 file 오픈 실패했다면 에러메세지 출력
            if (file==NULL){
                printf("파일 열기 실패\n");

                exit(1);
            }
            // file의 내용을 buf에 저장한다.
            fgets(buf,sizeof(buf),file);

            //클라이언트에게 buf의 내용을 전달한다.
            send(sa,buf,sizeof(buf),0);

            fclose(file);//파일 닫기

        }

        //파일 쓰기 명령어 수행
        if(!strcmp(command, "wrt\n")){
            char filename[20],buf[MAX];
            FILE *file;

            filename[0] ='\0';//filename 배열 초기화
            memset( buf, 0, sizeof(buf)); // buf 배열 초기화

            recv(sa,filename,20,0); //클라이언트에서 쓰기 할 파일 명을 수신받는다.

            //입력받은 파일 이름의 파일 생성, 만약 있으면 삭제 후 재생성
            file = fopen(filename,"w");

            // 파일 오픈 실패 시 에러메세지 출력
            if (file==NULL){
                printf("파일 열기 실패\n");

                exit(1);
            }

            //클라이언트에서 입력할 파일 내용을 전송받아 buf에 저장한다.
            recv(sa,buf,sizeof(buf),0);
            //buf의 내용을 file에 쓴다.
            fprintf(file,buf,sizeof(buf));
           
            //file 닫기
            fclose(file);

        }
        close(sa); //클라이언트와의 통신 소켓을 닫는다.
    }
   
    }
 

 

<클라이언트 코드>

 
 
//sol2_client.c

#include <stdio.h> //표준 입출력 라이브러리
#include <string.h>// memset 등 사용
#include <unistd.h> // exit, sockaddr_in 등 사용
#include <sys/socket.h> // 소켓 관련 라이브러리
#include <arpa/inet.h> //아파넷 관련 라이브러리
#include <fcntl.h> //파일입출력 관련 라이브러리
#include <stdlib.h> // 문자열 관련 함수 사용 위한 라이브러리
#define MAX 500

#define IPADDR "127.0.0.1"

// 에러 발생 시 내용 출력하는 에러 핸들러
void error_handling(char * message){
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

void main(){
    int cs;
    char buf[MAX],command[5],filename[20],temp[5];
    struct sockaddr_in csa;

//csa 구조체 초기화 후 통신타입, ip주소, 포트번호 지정
    memset(&csa, 0, sizeof(csa));
    csa.sin_family = AF_INET;
    csa.sin_addr.s_addr = inet_addr(IPADDR); // 루프백 주소인 127.0.0.1 지정
    csa.sin_port = htons(11234);// 포트번호 11234

// tcp 통신 소켓 생성
    cs = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(cs ==-1){ // 소켓 생성 실패할 경우 에러 메세지 출력
        error_handling("client socket error");
    }
    // 서버와 소켓 연결한다. 연결 실패 시 에러 메세지 출력
    if(connect(cs, (struct sockaddr *) &csa, sizeof(csa))==-1){
        error_handling("connect error");
    }

    //파일을 read할 지, write 할 지 입력을 받아 command 문자열에 저장한다.
    printf("\n <명령어 Read File :rd, Write File: wrt>\n");
    printf("[command] ");
    fgets(command,5,stdin);
    send(cs,command,5,0); //명령어를 서버에 전송한다.

    //파일 읽기 수행
    if(!strcmp(command,"rd\n")){

        //읽고싶은 파일 명을 입력하여 서버에게 전송한다
        printf ("읽고싶은 파일 명 : ");
        scanf("%s",filename);
        fgets(temp,5,stdin); // 버퍼에 남은 \n 제거
        send(cs,filename,20,0);

        //buf 배열 초기화
        memset( buf, 0, sizeof(buf));

        // 서버가 전송하는 파일의 내용을 받아와 buf에 저장한다
        recv(cs,buf,sizeof(buf),0);

        // 파일의 내용을 출력한다.
        printf("Open file '%s' : [ %s ]\n", filename, buf);

    }

    //파일 쓰기 명령어 수행
    else if(!strcmp(command,"wrt\n")){
       
        //buf 배열 초기화
        memset( buf, 0, sizeof(buf));

        //생성할 파일 명을 입력받는다.
        printf ("생성할 파일 명 : ");
        scanf("%s",filename); //문자열을 입력받아 filename에 저장한다.
        fgets(temp,5,stdin);//버퍼에 남은 \n 제거
        send(cs,filename,20,0); // 서버에게 파일 이름을 전송한다.
       
        //파일에 저장할 내용을 입력받는다.
        printf("파일에 저장할 내용 :");
        fgets(buf,MAX,stdin); // buf에 내용을 입력받는다.
        send(cs,buf,sizeof(buf),0); //서버에게 파일에 저장할 내용을 전송한다.

        printf("파일 생성이 완료되었습니다.\n");    


 

<실행결과>

 원래 있던 a.txt를 rd 명령어를 수행하여 화면에 보였다. cat a.txt로 확인한 파일 내용과 동일한 것을 확인할 수 있다.

 wrt 명령어를 수행하여 b.txt 를 생성하였고, 다시 rd 명령어를 수행하여 화면에 띄워보였다. cat b.txt로 확인한 파일 내용과 동일한 것을 확인할 수 있다.